JD-GUI

最近接了一个项目需要反编译class,尝试了用ChatGPT4 尝试了一下,不允许,于是使用JD-GUI来操作

image-20230908122922005

1. 下载和安装

地址:http://java-decompiler.github.io/

下载链接:

jd-gui-1.6.6.jar

jd-gui-1.6.6-min.jar

绿色软件,用jdk就可以直接启动,运行不了的话打开命令行执行下面语句

1
java -jar jd-gui-1.6.6.jar

打开是这个样子的

image-20230908142843084

2. 设置一下导出格式

这边导出的java需要和源代码对比,所以需要设置一下不导出行号和反编译器信息

Help–Preferences

image-20230908143045891

把Sources saving中的两个框的√去掉(因为默认是选中的)

image-20230908143113339

3. 打开需要反编译的文件

这里可以选择war包或者jar包,如果是解压好的文件夹,随便找个class打开,jd-gui会自动查找根目录

image-20230908143602171

4. 导出源代码

File选项找到Save All Sources这个是保存jar全部类。如果是单个就是Save Sources

image-20230908143657849

image-20230908143826827

5. 现在可以把源码导入到ChatGTP来分析源码了

image-20230908144111744

thingsboard

1 安装

源码地址:https://github.com/thingsboard/thingsboard.git

编译需要JDK11MAVN

1.1 在windows上安装docker:

docker下载地址:https://www.docker.com/products/docker-desktop/

安装方法看说明

需要把dockerbin目录添加到环境变量Path

1.2 创建数据卷

安装好之后需要创建volume

PowerShell输入

1
2
docker volume create mytb-data
docker volume create mytb-logs

创建数据卷和日志卷

1.3 创建docker-compose.yaml

新建文件夹->thingsboard->创建文本文件,重命名为docker-compose.yaml

输入下面内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: '3.0'
services:
mytb:
restart: always
image: "thingsboard/tb-postgres"
ports:
- "8080:9090"
- "1883:1883"
- "7070:7070"
- "5683-5688:5683-5688/udp"
environment:
TB_QUEUE_TYPE: in-memory
volumes:
- mytb-data:/data
- mytb-logs:/var/log/thingsboard
volumes:
mytb-data:
external: true
mytb-logs:
external: true
  • 8080:9090 - 容器内部HTTP端口 9090 映射到外部 8080

  • 1883:1883 - 容器内部MQTT 端口 1883映射到外部 1883

  • 7070:7070 - 容器内部Edge RPC 端口 7070映射到外部 7070

  • 5683-5688:5683-5688/udp - 将本地 UDP 端口 5683-5688 连接到公开的内部 COAP 和 LwM2M 端口

  • ~/.mytb-data:/data - ThingsBoard DataBase 数据目录设置为mytb-data

  • ~/.mytb-logs:/var/log/thingsboard - ThingsBoard 日志目录设置为mytb-logs

  • mytb - 主机名

  • restart: always - 设置ThingsBoard自启

  • image: thingsboard/tb-postgres - docker镜像,也可以是thingsboard/tb-cassandra或者thingsboard/tb

    powershellcd到你创建的目录输入

    1
    2
    docker compose up -d
    docker compose logs -f mytb

启动thingsboard

打开地址:http://localhost:8080

账号:tenant@thingsboard.org

密码:tenant

2 添加设备

  • 登录并打开设备页面

  • 单击”+”图标

  • 输入设备名称

    image-20230908122316888

Modbus TCP/IP

1 什么是Modbus?

ModbusModicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议,支持使用RS232/RS485/RS422协议的串行设备,同时还支持调制解调器。 它的简单性以及制造商可以免费将其纳入其产品的事实使其成为连接工业电子设备的最流行的方法。 Modbus比其他通信协议使用的更广泛的主要原因有以下几点:

  • 公开发表并且无著作权要求;
  • 易于部署和维护;
  • 对供应商来说,修改移动本地的比特或字节没有很多限制;

Modbus通过设备之间的串行线进行数据传输。最简单的设置是使用一根串行电缆连接两个设备(主设备和从设备)上的串行端口。 数据以称为比特10的序列发送。每个位都作为电压发送。0被发送为正电压,1被发送为负电压。如下图所示;

img

2 主从模式

Modbus解决了通过串行线路在电子设备之间发送信息的问题。 该协议在遵循该协议的体系结构中实现主/从模型。 Modbus主站(Master)负责从其他设备(Slave)请求信息。 标准Modbus网络中有一个Modbus主站。具体如下图所示;

img

