Spring07-切面的应用

Spring切面应用

切面的顺序

1、事务的优先级默认是最小的,Ordered.LOWEST_PRECEDENCE = Integer.MAX_VALUE,值越小,执行目标方法时越先开始,越后结束。所以事务理论上来说是最靠近目标方法的一层切面

2、如果Order相同,则是按照切面字母的顺序来执行切面,@Transactional和@Cacheable,则先执行@Cacheable

多数据源插件(后续补充)

分布式锁插件

思路:

1、声明一个注解

2、切面环绕通知切这个注解

3、使用redis做分布式锁

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
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {

/**
* 锁过期时间 1小时
*
* @return
*/
long expireMillis() default 3600000L;

/**
* 锁尝试时间 1分钟
*
* @return
*/
long timeoutMillis() default 60000L;

/**
* 锁的主key
*
* @return
*/
String mainKey() default "";

/**
* 锁的spEL格式Key
*
* @return
*/
String spelKey() default "";

/**
* 无法获得锁时,是否跳过
* true: 跳过
* false: 默认报错
*
* @return
*/
boolean skipWhenCanNotGetLock() default false;
}


@Slf4j
@Aspect
@Order(2)
@Component
public class DistributedLockAspect {
@Resource
private RedisDistributedLock redisDistributedLock;


String COM_LOCK = "com:lock:";


String COM_LOCK_FEIGN_OAUTH_TOKEN = COM_LOCK + "token:feign_oauth:";
/**
* 所有被@DistributedLock注解的方法,使用分布式锁,限制一个时间内只有一个任务在执行
*
* @param point
* @param distributedLock
* @return
* @throws Throwable
*/
@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint point, DistributedLock distributedLock) throws Throwable {
String key = generateLockKey(point, distributedLock.mainKey(), distributedLock.spelKey());

// 判断无法获得锁的时候,是否可以跳过程序
if (distributedLock.skipWhenCanNotGetLock() && redisDistributedLock.isExist(key)) {
log.warn("DistributedLock 无法获得锁: {}, 锁已存在, 程序将跳过执行", key);
return null;
}

boolean tryLock = false;
try {
tryLock = redisDistributedLock.tryLock(key, distributedLock.expireMillis(),
distributedLock.timeoutMillis());
if (!tryLock) {
throw new DistributedLockConcurrentException("@DistributedLock获取锁失败,已有相同任务在运行");
}
return point.proceed();
} finally {
if (tryLock) {
redisDistributedLock.unLock(key);
}
}
}


/**
* 生成分布式锁的key
*
* @param point
* @return
*/
private String generateLockKey(ProceedingJoinPoint point, String mainKey, String spelKey) {
MethodSignature signature = AopUtil.getMethodSignature(point);

String targrtMainKey;
if (StrUtil.isBlank(mainKey)) {
String className = AopUtil.getClassName(point);
String methodName = signature.getName();
targrtMainKey = className + "_" + methodName;
} else {
targrtMainKey = mainKey;
}

if (StrUtil.isBlank(spelKey)) {
return COM_LOCK + targrtMainKey;
}

Method method = signature.getMethod();
Object[] args = point.getArgs();
String targetSpelKey = AopUtil.parseSpelKey(spelKey, method, args, String.class);

return COM_LOCK + targrtMainKey + "_" + targetSpelKey;
}
}

