Spring Boot AOP记录用户操作日志
来源:
https://mrbird.cc/Spring-Boot-AOP%20log.html
在Spring框架中,使用AOP配合自定义注解可以方便的实现用户操作的监控。首先搭建一个基本的Spring Boot Web环境开启Spring Boot,然后引入必要依赖:
1<dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-jdbc</artifactId>
4</dependency>
5
6<!-- aop依赖 -->
7<dependency>
8 <groupId>org.springframework.boot</groupId>
9 <artifactId>spring-boot-starter-aop</artifactId>
10</dependency>
11
12<!-- oracle驱动 -->
13<dependency>
14 <groupId>com.oracle</groupId>
15 <artifactId>ojdbc6</artifactId>
16 <version>6.0</version>
17</dependency>
18
19<!-- druid数据源驱动 -->
20<dependency>
21 <groupId>com.alibaba</groupId>
22 <artifactId>druid-spring-boot-starter</artifactId>
23 <version>1.1.6</version>
24</dependency>
自定义注解
定义一个方法级别的@Log
注解,用于标注需要监控的方法:
1
2
3public Log {
4 String value() default "";
5}
创建库表和实体
在数据库中创建一张sys_log表,用于保存用户的操作日志,数据库采用oracle 11g:
1CREATE TABLE "SCOTT"."SYS_LOG" (
2 "ID" NUMBER(20) NOT NULL ,
3 "USERNAME" VARCHAR2(50 BYTE) NULL ,
4 "OPERATION" VARCHAR2(50 BYTE) NULL ,
5 "TIME" NUMBER(11) NULL ,
6 "METHOD" VARCHAR2(200 BYTE) NULL ,
7 "PARAMS" VARCHAR2(500 BYTE) NULL ,
8 "IP" VARCHAR2(64 BYTE) NULL ,
9 "CREATE_TIME" DATE NULL
10);
11
12COMMENT ON COLUMN "SCOTT"."SYS_LOG"."USERNAME" IS '用户名';
13COMMENT ON COLUMN "SCOTT"."SYS_LOG"."OPERATION" IS '用户操作';
14COMMENT ON COLUMN "SCOTT"."SYS_LOG"."TIME" IS '响应时间';
15COMMENT ON COLUMN "SCOTT"."SYS_LOG"."METHOD" IS '请求方法';
16COMMENT ON COLUMN "SCOTT"."SYS_LOG"."PARAMS" IS '请求参数';
17COMMENT ON COLUMN "SCOTT"."SYS_LOG"."IP" IS 'IP地址';
18COMMENT ON COLUMN "SCOTT"."SYS_LOG"."CREATE_TIME" IS '创建时间';
19
20CREATE SEQUENCE seq_sys_log START WITH 1 INCREMENT BY 1;
库表对应的实体:
1public class SysLog implements Serializable{
2
3 private static final long serialVersionUID = -6309732882044872298L;
4
5 private Integer id;
6 private String username;
7 private String operation;
8 private Integer time;
9 private String method;
10 private String params;
11 private String ip;
12 private Date createTime;
13 // get,set略
14}
保存日志的方法
为了方便,这里直接使用Spring JdbcTemplate来操作数据库。定义一个SysLogDao接口,包含一个保存操作日志的抽象方法:
1public interface SysLogDao {
2 void saveSysLog(SysLog syslog);
3}
其实现方法:
1
2public class SysLogDaoImp implements SysLogDao {
3
4
5 private JdbcTemplate jdbcTemplate;
6
7
8 public void saveSysLog(SysLog syslog) {
9 StringBuffer sql = new StringBuffer("insert into sys_log ");
10 sql.append("(id,username,operation,time,method,params,ip,create_time) ");
11 sql.append("values(seq_sys_log.nextval,:username,:operation,:time,:method,");
12 sql.append(":params,:ip,:createTime)");
13
14 NamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(this.jdbcTemplate.getDataSource());
15 npjt.update(sql.toString(), new BeanPropertySqlParameterSource(syslog));
16 }
17}
切面和切点
定义一个LogAspect类,使用@Aspect
标注让其成为一个切面,切点为使用@Log
注解标注的方法,使用@Around
环绕通知:
1
2
3public class LogAspect {
4
5
6 private SysLogDao sysLogDao;
7
8 ("@annotation(com.springboot.annotation.Log)")
9 public void pointcut() { }
10
11 ("pointcut()")
12 public Object around(ProceedingJoinPoint point) {
13 Object result = null;
14 long beginTime = System.currentTimeMillis();
15 try {
16 // 执行方法
17 result = point.proceed();
18 } catch (Throwable e) {
19 e.printStackTrace();
20 }
21 // 执行时长(毫秒)
22 long time = System.currentTimeMillis() - beginTime;
23 // 保存日志
24 saveLog(point, time);
25 return result;
26 }
27
28 private void saveLog(ProceedingJoinPoint joinPoint, long time) {
29 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
30 Method method = signature.getMethod();
31 SysLog sysLog = new SysLog();
32 Log logAnnotation = method.getAnnotation(Log.class);
33 if (logAnnotation != null) {
34 // 注解上的描述
35 sysLog.setOperation(logAnnotation.value());
36 }
37 // 请求的方法名
38 String className = joinPoint.getTarget().getClass().getName();
39 String methodName = signature.getName();
40 sysLog.setMethod(className + "." + methodName + "()");
41 // 请求的方法参数值
42 Object[] args = joinPoint.getArgs();
43 // 请求的方法参数名称
44 LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
45 String[] paramNames = u.getParameterNames(method);
46 if (args != null && paramNames != null) {
47 String params = "";
48 for (int i = 0; i < args.length; i++) {
49 params += " " + paramNames[i] + ": " + args[i];
50 }
51 sysLog.setParams(params);
52 }
53 // 获取request
54 HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
55 // 设置IP地址
56 sysLog.setIp(IPUtils.getIpAddr(request));
57 // 模拟一个用户名
58 sysLog.setUsername("mrbird");
59 sysLog.setTime((int) time);
60 sysLog.setCreateTime(new Date());
61 // 保存系统日志
62 sysLogDao.saveSysLog(sysLog);
63 }
64}
测试
TestController:
1
2public class TestController {
3
4 ("执行方法一")
5 ("/one")
6 public void methodOne(String name) { }
7
8 ("执行方法二")
9 ("/two")
10 public void methodTwo() throws InterruptedException {
11 Thread.sleep(2000);
12 }
13
14 ("执行方法三")
15 ("/three")
16 public void methodThree(String name, String age) { }
17}
最终项目目录如下图所示:
启动项目,分别访问:
http://localhost:8080/web/one?name=KangKang
http://localhost:8080/web/two
http://localhost:8080/web/three?name=Mike&age=25
查询数据库:
1SQL> select * from sys_log order by id;
2
3 ID USERNAME OPERATION TIME METHOD PARAMS IP CREATE_TIME
4---------- ---------- ---------- ---------- ------------------------------ ------------------------------ ---------- --------------
5 11 mrbird 执行方法一 6 com.springboot.controller.Test name: KangKang 127.0.0.1 08-12月-17
6 Controller.methodOne()
7
8 12 mrbird 执行方法二 2000 com.springboot.controller.Test 127.0.0.1 08-12月-17
9 Controller.methodTwo()
10
11 13 mrbird 执行方法三 0 com.springboot.controller.Test name: Mike age: 25 127.0.0.1 08-12月-17
12 Controller.methodThree()
source code:
https://github.com/wuyouzhuguli/Spring-Boot-Demos/tree/master/07.Spring-Boot-AOP-Log :
● Spring Boot Admin 2.2.0发布,支持最新Spring Boot/Cloud之外,新增中文展示!
● 你应该知道的 @ConfigurationProperties 注解的使用姿势,这一篇就够了
● 使用 Spring Framework 时常犯的十大错误
● 用 Spring 的 BeanUtils 前,建议先了解这几个坑!
● 别在 Java 代码里乱打日志了,这才是打印日志的正确姿势!
● 面试官:你说 HashMap 线程不安全,它为啥不安全呢?
● Java 8 Stream Api 中的 peek 操作
● SonarQube 搭建好了,5分钟Docker搭建Maven私服
● 无套路,3分钟带你轻松上手SonarQube - 代码质量检测平台
如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!