主设备向从设备请求信息,最多大约可达到240个 . 每个从设备都有自己唯一的从设备地址标识Slave Address)。 除了从从设备请求信息之外,主设备还可以写入从设备的内部寄存器

3 协议的分类

Modbus的应用十分广泛,特别是在工业控制领域,具体如下图所示;

img

ChatGPT

ChatGPT

上传文件

登录后选择GPT-4下拉
Advanced Data Analysis
选择Advanced Data Analysis
对话框就会出现+号,就可以上传文件了

分析代码

把整个JD-GUI反编译的结果可以直接通过上面的上传文件上传到GPT-4中进行分析

git

git原理

git基本概念

git原理

git模型

git模型

git基本命令

git基本命令

merge

fast-forward

fast-forward

no-fast-forward

no-fast-forward

冲突

conflict

重置

软重置

soft-reset

硬重置

hard-reset

还原

revert

revert

拣选

cherry-pick

cherry-pick

取回

fetch

fetch

拉取

pull

pull

Reflog

reflog

reflog1

reflog+reset

reflog+reset

日志框架

故事开始

在一家IT企业中,项目经理虎大力(龙套) 正在指挥 程序员鹿小明(精英龙套)开发一个大型的增删改查项目。为了开发这个项目。项目组仅有的程序员鹿小明每天工作996

故事进入 V1.0 环节

one day,虎大力给鹿小明提出来一个新的需求,为了更好的进行公司的信息化建设,虎大力想要看到代码执行的情况,执行到某个业务的时候在控制台有所表示,例如:执行到查询方法的时候,需要在控制台上出现,这是一个打印方法的信息。

img

鹿小明一想,这好办啊,本来自己为了调试测试方便就写过很多打印语句,现在无非更多而已。于是就加班加点在所有的增删改查方法中都写了System.out.println()打印语句。顺利完成了这个工作。

img

故事进入 V2.0 环节

一段时间岁月静好

one day,虎大力找到鹿小明:你这个代码里面System.out.println()太多了,我需要你做成,测试时候显示,上线之后不显示。你去搞一下。

鹿小明于是冥思苦想:我要不要上线的时候把打印语句注释掉,测试的时候再打开呢?

但是想到要经常开关注释也不是个容易的事儿,于是鹿小明一咬牙,996变007,废寝忘食的更新出了 V2.0版本。他把日志打印封装成框架 logging-1.0.jar,可以进行统一的开关。顺利完成了这个工作。

img

故事进入 V3.0 环节

一段时间岁月静好

one day,虎大力找到鹿小明:你这个日志框架功能太简单了 ,再搞点新功能,像输出到文件啊,异步啊都搞上。

鹿小明于是冥思苦想,007之后继续007,废寝忘食的更新出了 V3.0 版本,封装成一个新的框架logging-2.0.jar。顺利完成了这个工作。

img

故事进入 V4.0 环节

一段时间岁月静好

one day,虎大力找到鹿小明:1.0和2.0的api不一样,1.0换成2.0,2.0换成1.0 每次切换都要改代码,你改一下吧,改成可以想用哪个用哪个的。

鹿小明于是冥思苦想,007之后继续007,这个需求有点难,他从JDBC上找到了灵感,JDBC通过统一接口实现了驱动的切换,日志也可以。img

于是,他爆肝搞出来一个日志接口层(日志门面),让 1.0 和 2.0的日志框架都实现这个接口,这样想用1.0的时候就导入1.0,想用2.0的时候就导入2.0 。顺利完成了这个任务。

img

img

而这个设计的结构也是现在主流日志框架:log4j logback log4j2 等的结构

日志门面(接口) 日志实现
SLF4J,commons-logging Logback,Log4j

通过它们就打印出了我们常见的各种日志信息

img

日志框架结构分析

日志框架实际上分为三个部分,除了上面提到的日志门面(接口)和日志库(实现),还有日志适配器

img

日志门面 接口规范

定义接口规范,不负责具体实现,也就是说以后代码中打印日志时调用的日志门面接口的方法。常见的有 SLF4J,commons-logging 都是这样。常见的日志门面有下面几种

日志门面(接口规范层) 简介
JCL(Jakarta Commons Logging) 这个jar就是常见的 commons-logging.jar,也是Spring框架中使用的日志门面。由于上一次更新还是在2014年,所以不建议使用
SLF4j(Simple Logging Facade for Java) 这个jar可以说是最常用的日志jar包了
jboss-logging 使用最少,一些特定的框架在使用

根据简单的分析,在我们的代码中如果要选择一款 日志的接口规范的话,毫无疑问,只有 SLF4j 配得上我们的项目。

