力學分析的整體法,Spring 源碼分析(三) —— AOP(二)Spring AOP 整體架構

 2023-11-19 阅读 46 评论 0

摘要:2019獨角獸企業重金招聘Python工程師標準>>> Spring AOP 架構 ????????先是生成代理對象,然后是攔截器的作用,最后是編織的具體實現。這是AOP實現的三個步驟,當然Spring AOP也是一樣。 ????????而從Spring AOP整體架構上看,其核心都是建

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

Spring AOP 架構

????????先是生成代理對象,然后是攔截器的作用,最后是編織的具體實現。這是AOP實現的三個步驟,當然Spring AOP也是一樣。

????????而從Spring AOP整體架構上看其核心都是建立在代理上的。當我們建立增強實例時,我們必須先使用 ProxyFactory 類加入我們需要織入該類的所有增強,然后為該類創建代理。一般而言,AOP實現代理的方法有三種,而?Spring 內部用到了其中的兩種方法:動態代理和靜態代理,而動態代理又分為JDK動態代理和CGLIB代理。而前面提到的 ProxyFactory 類控制著 Spring AOP 中的織入和創建代理的過程。在真正創建代理之前,我們必須指定被增強對象或者目標對象。我們是通過 setTarget() 方法來完成這個步驟的。而 ProxyFactory 內部將生成代理的過程全部轉給 DefaultAopProxyFactory 對象來完成,然后根據設置轉給其他的類來完成。下面是 Spring AOP 生成代理的原理圖:

231946_FGbT_2528735.png

????????而特別需要注意的是,在 Spring AOP 中,一個 Advisor(通知者,也翻作 通知器或者增強器)就是一個切面,他的主要作用是整合切面增強設計(Advice)和切入點設計(Pointcut)Advisor有兩個子接口:IntroductionAdvisor 和 PointcutAdvisor,基本所有切入點控制的?Advisor 都是由?PointcutAdvisor 實現的。而下面的是Spring AOP的整體架構圖

041131_h0UQ_2528735.jpg

????????乍一看,非常的復雜,但如果結合上面的Spring AOP生成代理的原理圖一起看,也就那么回事,只是豐富的許多屬性了,我們會慢慢介紹的。


Spring AOP 使用范例

????????項目結構

????????分析 Spring AOP 源碼的話,直接從最簡單業務代碼入手,一步步的對源碼進行解析。希望借此能看清楚 Spring AOP 縱向和橫向代碼之間的聯系,而且思維也不會太跳躍。下面就是 Spring AOP 測試程序 Test? 項目的結構圖:

225844_I4vr_2528735.png

????????注:Spring 源碼版本是 Spring-4.1.6,我的源碼分析也是基于這一版本的,JDK 版本是 1.8,aspect 是用的 aspectjrt-1.7.4 和 aspectjweaver-1.7.4。

力學分析的整體法。

????????創建用于攔截的Bean

????????在實際項目中,Bean應該可以算是核心邏輯了,其中的 printTest 方法中可能會封裝整個的核心業務邏輯,如此重要的地方我們想在printTest方法前后加入日志來進行跟蹤或者調試也是很自然地想法,但是如果直接修改源碼是不符合面向對象的設計原則的,它并不符合面向對象的設計方式,而且隨意的改動原有的代碼也會造成一定的風險,這里就體現 AOP 的優越性了。

package?test;/***?測試Bean**?@Auther?kay*?@Date???2016-03-08*/
public?class?TestBean?{private?String?testStr?=?"testStr";public?String?getTestStr(){return?testStr;}public?void?setTestStr(String?testStr){this.testStr?=?testStr;}public?void?printTest(){System.out.println("test?Bean");}
}


????????創建Advisor

????????Spring AOP 實現的核心,這里采用了@AspectJ注釋對POJO進行標注,使AOP的工作大大簡化。