@Slf4j
@Component
@Validated
public class RedisDistributedLock {

@Resource
private RedisTemplate redisTemplate;

/**
* 尝试获得获得锁
*
* @param key key
* @param expireMillis 锁超时时间
* @param timeoutMillis 获取锁最大等待时间
* @return
*/
public boolean tryLock(String key, long expireMillis, long timeoutMillis) {
String macThreadId = getThreadSignature();
return tryLock(key, macThreadId, expireMillis, timeoutMillis);
}

/**
* 尝试获得获得锁
*
* @param key key
* @param expireMillis 锁超时时间
* @param tryLockCnt 获取锁尝试次数
* @return
*/
public boolean tryLockCnt(String key, @Min(1) int tryLockCnt, long expireMillis) {
String macThreadId = getThreadSignature();
return tryLockCnt(key, macThreadId, tryLockCnt, expireMillis);
}

/**
* 释放锁
*
* @param key 锁的key
* @return
*/
public boolean unLock(String key) {
String macThreadId = getThreadSignature();
return unLock(key, macThreadId);
}


/**
* 延迟释放锁
*
* @param key 锁的key
* @param expireSecond expireSecond
* @return
*/
public boolean unLockLater(String key, long expireSecond) {
String signature = getThreadSignature();
return unLockLater(key, signature, expireSecond);
}


/**
* 机器锁
* 锁住机器的MAC地址
* 只允许同一台机器执行
*
* @param key 锁的key
* @param expireMillis 锁的时间,单位秒,null表示锁1天
* @return
*/
public boolean tryMacLock(String key, long expireMillis, long timeoutMillis) {
//String mac = ServerRunTimeUtil.getMac();
//TODO是否可重写
String mac = "";
return tryLock(key, mac, expireMillis, timeoutMillis);
}

/**
* 尝试获得获得机器锁
*
* @param key key
* @param expireMillis 锁超时时间
* @param tryLockCnt 获取锁尝试次数
* @return
*/
public boolean tryMacLockCnt(String key, @Min(1) int tryLockCnt, long expireMillis) {
//String mac = ServerRunTimeUtil.getMac();
//TODO是否可重写
String mac = "";
return tryLockCnt(key, mac, tryLockCnt, expireMillis);
}

/**
* 释放机器锁
*
* @param key 锁的key
* @return
*/
public boolean unMacLock(String key) {
//String mac = ServerRunTimeUtil.getMac();
//TODO是否可重写
String mac = "";
return unLock(key, mac);
}

/**
* 判断锁是否存在
*
* @param key key
* @return
*/
public boolean isExist(String key) {
String lockKey = generateKey(key);
return redisTemplate.hasKey(lockKey);
}


/**
* 分布式锁加锁
*
* @param key
* @param requesterId
* @param expireMillis
* @param timeoutMillis
* @return
*/
private boolean tryLock(String key, String requesterId, long expireMillis, long timeoutMillis) {
String lockKey = generateKey(key);
//return eadisSyncCommands.lock(lockKey, requesterId, expireMillis, timeoutMillis);
// 利用lambda表达式
return (Boolean) redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
long expireAt = System.currentTimeMillis() + expireMillis + 1;
Boolean acquire = redisConnection.setNX(lockKey.getBytes(), String.valueOf(expireAt).getBytes());
if (acquire) {
return true;
} else {
byte[] value = redisConnection.get(lockKey.getBytes());
if (Objects.nonNull(value) && value.length > 0) {
long expireTime = Long.parseLong(new String(value));
if (expireTime < System.currentTimeMillis()) {
// 如果锁已经过期
byte[] oldValue = redisConnection.getSet(lockKey.getBytes(), String.valueOf(System.currentTimeMillis() + expireMillis + 1).getBytes());
// 防止死锁
return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
}
}
}
return false;
}
});
}

/**
* 尝试获得获得锁
*
* @param key key
* @param requesterId 锁标识
* @param tryLockCnt 尝试获取锁的次数
* @param expireMillis 锁超时时间
* @return
*/
private boolean tryLockCnt(String key, String requesterId, @Min(1) int tryLockCnt, long expireMillis) {
int i = 0;
while (i < tryLockCnt) {
boolean tryLock = tryLock(key, requesterId, expireMillis, 0L);
if (tryLock) {
return true;
}
i++;
if (i == tryLockCnt) {
return false;
}
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
log.warn("获得分布式锁时,尝试睡眠失败", e);
}
}
return false;
}


/**
* 分布式锁释放
*
* @param key
* @param requesterId
* @return
*/
private boolean unLock(String key, String requesterId) {
String lockKey = generateKey(key);
return redisTemplate.delete(lockKey);
}

/**
* 分布式锁延迟释放
*
* @param key
* @param requesterId
* @param expireSecond
* @return
*/
private boolean unLockLater(String key, String requesterId, long expireSecond) {
String lockKey = generateKey(key);
Boolean expire = redisTemplate.expire(lockKey, expireSecond, TimeUnit.SECONDS);
return null == expire ? false : expire;
}

