zoukankan      html  css  js  c++  java
  • 相同类中方法间调用时日志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()使用的是自身实例对象,因此会出现上面失效的情况。

  • 相关阅读:
    不常用的cmd命令
    js获取宽度
    Marshaling Data with Platform Invoke 概览
    Calling a DLL Function 之三 How to: Implement Callback Functions
    Marshaling Data with Platform Invoke 之四 Marshaling Arrays of Types
    Marshaling Data with Platform Invoke 之一 Platform Invoke Data Types
    Marshaling Data with Platform Invoke 之三 Marshaling Classes, Structures, and Unions(用时查阅)
    Calling a DLL Function 之二 Callback Functions
    WCF 引论
    Marshaling Data with Platform Invoke 之二 Marshaling Strings (用时查阅)
  • 原文地址:https://www.cnblogs.com/wangrudong003/p/11508107.html
Copyright © 2011-2022 走看看