引入Springboot AOP

统一日志初步处理。

添加maven依赖,版本与starter-parent一致

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.5.10.RELEASE</version>
</dependency>

注解简介

详情查阅官网。
@Pointcut: 定义切点

execution: 匹配方法执行的连接点,语法为:
execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))

@Before: 前置方法

@AfterReturning: 后置返回方法

@AfterThrowing: 异常抛出时调用

@After: 同final, 无论异常抛出还是正常退出,均调用

@Around: 环绕通知,即同@Before与@After结合

@Order: 当有多个aspect时使用,定义优先级,值越小,执行顺序越高

方法调用顺序:
sequence

实例

请求日志

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
import com.kodgames.bus.component.reqres.Result;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.Arrays;
/**
* 请求日志
*/
@Aspect
@Component
public class WebLogAspect {
private static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
private ThreadLocal<Long> beginTime = new ThreadLocal<>();
@Pointcut("execution(public * com.kodgames.bus.controller.*.*(..))") //监听所有controller的所有公共方法
public void controllerPoint() {
}
@Before("controllerPoint()")
public void doBefore(JoinPoint joinPoint) {
//请求到达时间
beginTime.set(System.currentTimeMillis());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("Request Url: " + request.getRequestURL().toString());
logger.info("Http Method: " + request.getMethod());
logger.info("Class Method: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("Args: " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "controllerPoint()")
public void doAfterReturning(Object ret) throws Throwable {
logger.info("Response is: " + ret);
if (ret instanceof Result) {
Result result = (Result) ret;
logger.info("Response code is " + result.getCode());
logger.info("Response message is " + result.getMsg());
}
logger.info("method execute takes: {} ms", System.currentTimeMillis() - beginTime.get());
}
}

注:Result类数据结构如下

1
2
3
4
5
6
public class Result<T> {
private int code;
private String msg;
private String des;
private T data;
}

测试结果如下:
aop-test

数据库层日志

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
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 数据库层日志
*/
@Aspect
@Component
public class DaoLogAspect {
private static Logger logger = LoggerFactory.getLogger(DaoLogAspect.class);
private ThreadLocal<Long> beginTime = new ThreadLocal<>();
@Pointcut("execution(public * com.kodgames.bus.dao.*.*(..))")
public void daoPoint() {
}
@Before("daoPoint()")
public void doBefore(JoinPoint joinPoint) {
//请求到达时间
beginTime.set(System.currentTimeMillis());
logger.info("Dao Class Method: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("Dao Args: " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "daoPoint()")
public void doAfterReturning(Object ret) throws Throwable {
logger.info("Dao Response is: " + ret);
logger.info("Dao Method execute takes: {} ms", System.currentTimeMillis() - beginTime.get());
}
}

其他使用测试

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
/**
* 与catch完全处理异常不同,AfterThrowing不能完全处理该异常,异常依然会传播到上一级调用者直至JVM
*/
@AfterThrowing(throwing="tr", pointcut = "controllerPoint()")
public void throwsTr(Throwable tr){
logger.error("check throwable: " + tr);
}
//后置最终通知, final增强,不管是抛出异常或者正常退出都会执行
@After("controllerPoint()")
public void after(){
logger.info("method execute finally.....");
}
//环绕通知
@Around("controllerPoint()")
public Object around(ProceedingJoinPoint pjp) {
logger.info("method around start.....");
try {
Object o = pjp.proceed();
logger.info("method around proceed,result is: " + o);
return o;
} catch (Throwable e) {
logger.error("@Around get throwable: " + e);
return null;
}
}