/**
* 获得线程标识
* 只有线程标识相同,才能解锁
*
* @return
*/
private String getThreadSignature() {
//String mac = ServerRunTimeUtil.getMac();
//TODO是否可重写
String mac = "";
//String runtimeMxBeanName = ServerRunTimeUtil.getRuntimeMxBeanName();
//TODO 是否可重写
String runtimeMxBeanName = "";
String currentThreadId = String.valueOf(Thread.currentThread().getId());
return mac + "_" + runtimeMxBeanName + "_" + currentThreadId;
}

/**
* 生成锁的key
*
* @param key
* @return
*/
private String generateKey(String key) {
return key;
}

}

public class AopUtil {

public static Class getClassFromJoinPoint(ProceedingJoinPoint point) {
return point.getTarget().getClass();
}

public static Method getMethodFromJoinPoint(ProceedingJoinPoint point) {
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
return methodSignature.getMethod();
}

public static Object[] getMethodArgsFromJoinPoint(ProceedingJoinPoint point) {
return point.getArgs();
}

/**
* 获得MethodSignature
*
* @param point
* @return
*/
public static MethodSignature getMethodSignature(ProceedingJoinPoint point) {
if (null == point) {
return null;
}
Signature pointSignature = point.getSignature();
if (!(pointSignature instanceof MethodSignature)) {
return null;
}
return (MethodSignature) pointSignature;
}


/**
* 获得Class 全路径
*
* @param point
* @return
*/
public static String getClassName(ProceedingJoinPoint point) {
if (null == point) {
return "";
}
Object target = point.getTarget();
if (null == target) {
return "";
}
Class<?> aClass = target.getClass();
if (null == aClass) {
return "";
}
return aClass.getName();
}

/**
* 获取spEL表达式代表的值
*
* @param key
* @param method
* @param args
* @return
*/
public static <T> T parseSpelKey(String key, Method method, Object[] args, Class<T> clazz) {

if (StrUtil.isEmpty(key)) {
return null;
}

//获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = u.getParameterNames(method);

//使用SPEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
//SPEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//把方法参数放入SPEL上下文中
for (int i = 0; i < paraNameArr.length; i++) {
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(key).getValue(context, clazz);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	@Transactional
@Override
@DistributedLock(spelKey = "#accountId")
public void pay(String accountId, double money) {
//查询余额
double blance = accountInfoDao.qryBlanceByUserId(accountId);
//余额不足正常逻辑
if(new BigDecimal(blance).compareTo(new BigDecimal(money))<0) {
throw new RuntimeException("余额不足");
}

// TransactionalUtil.afterCommit(() -> {
// ((PayService) AopContext.currentProxy()).updateProductStore(1);
// });
//更新余额
int retVal = accountInfoDao.updateAccountBlance(accountId,money);
}

异步事务插件(事务提交后执行)

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
public interface VoidSupplier {
/**
* Gets a result.
*
* @throws Exception
*/
void get();
}

@Slf4j
public class ThreadPoolUtil {

/**
* 线程池execute
*
* @param threadPoolName
* @param supplier
*/
public static void execute(String threadPoolName, VoidSupplier supplier) {
ThreadPoolTaskExecutor executor = SpringUtil.getBean(threadPoolName, ThreadPoolTaskExecutor.class);
executor.execute(() -> {
try {
supplier.get();
} catch (Throwable e) {
log.error("Unexpected error occurred invoking thread pool execute", e);
}
});
}
}

@Slf4j
@Component
public class TransactionalUtil {
@Resource
private DataSource dataSource;

private static DataSource MY_DATA_SOURCE;

@PostConstruct
public void init() {
MY_DATA_SOURCE = dataSource;
}

/**
*
* @param supplier 执行任务
*/
public static void afterCommit(VoidSupplier supplier) throws Exception {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
supplier.get();
return;
}

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
supplier.get();
}
});
}



/**
* 事务提交后异步操作
* 可指定线程池
*
* @param threadPoolName 线程池名称
* @param supplier 执行任务
*/
public static void asyncAfterCommit(String threadPoolName,VoidSupplier supplier) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
ThreadPoolUtil.execute(threadPoolName, supplier);
return;
}
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
ThreadPoolUtil.execute(threadPoolName, supplier);
}
});
}
}


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsyncAfterCommit {
String value() default DEFAULT_EXECUTOR_POOL;
}