package?test;import?org.aspectj.lang.ProceedingJoinPoint;
import?org.aspectj.lang.annotation.*;/***?Aspect切面**?@Auther?kay*?@Date???2016-03-08*/
@Aspect
public?class?AspectTest?{/***?配置切入點,主要為方便同類中其他方法使用此處配置的切入點*/private?final?String?POINT_CUT?=?"execution(*?test.TestBean.*(..))";/***?配置前置通知,使用在方法aspect()上注冊的切入點*?同時接受JoinPoint切入點對象,可以沒有該參數*/@Before(POINT_CUT)public?void?beforeTest(){System.out.println("before?Test");}/***?配置后置通知,使用在方法aspect()上注冊的切入點*/@After(POINT_CUT)public?void?afterTest(){System.out.println("after?Test");}/***?配置環繞通知,使用在方法aspect()上注冊的切入點**?@param?point?JoinPoint的支持接口*?@return?Object*/@Around(POINT_CUT)public?Object?arountTest(ProceedingJoinPoint?point){System.out.println("before1");Object?object?=?null;try{object?=?point.proceed();}catch?(Throwable?e){e.printStackTrace();}System.out.println("after1");return?object;}
}


????????配置文件

Spring Boot,????????XML是Spring的基礎,盡管Spring一再簡化配置,并且大有使用注解取代XML配置之勢,但無論如何XML還是Spring的基石。

<?xml?version="1.0"?encoding="UTF-8"?>
<beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="?http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.1.xsd"><!--?激活自動代理功能?--><aop:aspectj-autoproxy?/><!--?業務邏輯切面配置?--><bean?id="test"?class?=?"test.TestBean"?/><bean?class="test.AspectTest"?/></beans>


????????測試

????????測試程序,驗證效果。

package?test;import?org.springframework.context.ApplicationContext;
import?org.springframework.context.support.ClassPathXmlApplicationContext;/***?測試程序**?@Auther?kay*?@Date???2016-03-08*/
public?class?Test?{public?static?void?main(String[]?arge){ApplicationContext?context?=?new?ClassPathXmlApplicationContext("spring-config.xml");TestBean?bean?=?context.getBean("test",?TestBean.class);bean.printTest();}
}


????????打印結果

????????控制臺打印如下代碼:

225750_LohV_2528735.png

linux源碼分析。????????Spring AOP是如何實現AOP的?首先我們知道,Spring是否支持注釋的AOP是由一個配置文件來控制的,也就是<aop:aspectj-autoproxy />,下面我們分析就從這句配置開始。


Spring AOP 的入口

????????BeanDefinition 的解析

????????首先spring-config.xml配置文件里的<aop:aspectj-autoproxy>進行解析,發現其如果不是bean標簽,則會用不同的類來解析。解析AOP的是:http\://www.springframework.org/schema/aop=org.springframeworl.aop.config.AopNamespaceHandler。也就是? AopNamespaceHandler 類來解析,我們對?AopNamespaceHandler 類進行分析

004313_Ah28_2528735.png

????????發現?AopNamespaceHandler 的一段代碼是?<aop:aspectj-autoproxy> 解析的入口

AopNamespaceHandler.java

????????registerBeanDefinitionParser("aspectj-autoproxy",?new?AspectJAutoProxyBeanDefinitionParser());

Spring aop。????????????????

????????BeanDefinition 的載入

????????由此,我們知道只要配置文件中出現“aspectj-autoproxy”的注解時就會使用解析器對 AspectJAutoProxyBeanDefinitionParser 進行解析

004611_KAlY_2528735.png

????????所有解析器,都是由 BeanDefinitionParser 接口的統一實現,入口都是從 parse函數開始的,AspectJAutoProxyBeanDefinitionParser 的 parse 函數如下:

AspectJAutoProxyBeanDefinitionParser .java

public?BeanDefinition?parse(Element?element,?ParserContext?parserContext)?{//?注冊?AnnotationAwareAspectJAutoProxyCreatorAopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext,?element);//?對于注釋中子類進行處理extendBeanDefinition(element,?parserContext);return?null;
}

Springboot框架、????????其中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary 函數是 AnnotationAwareAspectJAutoProxyCreator 注冊的地方,也是注冊的主要邏輯實現的地方。

AopNamespaceUtils.java

