詳解spring中aop不生效的幾種解決辦法
先看下這個問題的背景:假設(shè)有一個spring應(yīng)用,開發(fā)人員希望自定義一個注解@Log,可以加到指定的方法上,實現(xiàn)自動記錄日志(入?yún)?、出參、響?yīng)耗時這些)
package com.cnblogs.yjmyzz.springbootdemo.aspect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Log { }
然后再寫一個Aspect來解析這個注解,對打了Log注解的方法進行增強處理
package com.cnblogs.yjmyzz.springbootdemo.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Component @Aspect public class LogAspect { @Pointcut("execution (* com.cnblogs.yjmyzz.springbootdemo.service..*.*(..))") public void logPointcut() { } @Around("logPointcut()") public void around(JoinPoint point) { String methodName = point.getSignature().getName(); Object[] args = point.getArgs(); Class<?>[] argTypes = new Class[point.getArgs().length]; for (int i = 0; i < args.length; i++) { argTypes[i] = args[i].getClass(); } Method method = null; try { method = point.getTarget().getClass().getMethod(methodName, argTypes); } catch (Exception e) { e.printStackTrace(); } //獲取方法上的注解 Log log = method.getAnnotation(Log.class); if (log != null) { //演示方法執(zhí)行前,記錄一行日志 System.out.println("before:" + methodName); } try { //執(zhí)行方法 ((ProceedingJoinPoint) point).proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } finally { if (log != null) { //演示方法執(zhí)行后,記錄一行日志 System.out.println("after:" + methodName); } } } }
寫一個測試Service類:
package com.cnblogs.yjmyzz.springbootdemo.service; import com.cnblogs.yjmyzz.springbootdemo.aspect.Log; import org.springframework.stereotype.Component; @Component public class HelloService { @Log public void sayHi(String msg) { System.out.println("\tsayHi:" + msg); } public void anotherSayHi(String msg) { this.sayHi(msg); } }
最后來跑一把:
package com.cnblogs.yjmyzz.springbootdemo; import com.cnblogs.yjmyzz.springbootdemo.service.HelloService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** * @author 菩提樹下的楊過 */ @ComponentScan("com.cnblogs.yjmyzz") @Configuration @EnableAspectJAutoProxy public class SampleApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleApplication.class); HelloService helloService = context.getBean(HelloService.class); helloService.sayHi("hi-1"); System.out.println("\n"); helloService.anotherSayHi("hi-2"); } }
輸出如下:
顯然HelloService中的anotherSayHi方法,并未被aop增強。 原因其實很簡單,了解AOP原理的同學想必都知道,AOP的實現(xiàn)有二類,如果是基于接口的,會采用動態(tài)代理,生成一個代理類,如果是基于類的,會采用CGLib生成子類,然后在子類中擴展父類中的方法。
本文中HelloService并不是一個接口,所以從上圖的斷點中可以看出,當Spring運行時,HelloService被增加為...EnhancerBySpringCGLib...。但是當調(diào)用到anotherSayHi時
方法的調(diào)用方,其實是原始的HelloSerfvice實例,即:是未經(jīng)過Spring AOP增強的對象實例。所以解決問題的思路就有了,想辦法用增強后的HelloService實例來調(diào)用!
方法一:用Autowired 注入自身的實例
這個方法,第一眼看上去感覺有些怪,自己注入自己,感覺有點象遞歸/死循環(huán)的搞法,但確實可以work,Spring在解決循環(huán)依賴上有自己的處理方式,避免了死循環(huán)。
方法二:從Spring上下文獲取增強后的實例引用
原理與方法一其實類似,不多解釋。
方法三: 利用AopContext
不過這個方法要注意的是,主類入口上,必須加上exporseProxy=true,參考下圖:
最后來驗證下這3種方法是否生效:
從運行結(jié)果上看,3種方法都可以解決這個問題?!?/p>
到此這篇關(guān)于詳解spring中aop不生效的幾種解決辦法的文章就介紹到這了,更多相關(guān)spring中aop不生效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
作者:菩提樹下的楊過
出處:http://yjmyzz.cnblogs.com
相關(guān)文章
Spring將MultipartFile轉(zhuǎn)存到本地磁盤的三種方式
在Java中處理文件向來是一種不是很方便的操作,然后隨著Spring框架的崛起,使用Spring框架中的MultipartFile來處理文件也是件很方便的事了,今天就給大家介紹Spring將MultipartFile轉(zhuǎn)存到本地磁盤的方式,需要的朋友可以參考下2024-10-10詳解springboot 使用c3p0數(shù)據(jù)庫連接池的方法
本篇文章主要介紹了springboot 使用c3p0數(shù)據(jù)庫連接池的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09SpringBoot+TestNG單元測試的實現(xiàn)
本文主要介紹了SpringBoot+TestNG單元測試的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-07-07idea一招搞定同步所有配置(導(dǎo)入或?qū)С鏊信渲?
使用intellij idea很長一段時間,軟件相關(guān)的配置也都按照自己習慣的設(shè)置好,如果需要重裝軟件,還得需要重新設(shè)置,本文就詳細的介紹了idea 同步所有配置,感興趣的可以了解一下2021-07-07Java并發(fā)編程之關(guān)鍵字volatile知識總結(jié)
今天帶大家學習java的相關(guān)知識,文章圍繞著Java關(guān)鍵字volatile展開,文中有非常詳細的知識總結(jié),需要的朋友可以參考下2021-06-06