@Slf4j
@Aspect
@Order(1)
@Component
public class AsyncAfterCommitAspect {

@Resource
private AsyncUncaughtExceptionHandler syncUncaughtExceptionHandler;

/**
* 所有被@AsyncAfterCommit注解的方法,
* 标注这个注解的方法,拥有提交事务后,异步执行的功能
*
* @param point
* @param asyncAfterCommit
* @return
* @throws Throwable
*/
@Around("@annotation(asyncAfterCommit)")
public Object around(ProceedingJoinPoint point, AsyncAfterCommit asyncAfterCommit) throws Throwable {
String value = asyncAfterCommit.value();
Method method = AopUtil.getMethodFromJoinPoint(point);
Class<?> returnType = method.getReturnType();

// 由于future模式通常需要使用get方法,而【提交事务后】异步执行,无法在方法内获得运算结果。基于此,不提供future模式
if (ListenableFuture.class.isAssignableFrom(returnType)) {
throw new AsyncAfterException("@AsyncAfterCommit不支持ListenableFuture模式");
} else if (Future.class.isAssignableFrom(returnType)) {
throw new AsyncAfterException("@AsyncAfterCommit不支持Future模式");
}

TransactionalUtil.asyncAfterCommit(value, () -> {
try {
point.proceed();
} catch (ExecutionException var2) {
handleError(var2.getCause(), method, point.getArgs());
} catch (Throwable var3) {
handleError(var3, method, point.getArgs());
}
});
return null;
}

private void handleError(Throwable ex, Method method, Object... params) {
try {
syncUncaughtExceptionHandler.handleUncaughtException(ex, method, params);
} catch (Throwable var5) {
log.error("Exception handler for async method '" + method.toGenericString() + "' threw unexpected " +
"exception itself", var5);
}
}
}

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 	@Transactional
@Override
@DistributedLock(spelKey = "#accountId")
@AsyncAfterCommit
public void pay(String accountId, double money) {
//查询余额
double blance = accountInfoDao.qryBlanceByUserId(accountId);
//余额不足正常逻辑
if(new BigDecimal(blance).compareTo(new BigDecimal(money))<0) {
throw new RuntimeException("余额不足");
}

// TransactionalUtil.afterCommit(() -> {
// ((PayService) AopContext.currentProxy()).updateProductStore(1);
// });
//更新余额
int retVal = accountInfoDao.updateAccountBlance(accountId,money);
}

定制化报错通知报警

思路:

1、定义一个注解BizService

2、声明一个切面

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
@Component
@Aspect
@Slf4j
public class BizServiceAspect {

//切自定义注解
@Pointcut("@within(自定义注解)")
public void bizServiceCut() {

}

@Around("bizServiceCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
Object[] args = proceedingJoinPoint.getArgs();
return proceedingJoinPoint.proceed(args);
} catch (Throwable e) {
try {
// 发布告警事件
Class bizClazz = proceedingJoinPoint.getTarget().getClass();
Object source = proceedingJoinPoint.getThis();
Signature sig = proceedingJoinPoint.getSignature();
if (sig instanceof MethodSignature) {
MethodSignature targetMethod = (MethodSignature) sig;
Method bizMethod = bizClazz.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
AppContext.publishEvent(new BizAlertEvent(source, bizClazz, bizMethod, getLogTranceId(), e));
}
} catch (Exception e1) {
log.warn("告警事件发布失败:", e1);
}
throw e;
}
}

private String getLogTranceId() {
return MDC.get(Constant.LOG_TRACE_ID);
}
}

接口出入参日志打印

