@Async、@Transational、AOP 方法內(nèi)部調(diào)用失效的解決方法

遇到過 方法A 內(nèi)調(diào)用Aop修飾的方法B 失效、方法A 內(nèi)調(diào)用@Async修飾的方法C 失效,百度谷歌都沒看到一個好的解決,這里分享一個我的解決方案。

為什么失效

這個百度上很多解答,分析的也很好,其實(shí)就是Spring代理機(jī)制造成的。

簡單的說,就是通過spring容器獲取的類對象,很多情況下并不是原類,而是被spring修飾過了的代理類。

例如你執(zhí)行 A類對象的方法A.invoke(),而spring對A類做了修飾:

proxyAbean.invoke():

before

invoke(bean,A)

after

實(shí)際你運(yùn)行的是spring修飾過的代理類proxyAbean.invoke()方法。

這樣就會造成一個問題,如果你在invoke()中調(diào)用A類的其余方法invoke2(),此時invoke2()是直接調(diào)用的原類的 A.invoke2(),而不是代理類proxyAbean.invoke2(),spring對方法做的修飾增強(qiáng)(@Async、@TransationalAOP)全部不會實(shí)現(xiàn)。

如何解決

百度上都講,將調(diào)用方法放入另外一個類就行了,這種方法其實(shí)走了彎路。

既然是因?yàn)闆]有調(diào)用到代理類的方法造成的,那我們重新獲取一遍代理類,調(diào)用方法不就行了嗎?

public class A{
    public void aMethod() {
        System.out.println("method a start");

//      bMethod();   //直接調(diào)用方法b,@Async不會生效

        A a = context.getBean(A.class); //從spring容器中重新獲取A的代理對象,再調(diào)用b方法注解即生效
        a.bMethod();
        System.out.println("method a end");
    }

    @Async
    public void bMethod() {
        Thread.sleep(1000);
        System.out.println("我是異步方法!");
    }
}

代理類的獲取很簡單,通過spring容器context.getBean()即可。一般的spring項(xiàng)目都會全局保持一個context:

/**
 * 持有spring上下文的工具類,一個系統(tǒng)只能有一個SpringContextHolder。
 * <p>該工具類主要用于: 通過spring上下文獲取bean</p>
 */
public class SpringContextHolder implements ApplicationContextAware,DisposableBean{
    protected static final Log log = LogFactory.getLog(SpringContextHolder.class);
    
    private static ApplicationContext applicationContext;
    
    /**
     * 將spring容器上下文:applicationContext注入
     */
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        if(applicationContext != null)  throw new IllegalStateException("ApplicationContextHolder already holded 'applicationContext'.");
        log.info("Injecting 'applicationContext' to " + SpringContextHolder.class.getSimpleName() + ", applicationContext=" + context);
        applicationContext = context;
    }
    
    private static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    /**
     * 本類SpringContextHolder被銷毀時,將spring上下文置空
     */
    @Override
    public void destroy() throws Exception {
        applicationContext = null;
    }
    
    /**
     * 根據(jù)class獲取spring容器中的bean
     */
    public static <T> T getBean(Class<T> c){
        return applicationContext.getBean(c);
    }
    
    /**
     * 根據(jù)class名稱獲取spring中的bean
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String beanName){
        return (T)getApplicationContext().getBean(beanName);
    }

}

補(bǔ)充:spring官方文檔上的解決

spring的官方文檔上針對AOP方法內(nèi)部調(diào)用還提供了一種解決方案:

  1. 在配置文件加入如下配置,使代理類暴露給線程。注意該配置要spring3.0以上:
<aop:aspectj-autoproxy expose-proxy="true"/>
  1. 手動調(diào)用代理類運(yùn)行方法B:
if (null != AopContext.currentProxy()) {
            rs=((Bean)AopContext.currentProxy()).method(...);
        } else {
            rs=method(...);
        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,881評論 18 139
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,360評論 11 349
  • 一、C 語言的發(fā)展和過程 高級語言:a+b 匯編語言:ADD AX,BX 機(jī)器語言:0000 0001 1101 ...
    我可能是個假開發(fā)閱讀 707評論 0 2
  • 遠(yuǎn)離塵世 ~圖文美麗 累了 也倦了 約一下閨密 遠(yuǎn)離吵雜的城市 到?jīng)]有污染的哈巴河度假村 釋放堆積的壓力 悠閑的劃...
    雪花琵琶閱讀 384評論 0 17
  • 某女星公開出軌,寧愿冒天下之大不韙,依然選擇"自由奔放的性"!對這種毫無約束,不對家庭負(fù)責(zé)的人,真心嗤之以鼻! 地...
    Lucy623閱讀 111評論 1 1