# Java 系统的结构
首先认识下整个 Java 服务系统的结构,以用户 kmyckj 的系统 prj_java 为例,如:
├─ kedao
│ ├─ .vscode // VS Code 远程连接到服务器时,打开的工作路径:/opt/kedao,即kedao中间件的安装路径
│ ├─ bin // 可执行程序的目录,服务发布后也在此目录的 lib/用户帐号/系统名称 下
│ ├─ document // 文档存放目录
│ ├─ projects // 前端工程目录
│ ├─ publish // 发布文件目录
│ ├─ src_c // C/C++服务工程源代码目录
│ ├─ src_java // Java服务工程源代码目录
│ │ └─ kmyckj // 主用户帐号名称,开发服务器归属用户的用户名称,只有一个
│ │ ├─ prj_java // 系统工程源码目录,名称随着系统名称而变化
│ │ │ ├─ lib // 其他自定义jar包文件的目录,需要自己在 pom.xml 中配置,也可以不需要
│ │ │ ├─ src // 代码目录
│ │ │ │ ├─ main // main
│ │ │ │ │ ├─ java // java
│ │ │ │ │ │ ├─ kmyckj // 注册kedao开发者的主用户帐号名称,包的路径
│ │ │ │ │ │ │ └─ prj_java // 系统的名称,包的路径
│ │ │ │ │ │ │ ├─ model // 数据结构类文件夹
│ │ │ │ │ │ │ ├─ services // 服务文件夹
│ │ │ │ │ │ │ │ ├─ basic_func_employee // 员工模块
│ │ │ │ │ │ │ │ ├─ basic_func_funcRounter // 功能路由模块
│ │ │ │ │ │ │ │ ├─ basic_func_organization // 组织机构模块
│ │ │ │ │ │ │ │ ├─ basic_func_post // 岗位模块
│ │ │ │ │ │ │ │ ├─ basic_func_role_permission // 角色权限模块
│ │ │ │ │ │ │ │ ├─ sys_login // 登录模块
│ │ │ │ │ │ │ │ ├─ ... // 服务模块文件夹
│ │ │ │ │ │ │ │ │ └─ ... // 服务文件
│ │ │ │ │ │ │ │ └─ ... // 其他模块文件夹
│ │ │ │ │ │ │ ├─ utils // 公共方法类文件夹
│ │ │ │ │ │ │ └─ ... // 其他文件
│ │ │ │ │ │ ├─ kedaoUtils // 日志输出动态库的单元文件
│ │ │ │ │ │ │ └─ SyncLog.java // 注册kedao开发者的帐号名称,包的路径
│ │ │ │ │ │ └─ ... // 其他资源文件
│ │ │ │ │ └─ resources // 资源文件夹
│ │ │ │ │ ├─ prj_java.properties // 对应系统的属性文件
│ │ │ │ │ ├─ libKedaoSyncLog.so // 日志输出动态库文件
│ │ │ │ │ └─ ... // 其他资源文件
│ │ │ │ └─ test // 测试
│ │ │ ├─ target // 编辑后的文件目录,在编辑后,会自动将jar包文件复制并覆盖到对应的 bin/lib/kmyckj/prj_java 目录下
│ │ │ └─ pom.xml // Maven 的 pom.xml
│ │ └─ ... // 其他系统的工程源码目录
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
34
35
36
37
38
39
40
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
34
35
36
37
38
39
40
这是默认的系统结构,如果自己有更优秀的系统框架,可以替换成自己的框架。
注意:如果替换成自己的框架,需要保证服务类的 package 关系,以及服务的参数正确,如:
package kmyckj.prj_java.services.mdl_test;
...
public class svc_test_query_function {
...
public String exec_svc(final String in_data, final String http_headers) {
...
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 重点掌握 utils 下的代码
在进行服务开发之前,一定要熟悉 utils 下的这几个源码文件:
# 用户登录身份验证的逻辑,当然,也可以更改成自己的实现逻辑
CheckUserLogin.java
# 数据访问帮助类,重点掌握,功能已满足绝大部分应用的需求,也可以自己扩展功能
JDBCHelper.java
1
2
3
4
5
2
3
4
5
# 模块的文件结构
modules // 模块目录
├─ mdl_test // 模块名称
│ ├─ public // 模块内的公共文件目录
│ │ ├─ f_custom_dll_init.hpp // 每个模块下,都有一个自定义的初始化公共函数文件
│ │ └─ ... // 自己增加的公共源码文件
│ ├─ service // 服务文件目录
│ │ ├─ svc_test_query_function.hpp // 服务源码文件,与服务名称同名的 *.hpp 文件
│ │ └─ ... // 其他服务的源码文件
│ ├─ makefile // 模块动态库的 makefile 文件
│ └─ mdl_test.cpp // 模块动态库的 main 文件,与模块同名的 *.cpp 文件
└─ ...
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
makefile:自动生成,不需要改动;
mdl_test.cpp:自动生成,不需要改动;
public/f_custom_dll_init.hpp:根据业务需要,修改动态库文件的初始化函数,大多数情况不需要改动;
service/svc_test_query_function.hpp:service 目录下的服务文件,也就是注册服务的代码文件,在这里实现业务逻辑;
其他文件,大多数情况不需要改动,只需关注服务的核心逻辑开发即可。
# 编写服务
我们通过注册服务后生成的服务代码,增加业务逻辑
package kmyckj.prj_java.services.mdl_test;
import java.lang.reflect.Type;
import java.util.*;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import kmyckj.prj_java.model.*;
import kmyckj.prj_java.utils.*;
import kedaoUtils.SyncLog;
public class svc_test_query_function {
SyncLog syncLog = new SyncLog();
public void init_log(int msg_type) { syncLog.setType(msg_type); }
public String exec_svc(final String in_data, final String http_headers) {
Gson gson = new Gson();
SVC_REQUEST_OBJ<SYS_FUNCTION> req = new SVC_REQUEST_OBJ<SYS_FUNCTION>(); // 服务的请求参数
SVC_RESPONSE_OBJ<List<SYS_FUNCTION>> resp = new SVC_RESPONSE_OBJ<List<SYS_FUNCTION>>(); // 服务的返回参数
int iRst = 0;
StringBuilder sql_txt = new StringBuilder();
ArrayList<String> lst_params = new ArrayList<>();
OTL_EXC sqlMsg = new OTL_EXC();
// 建立数据库连接
JDBCHelper dbConn = new JDBCHelper();
try {
// 获取请求头包信息(如果不需要从头部里获取信息,可以注释掉)
// HttpHeaders httpHeaders = gson.fromJson(http_headers, HttpHeaders.class);
// 建立数据库连接
dbConn.getConnection(sqlMsg);
// 解析入参: 入参 in_data 字符串转对象 req
Type type = new TypeToken<SVC_REQUEST_OBJ<SYS_FUNCTION>>(){}.getType();
req = gson.fromJson(in_data, type);
if (req == null) {
resp.err_msg = "服务 svc_test_query_function :解包失败。";
resp.code = -1;
// 打印错误日志
syncLog.cerr(resp.err_msg);
// 断开数据库连接
dbConn.close();
return gson.toJson(resp);
}
// 检查用户登录有效性
// 函数说明:在账号登录时,产生一个随机字符串 login_key,每次服务请求,都会带着 login_key,与数据库的值进行比较,相等,则表示是有效的请求;不相等则认为是无效的请求。
if (!CheckUserLogin.check_user_login(dbConn, req.sys_head.usr_id, req.sys_head.login_key)) {
iRst = -1;
resp.code = iRst;
resp.err_msg = "服务[svc_test_query_function]验证身份信息失败。";
syncLog.cerr(resp.err_msg);
dbConn.close();
return gson.toJson(resp);
}
// 开始事务
dbConn.begin(sqlMsg);
// ---------------------------------------------------------------------
// 编写业务逻辑代码
// ---------------------------------------------------------------------
// 组织SQL语句
sql_txt.setLength(0);
if (req.data.menu_title.equals(""))
{
sql_txt.append( "SELECT * FROM sys_function ORDER BY menu_title");
}
else
{
sql_txt.append( "SELECT * FROM sys_function WHERE menu_title = ?");
}
// 组织参数
lst_params.clear();
if (!req.data.menu_title.equals(""))
{
lst_params.add(req.data.menu_title);
}
// 声明变量,接收查询结果
ArrayList<SYS_FUNCTION> v_sys_functions = new ArrayList<>();
// 执行查询
dbConn.executeQueryToObjs(sql_txt, lst_params, v_sys_functions, SYS_FUNCTION.class, sqlMsg);
if (sqlMsg.code != 0)
{
resp.code = sqlMsg.code;
resp.err_msg = "服务[svc_test_query_function]-查询 sys_function 时发生错误";
// 打印错误日志
syncLog.cerr(resp.err_msg);
// 事务回滚
dbConn.rollback(sqlMsg);
// 断开数据库连接
dbConn.close();
// 返回结果
return gson.toJson(resp);
}
/*
JDBCHelper 中常用的函数有:
1、原子服务,实现数据表的增删改查;使用原子服务时,数据表必须有唯一健字段
atom_exec_delete()
atom_exec_insert()
atom_exec_select()
atom_exec_update()
2、执行SQL语句
execute()
executeUpdate()
executeBatch()
3、执行SQL查询语句,返回单个值
executeQueryToStringValue()
executeQueryToIntegerValue()
executeQueryToDoubleValue()
4、执行SQL查询语句,返回多条记录容器 ArrayList<>
executeQueryToObjs()
5、执行SQL查询语句,返回单条记录的数据对象
executeQueryToObj()
6、执行SQL查询语句,返回多条记录 ArrayList<Map<string, string>>
executeQueryToMaps()
7、执行SQL查询语句,返回单条记录的 Map<string, string>
executeQueryToMap()
*/
/*
关于日志输出,日志输出有3种方式,都支持多线程高并发:
1、日志输出到 stdout,通常用在开发过程中打印日志,用来辅助测试,发布生产环境时,应该删除此类日志的输出
syncLog.cerr("错误日志输出");
2、错误日志输出到 stderr,通常用在服务发生错误时的日志打印
syncLog.cout("日志输出");
3、日志输出到 log 日志文件,每天产生一个log日志文件,在 bin/log 目录下;kedao中间件默认输出每一个服务的执行情况
syncLog.log("日志输出");
*/
// ---------------------------------------------------------------------
// 将结果赋值给返回对象
// ---------------------------------------------------------------------
resp.data = v_sys_functions;
// 提交事务
dbConn.commit(sqlMsg);
// ------------------------------------------------------------------------------------------
// 返回结果
// ------------------------------------------------------------------------------------------
resp.code = iRst;
// 断开数据库连接
dbConn.close();
return gson.toJson(resp);
}
catch(Exception e) {
// 回滚事务
dbConn.rollback(sqlMsg);
// 错误码
iRst = -1;
resp.code = iRst;
resp.err_msg = e.getMessage();
String out_str = gson.toJson(resp);
// 打印错误日志
syncLog.cerr(resp.err_msg);
// 断开数据库连接
dbConn.close();
return out_str;
}
}
}
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# 编译服务
通过 Maven 编译 Java

# Jar包的输出路径
在 bin/lib/用户名/系统名 目录下,生成与模块名同名的 *.jar 。