利用切面的方式:

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
@Aspect
@Component
public class WebLogAspect {
private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
/** 以 controller 包下定义的所有请求为切入点 */
@Pointcut("execution(public * com.ruankao.controller..*.*(..))")
public void webLog() {}
/**
* 在切点之前织入
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint){
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
logger.info("========================================== Start ==========================================");
// 打印请求 url
logger.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
logger.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
logger.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
logger.info("Request Args : {}", joinPoint.getArgs());
}
/**
* 在切点之后织入
* @throws Throwable
*/
@After("webLog()")
public void doAfter() throws Throwable {
logger.info("=========================================== End ===========================================");
// 每个请求之间空一行
logger.info("");
}
/**
* 环绕
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 打印出参
logger.info("Response Args : {}",JSON.toJSON(result));
// 执行耗时
logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
return result;
}
}

利用Filter的方式:

重新定义request response,读取内容再写入

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
@WebFilter(filterName = "logFilter", urlPatterns = "/*")
@Slf4j
@Configuration
public class LogFilter implements Filter {

@SneakyThrows
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

//转换成代理类
ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);

long before = System.currentTimeMillis();

//通过ApplicationContext上下文(spring)找到RequestMappingHandlerMapping这个bean
RequestMappingHandlerMapping handlerMapping = ApplicationContextUtil.getBean(RequestMappingHandlerMapping.class);
//RequestMappingHandlerMapping是对应url和处理类方法的一个类
HandlerExecutionChain handlerChain = handlerMapping.getHandler(requestWrapper);
//通过处理链找到对应的HandlerMethod类
HandlerMethod handler = (HandlerMethod) handlerChain.getHandler();
//HandlerMethod中有bean和method
Object bean = handler.getBean();//处理请求的类
Method method = handler.getMethod();//处理请求的方法

// 打印请求相关参数
log.info("========================================== Start ==========================================");
// 打印请求 url
log.info("URL : {}", httpServletRequest.getRequestURL().toString());
// 打印 Http method
log.info("HTTP Method : {}", httpServletRequest.getMethod());
// 打印调用 controller 的全路径以及执行方法
log.info("Class Method : {}.{}", bean, method);
// 打印请求的 IP
log.info("IP : {}", httpServletRequest.getRemoteAddr());
// 打印请求入参
log.info("Request Args : {}", JSON.toJSON(requestWrapper.getBody()));

log.info("=========================================== End ===========================================");
// 每个请求之间空一行
log.info("");

filterChain.doFilter(requestWrapper, wrapperResponse);
//获取返回值
byte[] content = wrapperResponse.getContent();
// 打印出参
log.info("Response Args : {}", JSON.toJSON(new String(content)));
// 执行耗时
log.info("Time-Consuming : {} ms", System.currentTimeMillis() - before);

//把返回值输出到客户端
ServletOutputStream out = httpServletResponse.getOutputStream();
out.write(content);
out.flush();
}
}

class ResponseWrapper extends HttpServletResponseWrapper {

private ByteArrayOutputStream buffer;

private ServletOutputStream out;

public ResponseWrapper(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
}

@Override
//Servlet容器通过调用getOutputStream()方法获得的输出流将是我们自定义的包装流WapperedOutputStream
public ServletOutputStream getOutputStream() throws IOException {
return out;
}

@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
}

public byte[] getContent() throws IOException {
flushBuffer();
return buffer.toByteArray();
}

class WrapperOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos;

public WrapperOutputStream(ByteArrayOutputStream bos) {
this.bos = bos;
}

@Override
public void write(int b) throws IOException {
bos.write(b);
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setWriteListener(WriteListener arg0) {

}
}

}

class RequestWrapper extends HttpServletRequestWrapper {
private final String body;

public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {

} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}

@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setReadListener(ReadListener readListener) {
}

@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;

}

@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}

public String getBody() {
return this.body;
}

}

常见切面@Around execution表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//@Around("@annotation(自定义注解)")//自定义注解标注在方法上的方法执行aop方法
如:@Around("@annotation(org.springframework.transaction.annotation.Transactional)")

//@Around("@within(自定义注解)")//自定义注解标注在的类上;该类的所有方法(不包含子类方法)执行aop方法
如:@Around("@within(org.springframework.transaction.annotation.Transactional)")

//@Around("within(包名前缀.*)")//com.aop.within包下所有类的所有的方法都会执行(不包含子包) aop方法
如:@Around("within(com.aop.test.*)")

//@Around("within(包名前缀..*)")//com.aop.within包下所有的方法都会执行(包含子包)aop 方法
如:@Around("within(com.aop.test..*)")

//@Around("this(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,但不包含父类的方法)都会执行aop方法
如:@Around("this(com.aop.service.TestService)")

//@Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,包含父类的方法)
如:@Around("this(com.aop.service.TestService)")