public?static?void?registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext?parserContext,?Element?sourceElement)?{//?注冊或升級?AutoProxyCreator?定義?beanName?為?org.Springframework.aop.config.internalAutoProxyCreator的//?BeanDefinitionBeanDefinition?beanDefinition?=?AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(),?parserContext.extractSource(sourceElement));//?對于?proxy-target-class?以及?expose-proxy?屬性的處理useClassProxyingIfNecessary(parserContext.getRegistry(),?sourceElement);//?注冊組件并通知,便于監聽器作進一步處理//?其中?beanDefinition?的?className?為?AnnotationAwareAspectJAutoProxyCreatorregisterComponentIfNecessary(beanDefinition,?parserContext);
}


????????BeanDefinition 的注冊

????????在對于AOP實現上,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成的,它可以根據@Point 注解定義的切點來自動代理相匹配的 bean。但是為了配置簡便,Spring 使用了自定義配置來幫我們自動注冊?AnnotationAwareAspectJAutoProxyCreator。具體實現如下

AopNamespaceUtils.java

public?static?BeanDefinition?registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry?registry,?Object?source)?{return?registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,?registry,?source);
}

netty源碼分析?AopConfigUtils.java

private?static?BeanDefinition?registerOrEscalateApcAsRequired(Class<?>?cls,?BeanDefinitionRegistry?registry,?Object?source)?{Assert.notNull(registry,?"BeanDefinitionRegistry?must?not?be?null");//?如果已經存在自動代理創建器且存在的自動代理創建器與現在的不一致那么需要根據優先級來判斷到底需要任何使用if?(registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME))?{//?AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"BeanDefinition?apcDefinition?=?registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if?(!cls.getName().equals(apcDefinition.getBeanClassName()))?{int?currentPriority?=?findPriorityForClass(apcDefinition.getBeanClassName());int?requiredPriority?=?findPriorityForClass(cls);if?(currentPriority?<?requiredPriority)?{//?改變bean?最重要的就是改變bean?所對應的?className?屬性apcDefinition.setBeanClassName(cls.getName());}}//?如果已經存在自動代理創建器并且與將要創建的一致,那么無需再此創建return?null;}RootBeanDefinition?beanDefinition?=?new?RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order",?Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//?AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME,?beanDefinition);return?beanDefinition;
}

????????以上代碼中實現了自動注冊?AnnotationAwareAspectJAutoProxyCreator 類的功能,同時這里還涉及了一個優先級的問題,如果已經存在了自動代理創建器,而且存在的自動代理創建器與現在的不一致,那么就需要根據優先級來判斷到底需要使用哪一個?????

????????????

屬性處理

????????一般而言,Spring AOP 內部使用 JDK 動態代理或者 CGLIB 來為目標對象創建代理。如果被代理的目標對象實現了至少一個接口,則會使用 JDK 動態代理。所有該目標類型實現的接口都將被代理。若該目標對象沒有實現任何接口,則創建一個 CGLIB 代理。一般情況下,使用 CGLIB 需要考慮增強(advise)Final 方法不能被復寫以及需要指定 CGLIB 包的位置,盡管 CGLIB 效率更高,但還是推薦使用 JDK 動態代理。

????????而 AOP 配置中的?prioxy-target-class 和 expose-proxy 屬性在代理生成中起到了至關重要的。prioxy-target-class 主要負責上面兩種代理的實現,而?expose-proxy 則負責自我調用切面中的增強。

AopNamespaceUtils.java

private?static?void?useClassProxyingIfNecessary(BeanDefinitionRegistry?registry,?Element?sourceElement)?{if?(sourceElement?!=?null)?{//?對于?proxy-target-class?屬性的處理boolean?proxyTargetClass?=?Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));if?(proxyTargetClass)?{AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}//?對于?expose-proxy?屬性的處理boolean?exposeProxy?=?Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));if?(exposeProxy)?{AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}
}

AopConfigUtils.java

public?static?void?forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry?registry)?{//?強制使用,其實也是一個屬性設置if?(registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME))?{BeanDefinition?definition?=?registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);definition.getPropertyValues().add("proxyTargetClass",?Boolean.TRUE);}
}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);}
}

????????這些,基本就是Spring AOP部分的實現框架了,下節就要對AOP的主要實現類?AnnotationAwareAspectJAutoProxyCreator 進行分析。

Springboot注解?


——水門(2016年3月于杭州

轉載于:https://my.oschina.net/kaywu123/blog/632486

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/184114.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息