真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Springboot中怎么調(diào)用代理對(duì)象內(nèi)嵌

本篇文章給大家分享的是有關(guān)Springboot中怎么調(diào)用代理對(duì)象內(nèi)嵌,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

十載的涼山州網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整涼山州建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“涼山州網(wǎng)站設(shè)計(jì)”,“涼山州網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

@Async和@Transactional共存
    @Component
    public class AsyncWithTransactional {
        
        @Async
        @Transactional
        public void test() {
            
        }
    }

這樣一段代碼會(huì)發(fā)生什么?熟悉的人都會(huì)感覺疑惑,都有效果么?誰先被代理增強(qiáng)?

自動(dòng)代理創(chuàng)建器AbstractAutoProxyCreator它實(shí)際也是個(gè)BeanPostProcessor,所以它們的執(zhí)行順序很重要~~~

  • 兩者都繼承自ProxyProcessorSupport所以都能創(chuàng)建代理,且實(shí)現(xiàn)了Ordered接口- - -- - ---AsyncAnnotationBeanPostProcessor默認(rèn)的order值為Ordered.LOWEST_PRECEDENCE。但可以通過@EnableAsync指定order屬性來改變此值。

  • AsyncAnnotationBeanPostProcessor在創(chuàng)建代理時(shí)有這樣一個(gè)邏輯:若已經(jīng)是Advised對(duì)象了,那就只需要把@Async的增強(qiáng)器添加進(jìn)去即可。若不是代理對(duì)象才會(huì)自己去創(chuàng)建

    public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) {
    		if (bean instanceof Advised) {
    			advised.addAdvisor(this.advisor);
    			return bean;
    		}
    		// 上面沒有return,這里會(huì)繼續(xù)判斷自己去創(chuàng)建代理~
    	}
    }
  • AbstractAutoProxyCreator默認(rèn)值也同上。但是在把自動(dòng)代理創(chuàng)建器添加進(jìn)容器的時(shí)候有這么一句代碼:beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); 自動(dòng)代理創(chuàng)建器這個(gè)處理器是最高優(yōu)先級(jí)

  • 由上可知因?yàn)闃?biāo)注有@Transactional,所以自動(dòng)代理會(huì)生效,因此它會(huì)先交給AbstractAutoProxyCreator把代理對(duì)象生成好了,再交給后面的處理器執(zhí)行 由于AbstractAutoProxyCreator先執(zhí)行,所以AsyncAnnotationBeanPostProcessor執(zhí)行的時(shí)候此時(shí)Bean已經(jīng)是代理對(duì)象了,此時(shí)它會(huì)沿用這個(gè)代理,只需要把切面添加進(jìn)去即可~

方法調(diào)用順序影響

想必大家都知道一點(diǎn)就是同類的方法調(diào)用只有入口方法被代理才會(huì)被增強(qiáng),這是由于源碼級(jí)別只處理入口方法調(diào)用,是你的話你也這樣設(shè)計(jì),不然方法棧那么深,你管得了那么多嗎?既然知道了這個(gè)原因,那么我們接下來在看一下后面的列子。

沿用代理對(duì)象
     java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
    	at org.springframework.aop.framework.AopContext.currentProxy(AopContext.java:69)
    	at com.fsx.dependency.B.funTemp(B.java:14)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:206)
    	at com.sun.proxy.$Proxy44.funTemp(Unknown Source)
    	...

這個(gè)異常在上述情況最容易出現(xiàn),然而解決的方法都是@EnableAspectJAutoProxy(exposeProxy = true)

咦,是不是我們可以從容器中獲取代理對(duì)象呢?沒有錯(cuò),從容器獲取代理對(duì)象也是一種沿用代理對(duì)象來調(diào)用方法鏈的手段,但是你會(huì)用么?依賴于代理的具體實(shí)現(xiàn)而書寫代碼,這樣移植性會(huì)非常差的。

揭秘@EnableAspectJAutoProxy(exposeProxy = true)

Spring內(nèi)建的類且都是代理類的處理類:CglibAopProxyJdkDynamicAopProxy兩者很類似,在處理這個(gè)邏輯上。所以此處只以JdkDynamicAopProxy作為代表進(jìn)行說明即可。

我們知道在執(zhí)行代理對(duì)象的目標(biāo)方法的時(shí)候,都會(huì)交給InvocationHandler處理,因此做事情的在invoke()方法里:

    final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    	...
    	@Override
    	@Nullable
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		...
    			if (this.advised.exposeProxy) {
    				// Make invocation available if necessary.
    				oldProxy = AopContext.setCurrentProxy(proxy);
    				setProxyContext = true;
    			}
    		...
    		finally {
    			if (setProxyContext) {
    				// Restore old proxy.
    				AopContext.setCurrentProxy(oldProxy);
    			}
    		}
    	}
    }

