SprintBoot之AOP整合系统日志

前言

涉及到的技术:SpringBootMySQLSpring-Data-JPA

AOP指面向切面

思路

通过aop,将每次请求的信息,记录保存到数据库中去。

代码实现

数据库

我们先建立一张数据库表sys_log,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DROP TABLE IF EXISTS `sys_log`;
CREATE TABLE `sys_log` (
`id` varchar(64) NOT NULL,
`class_name` varchar(64) DEFAULT NULL,
`method_name` varchar(64) DEFAULT NULL,
`params` varchar(255) DEFAULT NULL,
`exec_time` longblob,
`remark` varchar(64) DEFAULT NULL,
`create_date` varchar(64) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`ip` varchar(64) DEFAULT NULL,
`http_method` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

目录结构:

项目的目录结构,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├─src
├─main
├─java
│ └─com
│ └─seamoon
│ ├─aspect
│ ├─controller
│ ├─domain
│ ├─interfaceAnnotation
│ ├─repository
│ └─service
│ └─Impl
└─resources
├─static
└─templates

主要代码

通过Maven引入一些jar,比如:数据库驱动,JPA,AOP,hutool工具包
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
41
42
43
44
45
46
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>

配置完jar,写写配置文件,这里小编采用了yml后缀文件的格式:

1
2
3
4
5
6
7
8
9
10
11
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sys?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC
username: root
password: root

jpa:
hibernate:
ddl-auto: update
show-sql: true

sysLog.java

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
package com.seamoon.aop.domain;


import jdk.internal.vm.annotation.Stable;
import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Data
public class SysLog {

@Id
private String id;

/*
* 类名
* */
@Column(nullable = false)
private String className;

/*
* 方法名
* */
@Column(nullable = false)
private String methodName;

/*
* 参数
* */
@Column(nullable = false)
private String params;

/*
* 执行时间
* */
@Column(nullable = false)
private Long execTime;

/*
* 切面标记
* */
private String remark;

/*
* 创建时间
* */
@Column(nullable = false)
private String createDate;

/*
* 请求URL
* */
// @Column(nullable = false)
private String url;

/*
* 请求IP
* */
@Column(nullable = false)
private String ip;

/*
* http method
* */
@Column(nullable = false)
private String httpMethod;
}

SysLogRepository.java

1
2
3
4
5
6
7
8
9
10
package com.seamoon.aop.repository;

import com.seamoon.aop.domain.SysLog;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SysLogRepository extends JpaRepository<SysLog,String> {


}

SysLogService.java

1
2
3
4
5
6
7
8
9
package com.seamoon.aop.service;

import com.seamoon.aop.domain.SysLog;

public interface SysLogService {

boolean saveSysLog(SysLog sysLog);
}

SysLogServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.seamoon.aop.service.Impl;

import com.seamoon.aop.domain.SysLog;
import com.seamoon.aop.repository.SysLogRepository;
import com.seamoon.aop.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogRepository sysLogRepository;

@Override
public boolean saveSysLog(SysLog sysLog) {
sysLogRepository.save(sysLog);
// log.info(sysLog.getParams());
log.info("成功");
return true;
}
}

SysLogAnnotation.java

1
2
3
4
5
6
7
8
9
10
11
package com.seamoon.aop.interfaceAnnotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLogAnnotation {
String value() default "";
}

SysLogAspect.java

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
package com.seamoon.aop.aspect;

import cn.hutool.core.util.IdUtil;
import com.google.gson.Gson;
import com.seamoon.aop.domain.SysLog;
import com.seamoon.aop.interfaceAnnotation.SysLogAnnotation;
import com.seamoon.aop.service.SysLogService;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;


/**
* 这里我们使用注解的形式
* 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
* 切点表达式: execution(...)
* 例如:execution(public * com.example.demo.controller.*.*(..))
*/
@Pointcut("@annotation(com.seamoon.aop.interfaceAnnotation.SysLogAnnotation)")
public void logPointCut() {}

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

/**
* 保存日志
* @param joinPoint
* @param time
*/
private void saveLog(ProceedingJoinPoint joinPoint, long time) {
SysLog sysLog = new SysLog();
//获取请求url,ip,httpMethod
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String ip = request.getRemoteAddr();
String httpMethod = request.getMethod();
String url = request.getRequestURL().toString();
sysLog.setIp(ip);
sysLog.setHttpMethod(httpMethod);
sysLog.setUrl(url);
sysLog.setId(IdUtil.randomUUID());

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

sysLog.setExecTime(time);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
sysLog.setCreateDate(dateFormat.format(new Date()));
SysLogAnnotation annotation = method.getAnnotation(SysLogAnnotation.class);
if(annotation != null){
//注解上的描述
sysLog.setRemark(annotation.value());
}
//请求的 类名、方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setClassName(className);
sysLog.setMethodName(methodName);
//请求的参数
Object[] args = joinPoint.getArgs();
try{
List<String> list = new ArrayList<String>();
for (Object o : args) {
list.add(new Gson().toJson(o));
}
sysLog.setParams(list.toString());
}catch (Exception e){ }
sysLogService.saveSysLog(sysLog);
}

}

TestController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.seamoon.aop.controller;

import com.seamoon.aop.interfaceAnnotation.SysLogAnnotation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/test")
public class TestController {

@SysLogAnnotation("切面测试")
@GetMapping("/test")
public String test(@RequestParam("name") String name) {
System.out.println("sys-name:"+name);
return name;
}

@GetMapping("/v")
public String index(){
return "sueccs";
}

}

通过访问http://127.0.0.1:8080/test/test?name=123,查询数据库,得出数据。

致谢

感谢您到来,如对您有帮助,请给小编一点温暖~~

查看评论