日志库 代码实现

日志库是日志功能的具体实现,早期就是为了替代 System.out 语句而出现的。常用的日志库如下:

日志库(日志实现) 简介
log4j 最早诞生,用的也最多
logback 最晚出现,和log4j同一作者,是log4j的升级版
log-jdk jdk 在1.4版本出现的java.util.logging 简称 log-jdk

在实际的开发中,log4j和logback的使用都非常的广泛,但是如果你现在要开发的是一个新项目,那么推荐使用 logback

MAVEN

为何需要MAVEN

使用maven前

印度挂票

项目臃肿不堪,jar包从各种渠道引入

使用maven后

中国高铁

项目从统一的规范下载,升级管理jar版本只需要修改配置文件

安装

配置环境变量

系统变量名 说明 实例值
MAVEN_HOME maven根目录 C:\tool\apache-maven-3.3.9
Path windows查找命令路径 ;%MAVEN_HOME%\bin

约定配置

##常用Maven目录结构

1
2
3
4
5
6
7
8
9
10
11
12
src
-main
–java java源代码文件
–resources 资源库,会自动复制到classes目录里
–webapp web应用的目录。WEB-INF、css、js等
–test
–java 单元测试java源代码文件
–resources 测试需要用的资源库
target 打包输出目录
pom.xml maven的pom文件
LICENSE.txt Project’s license
README.txt Project’s readme

Maven POM

pom文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

<!-- 模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.thunisoft.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0</version>
</project>

node说明

节点 描述
project 工程的根标签。
modelVersion 模型版本需要设置为 4.0。
groupId 这是工程组的标识。它在一个组织或者项目中通常是唯一的
artifactId 这是工程的标识。它通常是工程的名称。
version 这是工程的版本号。在 artifact 的仓库中,它用来区分不同的版本。

Maven有哪些常用命令?都是什么作用?

Maven 构建生命周期

Maven构建生命周期

Clean(清理)

移除所有上一次构建生成的文件

Compile(编译)

编译项目的源代码。

Package(打包)

将编译后的代码打包成可分发格式的文件,比如JAR、WAR或者EAR文件。

Install(安装)

安装项目包到本地仓库,这样项目包可以用作其他本地项目的依赖。

Deploy(部署)

将最终的项目包复制到远程仓库中与其他开发者和项目共享。

设置JDK版本

方法一

1
2
3
4
5
6
7
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

方法二

1
2
3
4
5
6
7
8
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

Maven 快照(SNAPSHOT)

1、Snapshot 版本代表不稳定、尚处于开发中的版本。

2、Release 版本则代表稳定的版本。

什么情况下该用 SNAPSHOT?

协同开发时

如果 A 依赖构件 B,由于 B 会更新,B 应该使用 SNAPSHOT 来标识自己。
这种做法的必要性可以反证如下:

a

如果 B 不用 SNAPSHOT,而是每次更新后都使用一个稳定的版本,那版本号就会升得太快,每天一升甚至每个小时一升,这就是对版本号的滥用。

b

如果 B 不用 SNAPSHOT, 但一直使用一个单一的 Release 版本号,那当 B 更新后,A 可能并不会接受到更新。因为 A 所使用的 repository 一般不会频繁更新 release 版本的缓存(即本地 repository),所以B以不换版本号的方式更新后,A在拿B时发现本地已有这个版本,就不会去远程Repository下载最新的 B

所有地方都用 SNAPSHOT 版本行不行?

不行。

正式环境中不得使用 snapshot 版本的库。 比如说,今天你依赖某个 snapshot 版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的 snapshot 库。你再次构建时,Maven 会去远程 repository 下载 snapshot 的最新版本,你构建时用的库就是新的 jar 文件了,这时正确性就很难保证了。

Graphql

优点

一种用于 API 的查询语言

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

请求你所要的数据 不多不少

向你的 API 发出一个 GraphQL 请求就能准确获得你想要的数据,不多不少。 GraphQL 查询总是返回可预测的结果。使用 GraphQL 的应用可以工作得又快又稳,因为控制数据的是应用,而不是服务器。

获取多个资源 只用一个请求

GraphQL 查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。

描述所有的可能 类型系统

GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

API 演进 无需划分版本

给你的 GraphQL API 添加字段和类型而无需影响现有查询。老旧的字段可以废弃,从工具中隐藏。通过使用单一演进版本,GraphQL API 使得应用始终能够使用新的特性,并鼓励使用更加简洁、更好维护的服务端代码。

使用你现有的 数据和代码

