• 相同类中方法间调用时日志Aop失效处理


    本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有很多文章描述了,不过大多是从事务角度分享的,本篇打算从日志aop方面分享(当然都是aop,失效和处理方案都是一样),以下都是基于springboot演示;

    • 快速定义个日志Appender
    • 快速定义个拦截器和日志注解(aop)
    • 模拟相同类中方法间调用时aop失效
    • Aop失效处理方案(就两种足够了)

    快速定义个日志Appender

    日志我还是喜欢log4j,大部分朋友也同样吧,这里lombok与log4j结合来完成我们的日志,如下maven包(最新mvn还是建议去官网找):

     1         <dependency>
     2             <groupId>org.projectlombok</groupId>
     3             <artifactId>lombok</artifactId>
     4         </dependency>
     6         <dependency>
     7             <groupId>org.slf4j</groupId>
     8             <artifactId>slf4j-api</artifactId>
     9             <version>2.0.0-alpha0</version>
    10         </dependency>
    11         <dependency>
    12             <groupId>org.slf4j</groupId>
    13             <artifactId>slf4j-log4j12</artifactId>
    14             <version>2.0.0-alpha0</version>
    15         </dependency>

    先继承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<String> getOrderLog(String orderNum) {
    20         List<String> 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)没起作用,我们有如下常用两种方式处理方案;

    1. 用@Autowired或Resource引入自身依赖
    2. 开启暴露代理类,AopContext.currentProxy()方式获取代理类

    第一种:主要使用注解方法引入自身代理依赖,不要使用构造的方式会有循环依赖问题,以下使用方式:

    第二种:通过暴露代理类方式,实际原理是把代理类添加到当前请求的ThreadLocal里面,然后在使用时从ThreadLocal中获取代理类,再调用对应的方法,开启方式需要:

    1 @EnableAspectJAutoProxy(exposeProxy = true)

    然后方法中如下使用即可:

    最后来看下使用这两种方式正常走拦截器效果:

    不管是日志拦截器或事务,他们都是aop的方式,底层原理走的代理方式,只有使用代理类才会正常执行拦截器,而this.xxx()使用的是自身实例对象,因此会出现上面失效的情况。

  • 相关阅读:
    英文字典。怎样设计数据结构
    最近看的几部电影电视剧
    pylucene 中文
    提高浏览体验的五十个最佳FireFox扩展插件
    结构和细节
    <传> 《程序猿装B指南》,程序员童鞋们请认真学习
    c++ builder TTreeView customSort 实现 自定义排序
    《转》c++ 字符串系列:字符编码进阶(下)
    庆祝我又读完一本书
    c++ 回调函数深究
  • 原文地址:https://www.cnblogs.com/wangrudong003/p/11508107.html
Copyright © 2020-2023  润新知