教程共分為五篇,從AOP實例的構建及其重要組件、基本運行流程、容器創建流程、關鍵方法調用、原理總結歸納等幾個方面一步步走進AOP的世界。 本篇主要為讀者演示構建AOP實例及AOP核心組件分析。 一、項目構建 讀者可直接下載示例工程,或複製以下的代碼到本地工程開啟教程。 <?xml version=" ...
教程共分為五篇,從AOP實例的構建及其重要組件、基本運行流程、容器創建流程、關鍵方法調用、原理總結歸納等幾個方面一步步走進AOP的世界。
本篇主要為讀者演示構建AOP實例及AOP核心組件分析。
一、項目構建
讀者可直接下載示例工程,或複製以下的代碼到本地工程開啟教程。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tlj</groupId> <artifactId>spring-test</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.13.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.13.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.44</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.12.RELEASE</version> </dependency> </dependencies> </project>pom.xml
package config; import aop.LogAspects; import aop.MathCalculator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy @Configuration public class ConfigOfAOP { @Bean public MathCalculator calculator(){ return new MathCalculator(); } @Bean public LogAspects logAspects(){ return new LogAspects(); } }ConfigOfAOP
package aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import java.util.Arrays; /** * 切麵類 */ @Aspect public class LogAspects { @Pointcut("execution(public int aop.MathCalculator.*(..))") public void poinCut(){} @Before("poinCut()") public void logStart(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); System.out.println(joinPoint.getSignature().getName()+" 運行。。。@Before "+ Arrays.asList(args)); } @After("poinCut()") public void logEnd(){ System.out.println("除法結束..@After"); } @AfterReturning(value = "poinCut()",returning = "result")//獲取方法返回值 public void logReturning(Object result){ System.out.println("除法正常返回..@AfterReturning "+result); } @AfterThrowing(value = "poinCut()",throwing = "e") public void logException(Exception e){ System.out.println("除法異常..@AfterThrowing "+e); } }LogAspects
package aop; public class MathCalculator { public int div(int i,int j){ System.out.println("MathCalculator"); return i/j; } }MathCalculator
項目目錄結構如下:
到這裡,我們的項目是構建完了。
二、日誌切麵方法測試
打開測試類,運行測試方法
我們可以看到,總共列印了四行,除了第二行列印是業務方法的調用,其他都是調用日誌切麵類中的方法列印出來的。
這就是AOP的使用效果,除了用在日誌,還有其他很多用法,這裡就不贅述了。
三、關鍵組件探究
為什麼AOP能在業務方法調用的前後和發生異常時調用切麵方法呢,首先我們需要瞭解它引入了什麼組件。
為了讓AOP起作用,我們需要在配置類上添加@EnableAspectJAutoProxy註解,從字面上看,翻譯為啟動切麵自動代理,那它是怎麼啟動的呢
ctrl+滑鼠左鍵進入這個註解,我們可以看到EnableAspectJAutoProxy介面使用@Import註解導入了AspectJAutoProxyRegistrar這個類
再次ctrl+滑鼠左鍵進入AspectJAutoProxyRegistrar,可以看到,它實現了ImportBeanDefinitionRegistrar介面。
此介面中的registerBeanDefinitions方法,正是用來像容器中註冊組件的。
看來想要知道@EnableAspectJAutoProxy註解到底給容器中添加了什麼組件,我們需要進行調試,找到ImportBeanDefinitionRegistrar方法給容器中添加的組件。這個組件一定就是AOP實現的關鍵。
四、調試尋找組件
如下圖,我們在ImportBeanDefinitionRegistrar介面的註冊方法中打上斷點。
點擊debug開始調試,程式來到了AspectJAutoProxyRegistrar的registerBeanDefinitions方法
正在執行的是AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry),方法的字面意思:註冊切麵自動代理創造組件如果需要的話
接著進入這個方法直到以下這個方法,可以看到返回的是BeanDefinition類型,說明在方法裡面有組件定義或註冊相關的動作。
110行進行判斷,如果容器中存在AUTO_PROXY_CREATOR_BEAN_NAME這個定義信息,進行以下判斷,巴拉巴拉,最後return null,退出這個方法。
如果不存在,可以看到在125行已經有註冊名為AUTO_PROXY_CREATOR_BEAN_NAME的組件的動作。
把滑鼠放在AUTO_PROXY_CREATOR_BEAN_NAME上,可以看到它實際是叫internalAutoProxyCreator
接著我們進行下一步,到110行時,顯然第一次它是不存在這個類的,所以跳過if{}中的內容,到121行時,我們可以看看cls的信息,發現這個類叫AnnotationAwareAspectJAutoProxyCreator
到125行時,已經設置好AnnotationAwareAspectJAutoProxyCreator的各種屬性,將其命名為internalAutoProxyCreator註冊進容器,在126行進行返回。
在上面過程中,我們可以得到的結論是,@EnableAspectJAutoProxy註解實際上就是給容器中添加了名為internalAutoProxyCreator的組件,實際就是AnnotationAwareAspectJAutoProxyCreator這個類。
我們可以得出AnnotationAwareAspectJAutoProxyCreator就是實現AOP的核心組件。
接下來我們來探究一下AnnotationAwareAspectJAutoProxyCreator的繼承關係,以及它是什麼。
五、AnnotationAwareAspectJAutoProxyCreator組件是什麼
進入這個類,發現它繼承了AspectJAwareAdvisorAutoProxyCreator
那麼AspectJAwareAdvisorAutoProxyCreator又是什麼呢,接著進入AspectJAwareAdvisorAutoProxyCreator
發現AspectJAwareAdvisorAutoProxyCreator又繼承了AbstractAdvisorAutoProxyCreator
我們接著進入AbstractAdvisorAutoProxyCreator中查看
可以看到AbstractAdvisorAutoProxyCreator繼承了AbstractAutoProxyCreator
再進入AbstractAutoProxyCreator
可以看到AbstractAutoProxyCreator繼承了ProxyProcessorSupport
並實現了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware兩個介面
接下來我們主要看這兩個介面
SmartInstantiationAwareBeanPostProcessor明顯是一個後置處理器介面,BeanFactoryAware是一個底層組件介面,實現BeanFactoryAware就可以註入並調用BeanFactory。
經過層層的進入,可以得到如下的關係
這樣看來,我們可以得出結論——AnnotationAwareAspectJAutoProxyCreator是一個後置處理器(後置處理器原理)
總結
經過以上五個步驟,我們看到AOP的使用效果,發現了AOP的核心組件AnnotationAwareAspectJAutoProxyCreator是一個後置處理器,理清了AnnotationAwareAspectJAutoProxyCreator的繼承實現關係。
總得來說,就是明白了核心組件是什麼。
在接下來的篇章我們將從核心組件在哪發揮作用,何時發揮,以及做了什麼,一步步深入原理。