相同类中方法间调用时日志Aop失效处理
本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有很多文章描述了,不过大多是从事务角度分享的,本篇打算从日志aop方面分享(当然都是aop,失效和处理方案都是一样),以下都是基于springboot演示;
快速定义个日志Appender
快速定义个拦截器和日志注解(aop)
模拟相同类中方法间调用时aop失效
Aop失效处理方案(就两种足够了)
快速定义个日志Appender
日志我还是喜欢log4j,大部分朋友也同样吧,这里lombok与log4j结合来完成我们的日志,如下maven包(最新mvn还是建议去官网找):
复制代码
1
2 org.projectlombok
3 lombok
4
6
7 org.slf4j
8 slf4j-api
9 2.0.0-alpha0
10
11
12 org.slf4j
13 slf4j-log4j12
14 2.0.0-alpha0
15
复制代码
先继承log4j的AppenderSkeleton重写下append方法,简单记录下就行,如下:
复制代码
1 public class MyLogAppend extends AppenderSkeleton {
2 private String author;
3
4 public void setAuthor(String author) {
5 this.author = author;
6 }
7
8 @Override
9 protected void append(LoggingEvent loggingEvent) {
10 System.out.println(
11 JsonUtil.formatMsg("date -- {},level -- {},message -- {}",
12 LocalDate.now(),
13 loggingEvent.getLevel(),
14 loggingEvent.getMessage()));
15 }
16
17 @Override
18 public void activateOptions() {
19 super.activateOptions();
20 System.out.println("author:" + this.author);
21 }
22
23 @Override
24 public void close() {
25 this.closed = true;
26 }
27
28 @Override
29 public boolean requiresLayout() {
30 return false;
31 }
32 }
复制代码
然后项目根目录增加log4j.properties配置文件,配置内容定义info级别,就此完成了log4j自定义记录日志了:
复制代码
1 log4j.rootLogger=info,MyLogAppend
2 log4j.appender.MyLogAppend=com.sm.component.log.MyLogAppend
3 log4j.appender.MyLogAppend.author=shenniu003
复制代码
快速定义个拦截器和日志注解(aop)
通常同类中不同方法调用是常事,可以直接用this.xx();有时有这样需求,需要各个调用方法时候的参数记录下来,因此我们需要个拦截器,再增加个自定义注解方便使用:
复制代码
1 @Aspect
2 @Component
3 @Slf4j
4 public class MyLogInterceptor {
5
6 private final String pointcut = "@annotation(com.sm.component.ServiceLog)";
7
8 @Pointcut(pointcut)
9 public void log() {
10 }
11
12 @Before(value = "log()")
13 void before(JoinPoint joinPoint) {
14 Signature signature = joinPoint.getSignature();
15 log.info(
16 JsonUtil.formatMsg("method:{},params:{}",
17 signature.toLongString(),
18 joinPoint.getArgs()));
19 }
20 }
复制代码
复制代码
1 @Documented
2 @Target({ElementType.METHOD})
3 @Retention(RetentionPolicy.RUNTIME)
4 public @interface ServiceLog {
5 }
复制代码
拦截器拦截带有@ServiceLog注解的方法,然后记录请求参数和方法名;
模拟相同类中方法间调用时aop失效
利用上面完成的日志注解,这里在OrderService类中用getOrderDetail方法去调用getOrderLog方法,他两都标记日志注解便于记录参数日志;同时getOrderDetail方法也调用另外一个UserService类中的getNickName方法,便于比较:
复制代码
1 @Service
2 public class OrderService {
3
4 @Autowired
5 UserService userService;
6
7 @ServiceLog
8 public String getOrderDetail(String orderNum) {
9 String des = "订单号【" + orderNum + "】月饼一盒";
11 userService.getNickName(orderNum);
13 this.getOrderLog(orderNum + "11111");
15 return des;
16 }
17
18 @ServiceLog
19 public List getOrderLog(String orderNum) {
20 List logs = new ArrayList<>();
21 IntStream.range(0, 5).forEach(b -> {
22 logs.add("用户" + b + "购买成功");
23 });
24 return logs;
25 }
26 }
复制代码
复制代码
1 @Service
2 public class UserService {
3 @ServiceLog
4 public String getNickName(String userId) {
5 return "神牛" + userId;
6 }
7 }
复制代码
方法调用重点截图:
然后运行程序,接口触发调用getOrderDetail方法,以下拦截器中记录的日志信息:
能够看出拦截器只记录到了getOrderDetail和getNickName方法的日志,因此可以肯定getOrderLog根本没有走拦截器,尽管在方法上加了日志@ServiceLog注解也没用。
Aop失效处理方案(就两种足够了)
就上面相同类中方法间调用拦截器(aop)没起作用,我们有如下常用两种方式处理方案;
用@Autowired或Resource引入自身依赖
开启暴露代理类,AopContext.currentProxy()方式获取代理类
第一种:主要使用注解方法引入自身代理依赖,不要使用构造的方式会有循环依赖问题,以下使用方式:
第二种:通过暴露代理类方式,实际原理是把代理类添加到当前请求的ThreadLocal里面,然后在使用时从ThreadLocal中获取代理类,再调用对应的方法,开启方式需要:
1 @EnableAspectJAutoProxy(exposeProxy = true)
然后方法中如下使用即可:
最后来看下使用这两种方式正常走拦截器效果:
不管是日志拦截器或事务,他们都是aop的方式,底层原理走的代理方式,只有使用代理类才会正常执行拦截器,而this.xxx()使用的是自身实例对象,因此会出现上面失效的情况。
git地址: https://github.com/shenniubuxing3 云栖社区博客:https://yq.aliyun.com/users/xjexlr3no5xj4https://www.cnblogs.com/wangrudong003/p/11508107.html