Spring Boot中Aspect实现切面(以记录日志为例)

切面Aspect是Spring的一大优势。面向切面编程往往让我们的开发更加低耦合,也大大减少了代码量,同时让我们更专注于业务模块的开发,把那些与业务无关的东西提取出去,便于后期的维护和迭代。

Maven依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>

日志实体类和Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class SysLog {
private String className;

private String methodName;

private String[] methodParams;

private Object[] paramValues;

private Long execTime;

private String remark;

private String createDate;
}
1
2
3
4
5
6
7
8
9
@Slf4j
@Service
public class SysLogService {

public void save(SysLog sysLog) {
log.info(JSON.toJSONString(sysLog));
}

}

定义日志注解

记录日志使用注解的形式,切点表达式形式无需定义

1
2
3
4
5
6
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WithSysLog {
String value() default "";
}

声明切面,完成日志记录

这里需要对AOP有一定的了解。起码知道切点表达式、环绕通知、前置通知、后置通知等

支持注解形式和切点表达式形式

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
/**
* 系统日志切面
*
* @Author: yezhou
* @Date: 2019/5/9 10:40
* @Version 1.0
*/

@Slf4j
@Aspect //使用@Aspect注解声明一个切面
@Component
public class SysLogAspect {

@Autowired
private SysLogService sysLogService;

/**
* 使用注解的形式
* 也可以通过切点表达式直接指定需要拦截的package, 需要拦截的class 以及 method
* 切点表达式: execution(...)
*/
//@Pointcut("@annotation(me.yezhou.springdemo.annotation.WithSysLog)") //注解形式
@Pointcut("execution(public * me.yezhou.springdemo.controller.SysLogController.*(..))") //切点表达式形式
public void logPointCut() {
log.info("SysLogAspect.logPointCut");
}

/**
* 环绕通知 @Around, 当然也可以使用 @Before (前置通知) @After (后置通知)
*
* @param point
* @return
* @throws Throwable
*/
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
log.info("SysLogAspect.around");
long beginTime = System.currentTimeMillis();
Object result = point.proceed();
long time = System.currentTimeMillis() - beginTime;
try {
saveLog(point, time);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

/**
* 保存日志
*
* @param joinPoint
* @param time
*/
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
sysLog.setExecTime(time);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
sysLog.setCreateDate(dateFormat.format(new Date()));
WithSysLog withSysLog = method.getAnnotation(WithSysLog.class);
if (withSysLog != null) {
//注解上的描述
sysLog.setRemark(withSysLog.value());
}
//请求的 类名、方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
String[] parameterNames = signature.getParameterNames();
sysLog.setClassName(className);
sysLog.setMethodName(methodName);
sysLog.setMethodParams(parameterNames);
//请求的参数
Object[] args = joinPoint.getArgs();
sysLog.setParamValues(args);

sysLogService.save(sysLog);
}
}

测试Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class SysLogController {

//@WithSysLog("测试") //注解形式
@GetMapping("/test") //切点表达式形式
public String test(@RequestParam("name") String name) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return e.getMessage();
}
return name;
}

}

访问:http://127.0.0.1:8080/test?name=Joe.Ye

返回结果:

1
2
SysLogAspect.around
{"className":"me.yezhou.springdemo.controller.SysLogController","createDate":"2019-05-09 11:30:01","execTime":1000,"methodName":"test","methodParams":["name"],"paramValues":["Joe.Ye"],"remark":"测试"}

至此,我们成功实现了使用Aspect实现切面记录日志。

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :