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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
| import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 在方法中加入该注解,并配置指定格式的SQL模板,自动的填充SQL模板中指定的附属字段的值到返回对象和集合中
**/
@Slf4j
@Aspect
@RequiredArgsConstructor
@Component
public class FieldSqlReflectionAspect {
@Mapper
public interface GeneralMapper {
@Select("${sql}")
List<Map<String, Object>> select(@Param("sql") String strSql);
//当前
// @Insert("${sql}")
// int insert(@Param("sql") String strSql);
//
// @Delete("${sql}")
// int delete(@Param("sql") String strSql);
//
// @Update("${sql}")
// int update(@Param("sql") String strSql);
}
private final GeneralMapper generalMapper;
@Pointcut("@annotation(org.tekj.base.aop.FieldSqlReflection)")
private void cutMethod() {
}
/**
* 环绕通知:灵活自由的在目标方法中切入代码
*/
@Around("cutMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
FieldSqlReflection fieldReflection = getDeclaredAnnotation(joinPoint);
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 请求参数的处理 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
if (fieldReflection.reqEnable()) {
// 获取方法传入参数
Object[] params = joinPoint.getArgs();
if (params != null && params.length > 0) {
int reqParamsIndex = fieldReflection.reqParamsIndex();
if (reqParamsIndex >= 0 && reqParamsIndex < params.length) {
// 传入参数中指定索引的参数
Object reqParam = params[reqParamsIndex];
if (reqParam != null) {
String[] reqParamsSqlTemplates = fieldReflection.reqParamsSqlTemplates();
if (ArrayUtil.isNotEmpty(reqParamsSqlTemplates)) {
boolean isMap = reqParam instanceof Map;
for (String reqParamsSqlTemplate : reqParamsSqlTemplates) {
if (StrUtil.isEmpty(reqParamsSqlTemplate) || !reqParamsSqlTemplate.contains("id") || !reqParamsSqlTemplate.contains("#{")) {
//防御编程,防止SQL模板写错
continue;
}
try {
//解析SQL模板,处理目标字段和源字段
String mainIdFieldStr = ReUtil.getGroup1(Pattern.compile("#\\{(.*?)\\}"), reqParamsSqlTemplate);//源字段主键
List<String> mainIdFields = StrUtil.split(mainIdFieldStr, ',');//存在多个主键id的情况
List<String> cfDists = ReUtil.findAllGroup1(Pattern.compile("[as,AS] (\\w+)\\s*[\\\\, ]", Pattern.MULTILINE), reqParamsSqlTemplate);//目标字段集合
cfDists.remove("id");//目标,不应该包含主键
cfDists.removeAll(mainIdFields);//目标,不应该包含主键
boolean hasAllField = false;
if (!isMap) {//校验对象中的字段是否都存在
List<String> fieldNames = CollUtil.newArrayList(mainIdFields);
fieldNames.addAll(cfDists);
hasAllField = fieldNames.stream().allMatch(fieldName -> ReflectUtil.hasField(reqParam.getClass(), fieldName));
}
boolean hasFileId = ObjectUtil.isNotEmpty(mainIdFields) && ObjectUtil.isNotEmpty(cfDists)
&& (isMap || hasAllField);
if (!hasFileId) {
log.warn("ReqFieldReflection: Method:{} 自定义字段配置错误: 存在部分自定义的字段定义不存在:{},请检查!", methodName, mainIdFields);
} else {
Set<String> mainIdVles = new HashSet<>();
for (String mainIdField : mainIdFields) {
if (StrUtil.isEmpty(mainIdField)) continue;
if (isMap) {
mainIdVles.add(MapUtil.getStr((Map<?, ?>) reqParam, mainIdField));
} else {
mainIdVles.add(ReflectUtil.getFieldValue(reqParam, mainIdField) + "");
}
}
mainIdVles = mainIdVles.stream().filter(StrUtil::isNotEmpty).collect(Collectors.toSet());
if (CollUtil.isNotEmpty(mainIdVles)) {
String sqlFormat = reqParamsSqlTemplate.replaceAll("#\\{.*?\\}", CollUtil.join(mainIdVles, ","));
List<Map<String, Object>> maps = generalMapper.select(sqlFormat);
if (CollUtil.isEmpty(maps) || maps.size() > 1) {
log.warn("ReqFieldReflection: Method:{} sql:{} 查询结果为空或超过一条,请检查!", methodName, sqlFormat);
} else {
//填充目标字段的值
Map<String, Object> first = CollUtil.getFirst(maps);
for (String cfDist : cfDists) {
if (first.containsKey(cfDist)) {
log.info("ReqFieldReflection {}: 通过:{} 值:{} 自动为字段:{} 赋值为:{}", methodName, mainIdFields, mainIdVles, cfDist, first.get(cfDist));
if (isMap) {
((Map) reqParam).put(cfDist, first.get(cfDist));
} else {
ReflectUtil.setFieldValue(reqParam, cfDist, Convert.convert(ReflectUtil.getField(reqParam.getClass(), cfDist).getType(), first.get(cfDist)));
}
}
}
}
} else {
log.warn("ReqFieldReflection: Method:{} 没有找到请求数据中的相关主键字段或主键字段的值为空({})", methodName, mainIdFieldStr);
}
}
} catch (Exception e) {
log.error("ReqFieldReflection: Method:{} 自定义字段存在异常,附属字段查询异常", methodName);
e.printStackTrace();
}
}
} else {
log.warn("ReqFieldReflection: Method:{} 请求参数SQL模板为空,不做处理", methodName);
}
} else {
log.warn("ReqFieldReflection: Method:{} 指定请求参数为null,不做处理", methodName);
}
} else {
log.warn("ReqFieldReflection: Method:{} 请求参数索引:{} 超出范围,不做处理", methodName, reqParamsIndex);
}
} else {
log.warn("ReqFieldReflection: Method:{} 无请求参数不做处理", methodName);
}
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 请求参数的处理 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
// 执行源方法
Object proceed = joinPoint.proceed();
/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 响应结果的处理 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */
// 获取目标方法的名称,调试日志用
if (!fieldReflection.respEnable()) {
log.info("RespFieldReflection: Method:{} 未开启响应结果字段填充功能,不做处理", methodName);
return proceed;
}
if (fieldReflection == null) {
log.warn("RespFieldReflection: Method:{} 没有RespFieldReflection注解不做处理", methodName);//正常走不到这里
return proceed;
}
if (ObjectUtil.isNull(proceed)) {
log.warn("RespFieldReflection: Method:{} 目标结果数据为空不做处理", methodName);
return proceed;
}
/**
整体数据填充逻辑
0. 在需要处理返回结果的方法上追加@RespFieldReflection注解,根据注解中的使用说明做好配置
1. 反射获取出需要处理的结果数据,包装成集合对象,主列表数据
2. 在注解中拿到具体的查询SQL模板集合遍历,根据模板中的指定查询字段和填充字段,从主列表数据中,获取对应的值,填充到SQL模板中,执行SQL模板,获取结果数据;结果数据整理出VLE_MAP,ID_SET_FIELDS_MAP两个值映射对象;
3. 遍历主列表数据,根据VLE_MAP和ID_SET_FIELDS_MAP,填充目标字段值
* **/
//step.1
List<?> rows = null;
Object distData = proceed;
if (fieldReflection.respCustomFieldSpelEnable() && StrUtil.isNotBlank(fieldReflection.respCustomFieldSpel())) {
//自定义核心字段的映射,使用spel表达式
try {
StandardEvaluationContext ctx = new StandardEvaluationContext();
distData = new Object();
ctx.setVariable("distData", distData);
ctx.setVariable("resp", proceed);
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(fieldReflection.respCustomFieldSpel());
distData = expression.getValue(ctx);
if (distData == null) {
log.warn("RespFieldReflection: Method:{} 自定义核心响应结果字段映射表达式:{} 解析结果为空,不做处理", methodName, fieldReflection.respCustomFieldSpel());
return proceed;
}
} catch (Exception e) {
log.error("RespFieldReflection: Method:{} 自定义核心响应结果字段映射表达式:{} 解析异常,不做处理", methodName, fieldReflection.respCustomFieldSpel(), e);
return proceed;
}
}
if (distData instanceof Collection) {
rows = new ArrayList((Collection) distData);
} else if (distData instanceof Map) {//单个对象Map的情况
rows = CollUtil.newArrayList((Map) distData);
} else {//单个对象的情况
rows = CollUtil.newArrayList(distData);
}
if (CollUtil.isEmpty(rows)) {
log.warn("RespFieldReflection: Method:{} 无目标结果数据集合不做处理", methodName);
return proceed;
}
Object respItemObj = CollUtil.getFirst(rows);
boolean isMap = respItemObj instanceof Map;
//step.2
//K:需要填充名称的id的字段名称(mainIdField) K:K:主键字段(mainIdField)对应的值(ID) K:V:主键字段(mainIdField)对应的数据对象 K:K:K:主键字段对应的表字段名称 K:K:V:主键字段对应的表字段值
Map<String, Map<String, Map<String, Object>>> VLE_MAP = new HashMap<>();
//K:需要填充名称的id的字段名称(mainIdField) V:需要填充值的字段名称的字段集合(不存在不填充值)
Map<String, List<String>> ID_SET_FIELDS_MAP = new HashMap<>();
String[] cfSrcQuerySqlTemplates = fieldReflection.respSqlTemplates();
for (String cfSrcQuerySqlTemplate : cfSrcQuerySqlTemplates) {
if (StrUtil.isEmpty(cfSrcQuerySqlTemplate) || !cfSrcQuerySqlTemplate.contains("id") || !cfSrcQuerySqlTemplate.contains("#{")) {
//防御编程,防止SQL模板写错
continue;
}
try {
//解析SQL模板,处理目标字段和源字段
String mainIdFieldStr = ReUtil.getGroup1(Pattern.compile("#\\{(.*?)\\}"), cfSrcQuerySqlTemplate);//源字段主键
List<String> mainIdFields = StrUtil.split(mainIdFieldStr, ',');//存在多个主键id的情况
List<String> cfDists = ReUtil.findAllGroup1(Pattern.compile("[as,AS] (\\w+)\\s*[\\\\, ]", Pattern.MULTILINE), cfSrcQuerySqlTemplate);//目标字段集合
cfDists.remove("id");//目标,不应该包含主键
cfDists.removeAll(mainIdFields);//目标,不应该包含主键
boolean hasAllField = false;
if (!isMap) {//校验对象中的字段是否都存在
List<String> fieldNames = CollUtil.newArrayList(mainIdFields);
fieldNames.addAll(cfDists);
hasAllField = fieldNames.stream().allMatch(fieldName -> ReflectUtil.hasField(respItemObj.getClass(), fieldName));
}
boolean hasFileId = ObjectUtil.isNotEmpty(mainIdFields) && ObjectUtil.isNotEmpty(cfDists)
&& (isMap || hasAllField);
if (!hasFileId) {
log.warn("RespFieldReflection: Method:{} 自定义字段配置错误: 存在部分自定义的字段定义不存在:{},请检查!", methodName, mainIdFields);
} else {
Set<String> mainIds = new HashSet<>(rows.stream()
.filter(ObjectUtil::isNotNull)
.flatMap(item -> {
if (isMap) {
return mainIdFields.stream()
.map(mainIdField -> MapUtil.getStr((Map<?, ?>) item, mainIdField))
.filter(StrUtil::isNotEmpty);
} else {
return mainIdFields.stream()
.map(mainIdField -> ReflectUtil.getFieldValue(item, mainIdField) + "")
.filter(StrUtil::isNotEmpty);
}
}).collect(Collectors.toSet()));
if (CollUtil.isNotEmpty(mainIds)) {
String sqlFormat = cfSrcQuerySqlTemplate.replaceAll("#\\{.*?\\}", CollUtil.join(mainIds, ","));
try {
List<Map<String, Object>> maps = generalMapper.select(sqlFormat);
if (CollUtil.isNotEmpty(maps)) {
if (!CollUtil.getFirst(maps).containsKey("id")) {
log.warn("RespFieldReflection: Method:{} 查询:{} 自定义字段查询结果中不存在id字段,请检查!", methodName, sqlFormat);
} else {
boolean isMultiId = mainIdFields.size() > 1;//多主键id的情况下,默认是主键id和对应的查询字段是一一对应处理的
for (String mainIdField : mainIdFields) {
Map<String, Map<String, Object>> distMap;
if (isMultiId) {
String mainIdName = mainIdField.replace("id", "name").replace("Id", "Name");
distMap = new HashMap<>();
for (Map<String, Object> map : maps) {
if (Objects.isNull(map)) continue;
distMap.put(map.get("id") + "", new HashMap<String, Object>() );
}
VLE_MAP.put(mainIdField, distMap);
} else {
distMap = maps.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(
item -> item.get("id") + "",
item -> item));
}
Map<String, Map<String, Object>> existMap = VLE_MAP.getOrDefault(mainIdField, new HashMap<>());
if (MapUtil.isNotEmpty(existMap)) {
//融合已存在的字段
for (Map.Entry<String, Map<String, Object>> existEntry : existMap.entrySet()) {
if (distMap.containsKey(existEntry.getKey())) {
Map<String, Object> innerObject = existEntry.getValue();
innerObject.putAll(distMap.get(existEntry.getKey()));
} else {
existMap.put(existEntry.getKey(), distMap.get(existEntry.getKey()));
}
}
} else {
existMap.putAll(distMap);
}
VLE_MAP.put(mainIdField, existMap);
}
}
}
} catch (Exception e) {
log.error("RespFieldReflection: Method:{} 附属字段查询异常:{}", methodName, e.getMessage());
e.printStackTrace();
}
}
}
boolean isMultiId = mainIdFields.size() > 1;//多主键id的情况下,默认是主键id和对应的查询字段是一一对应处理的
for (String mainIdField : mainIdFields) {
List<String> distCf = new ArrayList<>();
if (isMultiId) {
String mainIdName = mainIdField.replace("id", "name").replace("Id", "Name");
distCf = cfDists.stream()
.filter(item -> ObjectUtil.equal(item, mainIdName))
.collect(Collectors.toList());
} else {
distCf = cfDists;
}
List<String> orDefault = ID_SET_FIELDS_MAP.getOrDefault(mainIdField, new ArrayList<>());
orDefault.addAll(distCf);
ID_SET_FIELDS_MAP.put(mainIdField, orDefault);
}
} catch (Exception e) {
log.warn("RespFieldReflection: Method:{} 自定义字段存在异常,附属字段查询异常", methodName);
e.printStackTrace();
}
}
//自定义数据处理器
FieldSqlReflection.RespProcessor<Object> rsProcessor = null;
if (!FieldSqlReflection.DefaultRespProcessor.class.getName().equals(fieldReflection.respProcessor().getName())) {
rsProcessor = (FieldSqlReflection.RespProcessor<Object>) ReflectUtil.newInstance(fieldReflection.respProcessor());//TODO 针对此类对象做缓存处理,防止每次
}
//step.3
for (Object row : rows) {
if (rsProcessor != null) {
rsProcessor.bfIfProcess(row);
}
try {
//自定义字段填充
if (MapUtil.isNotEmpty(VLE_MAP)) {
for (Map.Entry<String, List<String>> cfSrcs : ID_SET_FIELDS_MAP.entrySet()) {
String mainIdField = cfSrcs.getKey();
if (!VLE_MAP.containsKey(mainIdField)) continue;
Map<String, Map<String, Object>> vleMap = VLE_MAP.get(mainIdField);
List<String> cfDists = cfSrcs.getValue();
for (String cfDist_ : cfDists) {
if (isMap) {
String cfSrcVle = MapUtil.getStr((Map<?, ?>) row, mainIdField);
if (StrUtil.isEmpty(cfSrcVle)) continue;
if (vleMap.containsKey(cfSrcVle)) {
Map<String, Object> vle = vleMap.get(cfSrcVle);
if (vle != null) {
((Map<String, Object>) row).put(cfDist_, vle.get(cfDist_));
}
}
} else {
String cfSrcVle = ReflectUtil.getFieldValue(row, mainIdField) + "";
if (StrUtil.isEmpty(cfSrcVle)) continue;
if (vleMap.containsKey(cfSrcVle)) {
Map<String, Object> vle = vleMap.get(cfSrcVle);
if (vle != null) {
Object finalVle = vle.get(cfDist_);
ReflectUtil.setFieldValue(row, cfDist_, Convert.convert(ReflectUtil.getField(row.getClass(), cfDist_).getType(), finalVle));
}
}
}
}
}
}
} catch (Exception e) {
log.error("AutoCommonBizInfo: Method:{} 自动填充业务数据值异常:{}", methodName, e.getMessage());
e.printStackTrace();
}
if (rsProcessor != null) {
rsProcessor.afIfProcess(row);
}
}
/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 响应结果的处理 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */
return proceed;
}
/**
* 获取方法中声明的注解
*
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
public FieldSqlReflection getDeclaredAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 反射获取目标类
Class<?> targetClass = joinPoint.getTarget().getClass();
// 拿到方法对应的参数类型
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
// 根据类、方法、参数类型(重载)获取到方法的具体信息
Method objMethod = targetClass.getMethod(methodName, parameterTypes);
// 拿到方法定义的注解信息
FieldSqlReflection annotation = objMethod.getDeclaredAnnotation(FieldSqlReflection.class);
// 返回
return annotation;
}
}
|