GraphQL 让你的整个应用共享一套 API,而不用被限制于特定存储引擎。GraphQL 引擎已经有多种语言实现,通过 GraphQL API 能够更好利用你的现有数据和代码。你只需要为类型系统的字段编写函数,GraphQL 就能通过优化并发的方式来调用它们。

入门

Maven

graphql-java需要运行在java 8 及其以上

1
2
3
4
5
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>14.0</version>
</dependency>

Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.StaticDataFetcher;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring;

public class HelloWorld {

public static void main(String[] args) {
String schema = "type Query{hello: String}";

SchemaParser schemaParser = new SchemaParser();
TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);

RuntimeWiring runtimeWiring = newRuntimeWiring()
.type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
.build();

SchemaGenerator schemaGenerator = new SchemaGenerator();
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);

GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
ExecutionResult executionResult = build.execute("{hello}");

System.out.println(executionResult.getData().toString());
// Prints: {hello=world}
}
}

Schema

SDL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 审判组织成员
interface Spzzcy{
# 编号
bh: ID
# 角色
js: String
# 姓名
xm: String
}
# 通用审判组织成员
type T_FY_SPZZCY implements Spzzcy{
bh: ID
js: String
xm: String
}

DataFetcher

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 获取诉讼档案
* @return 诉讼档案获取器
*/
public DataFetcher getSsda() {
return env -> {
GraphQLContext context = env.getContext();
Set<String> columns = GraphQLUtils.getColumns(env.getSelectionSet(), env.getFieldType());
Integer ajlb = context.get("ajlb");
String bhaj = context.get("bhaj");
return graphqlDao.getSsda(ajlb, bhaj, columns);
};
}

TypeResolver

1
2
3
4
5
6
7
8
9
10
/**
* 获取类型转换器
* @return 类型转换器
*/
public static TypeResolver getTypeResolver(String alias) {
return env -> {
GraphQLContext context = env.getContext();
return GraphQLUtils.getType(env.getSchema(), context.get("ajlb"), alias);
};
}

Types

GraphQL 支持以下数据类型

  • Scalar
  • Object
  • Interface
  • Union
  • InputObject
  • Enum

Execution

查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 查询数据
* @param ywlx 业务类型
* @param query 查询条件
* @param variables 查询变量
* @return 案件数据
*/
@Override
public Map<String, Object> query(Integer ywlx, String query, Map<String, Object> variables) {
//当业务类型查询不到相应的案件类别时,(有可能是业务标识为<司法辅助>司法辅助没有业务类型,用的是业务标识)如果案件类别为空,则返回
Integer ajlb = ZzfwGyUtils.getAjlbByYwlx(ywlx);
if (ajlb == null) {
logger.error("未根据所传业务类型找到对应的案件类别,业务类型:{}", ywlx);
return NOT_FOUND;
}
IDictCache dict = DictManager.getDict(ajlb);
logger.info("ywlx:{},query:{},variables,{}",ywlx,query,variables);
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
.context(GraphQLContext.newContext().of("ywlx",ywlx).of("ajlb", ajlb))
.variables(variables)
.root(dict)
.query(query)
.build();
ExecutionResult execute = graphQL.execute(executionInput);
return execute.toSpecification();
}

Data Fetchers

每个graphql字段类型都有一个graphql.schema.DataFetcher关联。其他语言的graphql实现通常将这种代码称为resolvers

如果没有在字段上指定DataFetcher,则将使用graphql.schema.PropertyDataFetcher来获取Java POJO对象的字段值。

Data mapping

PropertyDataFetcher

GraphQL-Java默认的graphql.schema.PropertyDataFetcher支持Map或者POJO
对于Schema中的每个对象,会通过Map的get(String key)方法或者POJO的getter方法获取它并返回

SDL Directives

附加操作

原来的SDL

我们可以看到js属性是个代码值我们需要进行转换,这个通常需要在data fetcher中进行处理

1
2
3
4
5
6
7
# 通用审判组织成员
type T_FY_SPZZCY implements Spzzcy{
bh: ID
js: String
xm: String
}

但是我们现在有更简单的方法

1
2
3
4
5
6
7
8
# 设置代码转换 使用Artery数据字典
directive @dict on FIELD_DEFINITION
# 通用审判组织成员
type T_FY_SPZZCY implements Spzzcy{
bh: ID
js: String @dict
xm: String
}

frp

公司使用NAT协议,所以远程一直靠TeamViewer,然后TeamViewer给我们公司发了律师函,有天逛github无意间发现frp这个“神器”,本着互联网精神,给大家共享一下(被公司网管或者ZF的BM部门抓到可不关我的事)。

Read More