本篇分享的內容是在相同類中方法間調用時Aop失效處理方案,該問題我看有不少文章描述了,不過大可能是從事務角度分享的,本篇打算從日誌aop方面分享(固然都是aop,失效和處理方案都是同樣),如下都是基於springboot演示;spring
日誌我仍是喜歡log4j,大部分朋友也一樣吧,這裏lombok與log4j結合來完成咱們的日誌,以下maven包(最新mvn仍是建議去官網找):api
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方法,簡單記錄下就行,以下:springboot
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自定義記錄日誌了:app
1 log4j.rootLogger=info,MyLogAppend 2 log4j.appender.MyLogAppend=com.sm.component.log.MyLogAppend 3 log4j.appender.MyLogAppend.author=shenniu003
一般同類中不一樣方法調用是常事,能夠直接用this.xx();有時有這樣需求,須要各個調用方法時候的參數記錄下來,所以咱們須要個攔截器,再增長個自定義註解方便使用:maven
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註解的方法,而後記錄請求參數和方法名;ide
利用上面完成的日誌註解,這裏在OrderService類中用getOrderDetail方法去調用getOrderLog方法,他兩都標記日誌註解便於記錄參很多天志;同時getOrderDetail方法也調用另一個UserService類中的getNickName方法,便於比較:ui
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 }
方法調用重點截圖:this
而後運行程序,接口觸發調用getOrderDetail方法,如下攔截器中記錄的日誌信息:spa
可以看出攔截器只記錄到了getOrderDetail和getNickName方法的日誌,所以能夠確定getOrderLog根本沒有走攔截器,儘管在方法上加了日誌@ServiceLog註解也沒用。代理
就上面相同類中方法間調用攔截器(aop)沒起做用,咱們有以下經常使用兩種方式處理方案;
第一種:主要使用註解方法引入自身代理依賴,不要使用構造的方式會有循環依賴問題,如下使用方式:
第二種:經過暴露代理類方式,實際原理是把代理類添加到當前請求的ThreadLocal裏面,而後在使用時從ThreadLocal中獲取代理類,再調用對應的方法,開啓方式須要:
1 @EnableAspectJAutoProxy(exposeProxy = true)
而後方法中以下使用便可:
最後來看下使用這兩種方式正常走攔截器效果:
無論是日誌攔截器或事務,他們都是aop的方式,底層原理走的代理方式,只有使用代理類纔會正常執行攔截器,而this.xxx()使用的是自身實例對象,所以會出現上面失效的狀況。