先是生成代理對象,而後是攔截器的做用,最後是編織的具體實現。這是AOP實現的三個步驟,固然Spring AOP也是同樣。
java
而從Spring AOP總體架構上看,其核心都是創建在代理上的。當咱們創建加強實例時,咱們必須先使用 ProxyFactory 類加入咱們須要織入該類的全部加強,而後爲該類建立代理。通常而言,AOP實現代理的方法有三種,而 Spring 內部用到了其中的兩種方法:動態代理和靜態代理,而動態代理又分爲JDK動態代理和CGLIB代理。而前面提到的 ProxyFactory 類控制着 Spring AOP 中的織入和建立代理的過程。在真正建立代理以前,咱們必須指定被加強對象或者目標對象。咱們是經過 setTarget() 方法來完成這個步驟的。而 ProxyFactory 內部將生成代理的過程所有轉給 DefaultAopProxyFactory 對象來完成,而後根據設置轉給其餘的類來完成。下面是 Spring AOP 生成代理的原理圖:
spring
而特別須要注意的是,在 Spring AOP 中,一個 Advisor(通知者,也翻做 通知器或者加強器)就是一個切面,他的主要做用是整合切面加強設計(Advice)和切入點設計(Pointcut)。Advisor有兩個子接口:IntroductionAdvisor 和 PointcutAdvisor,基本全部切入點控制的 Advisor 都是由 PointcutAdvisor 實現的。而下面的是Spring AOP的總體架構圖架構
乍一看,很是的複雜,但若是結合上面的Spring AOP生成代理的原理圖一塊兒看,也就那麼回事,只是豐富的許多屬性了,咱們會慢慢介紹的。框架
分析 Spring AOP 源碼的話,直接從最簡單業務代碼入手,一步步的對源碼進行解析。但願藉此能看清楚 Spring AOP 縱向和橫向代碼之間的聯繫,並且思惟也不會太跳躍。下面就是 Spring AOP 測試程序 Test 項目的結構圖:函數
注:Spring 源碼版本是 Spring-4.1.6,個人源碼分析也是基於這一版本的,JDK 版本是 1.8,aspect 是用的 aspectjrt-1.7.4 和 aspectjweaver-1.7.4。
源碼分析
在實際項目中,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"); } }
Spring AOP 實現的核心,這裏採用了@AspectJ註釋對POJO進行標註,使AOP的工做大大簡化。
ui
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; } }
XML是Spring的基礎,儘管Spring一再簡化配置,而且大有使用註解取代XML配置之勢,但不管如何XML仍是Spring的基石。
this
<?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/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <!-- 激活自動代理功能 --> <aop:aspectj-autoproxy /> <!-- 業務邏輯切面配置 --> <bean id="test" class = "test.TestBean" /> <bean class="test.AspectTest" /> </beans>
測試程序,驗證效果。
spa
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(); } }
控制檯打印以下代碼:
Spring AOP是如何實現AOP的?首先咱們知道,Spring是否支持註釋的AOP是由一個配置文件來控制的,也就是<aop:aspectj-autoproxy />,下面咱們分析就從這句配置開始。
首先spring-config.xml配置文件裏的<aop:aspectj-autoproxy>進行解析,發現其若是不是bean標籤,則會用不一樣的類來解析。解析AOP的是:http\://www.springframework.org/schema/aop=org.springframeworl.aop.config.AopNamespaceHandler。也就是 AopNamespaceHandler 類來解析,咱們對 AopNamespaceHandler 類進行分析
發現 AopNamespaceHandler 的一段代碼是 <aop:aspectj-autoproxy> 解析的入口
AopNamespaceHandler.java
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
由此,咱們知道只要配置文件中出現「aspectj-autoproxy」的註解時就會使用解析器對 AspectJAutoProxyBeanDefinitionParser 進行解析
全部解析器,都是由 BeanDefinitionParser 接口的統一實現,入口都是從 parse函數開始的,AspectJAutoProxyBeanDefinitionParser 的 parse 函數以下:
AspectJAutoProxyBeanDefinitionParser .java
public BeanDefinition parse(Element element, ParserContext parserContext) { // 註冊 AnnotationAwareAspectJAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 對於註釋中子類進行處理 extendBeanDefinition(element, parserContext); return null; }
其中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary 函數是 AnnotationAwareAspectJAutoProxyCreator 註冊的地方,也是註冊的主要邏輯實現的地方。
AopNamespaceUtils.java
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { // 註冊或升級 AutoProxyCreator 定義 beanName 爲 org.Springframework.aop.config.internalAutoProxyCreator的 // BeanDefinition BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // 對於 proxy-target-class 以及 expose-proxy 屬性的處理 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 註冊組件並通知,便於監聽器做進一步處理 // 其中 beanDefinition 的 className 爲 AnnotationAwareAspectJAutoProxyCreator registerComponentIfNecessary(beanDefinition, parserContext); }
在對於AOP實現上,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成的,它能夠根據@Point 註解定義的切點來自動代理相匹配的 bean。可是爲了配置簡便,Spring 使用了自定義配置來幫咱們自動註冊 AnnotationAwareAspectJAutoProxyCreator。具體實現以下
AopNamespaceUtils.java
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }
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 進行分析。
——水門(2016年3月於杭州)