本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有不少文章描述了,不过大可能是从事务角度分享的,本篇打算从日志aop方面分享(固然都是aop,失效和处理方案都是同样),如下都是基于springboot演示;spring
日志我仍是喜欢log4j,大部分朋友也一样吧,这里lombok与log4j结合来完成咱们的日志,以下maven包(最新mvn仍是建议去官网找):api
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.0-alpha0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.0-alpha0</version> </dependency>
先继承log4j的AppenderSkeleton重写下append方法,简单记录下就行,以下:springboot
public class MyLogAppend extends AppenderSkeleton { private String author; public void setAuthor(String author) { this.author = author; } @Override protected void append(LoggingEvent loggingEvent) { System.out.println( JsonUtil.formatMsg("date -- {},level -- {},message -- {}", LocalDate.now(), loggingEvent.getLevel(), loggingEvent.getMessage())); } @Override public void activateOptions() { super.activateOptions(); System.out.println("author:" + this.author); } @Override public void close() { this.closed = true; } @Override public boolean requiresLayout() { return false; } }
而后项目根目录增长log4j.properties配置文件,配置内容定义info级别,就此完成了log4j自定义记录日志了:app
log4j.rootLogger=info,MyLogAppend log4j.appender.MyLogAppend=com.sm.component.log.MyLogAppend log4j.appender.MyLogAppend.author=shenniu003
一般同类中不一样方法调用是常事,能够直接用this.xx();有时有这样需求,须要各个调用方法时候的参数记录下来,所以咱们须要个拦截器,再增长个自定义注解方便使用:maven
@Aspect @Component @Slf4j public class MyLogInterceptor { private final String pointcut = "@annotation(com.sm.component.ServiceLog)"; @Pointcut(pointcut) public void log() { } @Before(value = "log()") void before(JoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); log.info( JsonUtil.formatMsg("method:{},params:{}", signature.toLongString(), joinPoint.getArgs())); } }
@Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ServiceLog { }
拦截器拦截带有@ServiceLog注解的方法,而后记录请求参数和方法名ide
利用上面完成的日志注解,这里在OrderService类中用getOrderDetail方法去调用getOrderLog方法,他两都标记日志注解便于记录参很多天志;同时getOrderDetail方法也调用另一个UserService类中的getNickName方法,便于比较:ui
@Service public class OrderService { @Autowired UserService userService; @ServiceLog public String getOrderDetail(String orderNum) { String des = "订单号【" + orderNum + "】月饼一盒"; userService.getNickName(orderNum); this.getOrderLog(orderNum + "11111"); return des; } @ServiceLog public List<String> getOrderLog(String orderNum) { List<String> logs = new ArrayList<>(); IntStream.range(0, 5).forEach(b -> { logs.add("用户" + b + "购买成功"); }); return logs; } }
@Service public class UserService { @ServiceLog public String getNickName(String userId) { return "神牛" + userId; } }
方法调用重点截图:
而后运行程序,接口触发调用getOrderDetail方法,如下拦截器中记录的日志信息:
可以看出拦截器只记录到了getOrderDetail和getNickName方法的日志,所以能够确定getOrderLog根本没有走拦截器,尽管在方法上加了日志@ServiceLog注解也没用。this
就上面相同类中方法间调用拦截器(aop)没起做用,咱们有以下经常使用两种方式处理方案;spa
第一种:主要使用注解方法引入自身代理依赖,不要使用构造的方式会有循环依赖问题,如下使用方式:
第二种:经过暴露代理类方式,实际原理是把代理类添加到当前请求的ThreadLocal里面,而后在使用时从ThreadLocal中获取代理类,再调用对应的方法,开启方式须要:代理
@EnableAspectJAutoProxy(exposeProxy = true)
而后方法中以下使用便可:
最后来看下使用这两种方式正常走拦截器效果:
无论是日志拦截器或事务,他们都是aop的方式,底层原理走的代理方式,只有使用代理类才会正常执行拦截器,而this.xxx()使用的是自身实例对象,所以会出现上面失效的状况。
本文为云栖社区原创内容,未经容许不得转载。