最終決定是否會(huì)調(diào)用set方法是由this.advised.exposeProxy這個(gè)值決定的,因此下面我們只需要關(guān)心ProxyConfig.exposeProxy這個(gè)屬性值什么時(shí)候被賦值為true的就可以了。

ProxyConfig.exposeProxy這個(gè)屬性的默認(rèn)值是false。其實(shí)最終調(diào)用設(shè)置值的是同名方法Advised.setExposeProxy()方法,而且是通過反射調(diào)用的,再次強(qiáng)調(diào) 看清楚后置處理器,@EnableAspectJAutoProxy(exposeProxy = true)作用的范圍在AbstractAutoProxyCreator創(chuàng)建器,異步注解和緩存注解等就不行了,怎么解決后面在分析。

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
        AspectJAutoProxyRegistrar() {
        }
    
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
            AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
              //處理是否設(shè)置了該屬性
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
    
        }
    }

看一下是如何設(shè)置屬性值的,我們后面可以采用這樣的方式來設(shè)置

    public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
    		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
    			BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
    			definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
    		}
    	}

什么時(shí)候使用的呢? Springboot中怎么調(diào)用代理對(duì)象內(nèi)嵌

AopContext.setCurrentProxy(@Nullable Object proxy)CglibAopProxyJdkDynamicAopProxy代理都有使用。

案例分析
    @Component
    public class AsyncWithTransactional {
      //入口方法
        @Transactional
        public void transactional() {
          //不使用代理對(duì)象調(diào)用的話,后續(xù)方法不會(huì)被增強(qiáng)
            AsyncWithTransactional asyncWithTransactional = AsyncWithTransactional.class.cast(AopContext.currentProxy());
            asyncWithTransactional.async();
            
        }
        @Async
        public void async() {
    
        }
    }

這樣都完全ok的,但是如果換一下呢就會(huì)跑出異常。

子線程引起的問題
        @Transactional//@Transactional有此注解和沒有毫無關(guān)系
        @Async
        public void transactional() {
            AsyncWithTransactional asyncWithTransactional = AsyncWithTransactional.class.cast(AopContext.currentProxy());
            asyncWithTransactional.async();
        }
    public void async() {
    
        }

根本原因就是關(guān)鍵節(jié)點(diǎn)的執(zhí)行時(shí)機(jī)問題。在執(zhí)行代理對(duì)象transactional方法的時(shí)候,先執(zhí)行綁定動(dòng)作AopContext.setCurrentProxy(proxy);然后目標(biāo)方法執(zhí)行(包括增強(qiáng)器的執(zhí)行)invocation.proceed()。其實(shí)在執(zhí)行綁定的還是在主線程里而并非是新的異步線程,所以在你在方法體內(nèi)(已經(jīng)屬于異步線程了)執(zhí)行AopContext.currentProxy()那可不就報(bào)錯(cuò)了嘛~

所以入口方法用了類似@Async的效果注解都會(huì)導(dǎo)致代理對(duì)象綁定不對(duì),繼而導(dǎo)致調(diào)用錯(cuò)誤。

如何解決類似子線程引起的問題呢?
    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
            beanDefinition.getPropertyValues().add("exposeProxy", true);
        }
    }

這樣解決了@Async的綁定問題,@EnableCaching也可以基于這樣的思想來解決,以上就是我的簡單例子,但是配合我的文字說明,相信大家可以舉一反三,隨意玩弄它們之間的調(diào)用關(guān)系。 其實(shí)如果Spring做出源碼改變會(huì)更好的解決這個(gè)問題

  • @Async的代理也交給自動(dòng)代理創(chuàng)建器來完成(Spring做出源碼改變)

  • @EnableAsync增加exposeProxy屬性,默認(rèn)值給false即可(Spring做出源碼改變)

總結(jié):
  • 不要在異步線程里使用AopContext.currentProxy()

  • AopContext.currentProxy()不能使用在非代理對(duì)象所在方法體內(nèi)

以上就是Springboot中怎么調(diào)用代理對(duì)象內(nèi)嵌,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


文章題目:Springboot中怎么調(diào)用代理對(duì)象內(nèi)嵌
文章起源:http://weahome.cn/article/ihhgop.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部