Spring Aop深度解析
關於spring Aop也接觸了好多知識了,在此,做一個總結,以便學習。
Spring Aop是什麼?
AOP(Aspect-OrientedProgramming,面向切面編程),可以說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼往往水平地散佈在所有對象層次中,而與它所散佈到的對象的核心功能毫無關係。
基本術語:
(1)切面(Aspect)
切面是一個橫切關注點的模塊化,一個切面能夠包含同一個類型的不同增強方法,比如說事務處理和日誌處理可以理解爲兩個切面。切面由切入點和通知組成,它既包含了橫切邏輯的定義,也包括了切入點的定義。 Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連接點中。
@Component
@Aspect
public class LogAspect {
}
可以簡單地認爲, 使用 @Aspect 註解的類就是切面
(2) 目標對象(Target)
目標對象指將要被增強的對象,即包含主業務邏輯的類對象。或者說是被一個或者多個切面所通知的對象。
(3) 連接點(JoinPoint)
程序執行過程中明確的點,如方法的調用或特定的異常被拋出。連接點由兩個信息確定:
簡單來說,連接點就是被攔截到的程序執行點,因爲Spring只支持方法類型的連接點,所以在Spring中連接點就是被攔截到的方法。
@Before("pointcut()")
public void log(JoinPoint joinPoint) { //這個JoinPoint參數就是連接點
}
(4) 切入點(PointCut)
切入點是對連接點進行攔截的條件定義。切入點表達式如何和連接點匹配是AOP的核心,Spring缺省使用AspectJ切入點語法。
一般認爲,所有的方法都可以認爲是連接點,但是我們並不希望在所有的方法上都添加通知,而切入點的作用就是提供一組規則(使用 AspectJ pointcut expression language 來描述) 來匹配連接點,給滿足規則的連接點添加通知。
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void pointcut() {
}
上邊切入點的匹配規則是com.remcarpediem.test.aop.service包下的所有類的所有函數。
(5) 通知(Advice)
通知是指攔截到連接點之後要執行的代碼,包括了“around”、“before”和“after”等不同類型的通知。Spring AOP框架以攔截器來實現通知模型,並維護一個以連接點爲中心的攔截器鏈。
// @Before說明這是一個前置通知,log函數中是要前置執行的代碼,JoinPoint是連接點,
@Before("pointcut()")
public void log(JoinPoint joinPoint) {
}
通知方法:
前置通知:在我們執行目標方法之前運行(@Before)
後置通知:在我們目標方法運行結束之後 ,不管有沒有異常(@After)
返回通知:在我們的目標方法正常返回值後運行(@AfterReturning)
異常通知:在我們的目標方法出現異常後運行(@AfterThrowing)
環繞通知:動態代理, 需要手動執行joinPoint.procced()(其實就是執行我們的目標方法執行之前相當於前置通知, 執行之後就相當於我們後置通知(@Around)
代碼展示:
切面類:
package Aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
/**
* @author 孫一鳴 on 2020/2/22
*/
@Aspect
public class LogAspects {
@Pointcut("execution(public int Aop.Calculator.*(..))")
public void pointCut(){};
//@before代表在目標方法執行前切入, 並指定在哪個方法前切入
@Before("pointCut()")
public void logStart(){
System.out.println("除法運行....參數列表是:{}");
}
@After("pointCut()")
public void logEnd(){
System.out.println("除法結束......");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("除法正常返回......運行結果是:{}");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("運行異常......異常信息是:{}");
}
@Around("pointCut()")
public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("@Arount:執行目標方法之前...");
Object obj = proceedingJoinPoint.proceed();//相當於開始調div地
System.out.println("@Arount:執行目標方法之後...");
return obj;
}
}
業務邏輯方法:
package Aop;
/**
* @author 孫一鳴 on 2020/2/22
*/
public class Calculator {
//業務邏輯方法
public int div(int i, int j) {
System.out.println("----業務方法:----");
return i / j;
}
}
配置bean:
package Aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @author 孫一鳴 on 2020/2/22
*/
@Configuration
@EnableAspectJAutoProxy //開啓註解版Aop功能
public class Config1 {
@Bean
public Calculator calculator(){
return new Calculator();
}
@Bean
public LogAspects aspects(){
return new LogAspects();
}
}
測試類:
package Aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author 孫一鳴 on 2020/2/22
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config1.class);
Calculator calculator = (Calculator) context.getBean("calculator");
calculator.div(12,2);
}
}
結果截圖:
Aop原理研究:
對於Aop的原理,我們應該從Aop的註解 @EnableAspectJAutoProxy來研究。
1.@EnableAspectJAutoProxy是什麼?
我們進入這個註解來看:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
我們可以看到註解中要導入一個名爲:AspectJAutoProxyRegistrar的組件。
進入這個組件:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
ImportBeanDefinitionRegistrar 這個接口,在以前的學習中使用過,ImportBeanDefinitionRegistrar手動註冊bean到容器中,通過@import(HelloBeanRegister.class)使用
到這裏,我們大致梳理一下步驟
- 使用註解@EnableAspectJAutoProxy
- 註解中import導入組件 AspectJAutoProxyRegistrar
- 組件中要註冊Bean
- 註冊了一個什麼樣的對象呢? AnnotationAwareAspectJAutoProxyCreator.class
- 研究這個註解給容器中註冊的這個組件的功能?
我們仔細看一下組件AspectJAutoProxyRegistrar是怎樣註冊Bean?
組件中實現了接口ImportBeanDefinitionRegistrar,定義了註冊方法:registerBeanDefinitions(){
}
進入registerBeanDefinitions方法:
第一句:
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
首先調用了Aop配置工具類裏的方法
方法名解釋:如果需要的話註冊切面註解自動代理創建器
進入AopConfigUtils類
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
//給容器中註冊了一個註解 裝配模式下切面自動代理創建器
}
進入registerOrEscalateApcAsRequired方法:
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator"))
//首先判斷容器中是否含有internalAutoProxyCreator 這個Bean的定義信息
{
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
//如果有得到internalAutoProxyCreator這個Bean的信息
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {//如果容器中沒有internalAutoProxyCreator 這個Bean的定義信息
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
//將這個internalAutoProxyCreator Bean註冊進容器
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
研究這個組件的功能?AnnotationAwareAspectJAutoProxyCreator.class
進入AnnotationAwareAspectJAutoProxyCreator類
繼承了AspectJAwareAdvisorAutoProxyCreator
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
類圖詳細分析:
到這裏,我們大致梳理一下步驟
這是前面的步驟:
- 使用註解@EnableAspectJAutoProxy
- 註解中import導入組件 AspectJAutoProxyRegistrar
- 組件中要註冊Bean
- 註冊了一個什麼樣的對象呢? AnnotationAwareAspectJAutoProxyCreator.class
- 研究這個註解給容器中註冊的這個組件的功能?
===========================================
新發現: - 通過類圖知道,這個組件具有BeanPostProcessor的特點,也有BeanFactoryAware的特點,說明這個Bean組件AnnotationAwareAspectJAutoProxyCreator 是一個後置處理器,也是一個Aware接口的實現類
分析AnnotationAwareAspectJAutoProxyCreator的關於後置處理器的方法
首先來看AnnotationAwareAspectJAutoProxyCreator 繼承的最頂層父類:1.AbstractAutoProxyCreator
2.AbstractAdvisorAutoProxyCreator
3.父類:AspectJAwareAdvisorAutoProxyCreator
4.自己:AnnotationAwareAspectJAutoProxyCreator
Debug模式學習源碼:
創建和註冊AnnotationAwareAspectJAutoProxyCreator的過程流程:
1)、傳入配置類,創建ioc容器
2)、註冊配置類,調用refresh()刷新容器;
3)、registerBeanPostProcessors(beanFactory);註冊bean的後置處理器來方便攔截bean的創建;
- 1)、先獲取ioc容器已經定義了的需要創建對象的所有BeanPostProcessor
2)、給容器中加別的BeanPostProcessor
3)、優先註冊實現了PriorityOrdered接口的BeanPostProcessor;
4)、再給容器中註冊實現了Ordered接口的BeanPostProcessor;
5)、註冊沒實現優先級接口的BeanPostProcessor;
6)、註冊BeanPostProcessor,實際上就是創建BeanPostProcessor對象,保存在容器中;
創建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】-
1)、創建Bean的實例 2)、populateBean;給bean的各種屬性賦值 3)、initializeBean:初始化bean; 1)、invokeAwareMethods():處理Aware接口的方法回調 2)、applyBeanPostProcessorsBeforeInitialization():應用後置處理器的postProcessBeforeInitialization() 3)、invokeInitMethods();執行自定義的初始化方法 4)、applyBeanPostProcessorsAfterInitialization();執行後置處理器的postProcessAfterInitialization(); 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)創建成功;--》aspectJAdvisorsBuilder
beanFactory.addBeanPostProcessor(postProcessor); -
AnnotationAwareAspectJAutoProxyCreator => 後置處理器類型InstantiationAwareBeanPostProcessor
4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;創建剩下的單實例bean
- 1)、遍歷獲取容器中所有的Bean,依次創建對象getBean(beanName);
getBean->doGetBean()->getSingleton()->
2)、創建bean
【AnnotationAwareAspectJAutoProxyCreator在所有bean創建之前會有一個攔截,InstantiationAwareBeanPostProcessor,會調用postProcessBeforeInstantiation()】
2.1)、先從緩存中獲取當前bean,如果能獲取到,說明bean是之前被創建過的,直接使用,否則再創建;
只要創建好的Bean都會被緩存起來
2.2)、createBean();創建bean;
AnnotationAwareAspectJAutoProxyCreator 會在任何bean創建之前先嚐試返回bean的實例
【BeanPostProcessor是在Bean對象創建完成初始化前後調用的】
【InstantiationAwareBeanPostProcessor是在創建Bean實例之前先嚐試用後置處理器返回對象的】
1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
希望後置處理器在此能返回一個代理對象;如果能返回代理對象就使用,如果不能就繼續
1)、後置處理器先嚐試返回對象;
bean = applyBeanPostProcessorsBeforeInstantiation():
拿到所有後置處理器,如果是InstantiationAwareBeanPostProcessor;
就執行postProcessBeforeInstantiation
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
2)、doCreateBean(beanName, mbdToUse, args);真正的去創建一個bean實例;和3.6流程一樣;
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1)、每一個bean創建之前,調用postProcessBeforeInstantiation();
關心MathCalculator和LogAspect的創建
-
1)、判斷當前bean是否在advisedBeans中(保存了所有需要增強bean)
-
2)、判斷當前bean是否是基礎類型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
-
3)、是否需要跳過 1)、獲取候選的增強器(切面裏面的通知方法)【List<Advisor> candidateAdvisors】 每一個封裝的通知方法的增強器是 InstantiationModelAwarePointcutAdvisor; 判斷每一個增強器是否是 AspectJPointcutAdvisor 類型的;返回true 2)、永遠返回false
2)、創建對象
-
postProcessAfterInitialization;
3)、目標方法執行 ;
容器中保存了組件的代理對象(cglib增強後的對象),這個對象裏面保存了詳細信息(比如增強器,目標對象,xxx);
1)、CglibAopProxy.intercept();攔截目標方法的執行
2)、根據ProxyFactory對象獲取將要執行的目標方法攔截器鏈;- List< Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
1)、List< Object> interceptorList保存所有攔截器 5
一個默認的ExposeInvocationInterceptor 和 4個增強器;
2)、遍歷所有的增強器,將其轉爲Interceptor;
registry.getInterceptors(advisor);
3)、將增強器轉爲List;
如果是MethodInterceptor,直接加入到集合中
如果不是,使用AdvisorAdapter將增強器轉爲MethodInterceptor;
轉換完成返回MethodInterceptor數組;
3)、如果沒有攔截器鏈,直接執行目標方法;
攔截器鏈(每一個通知方法又被包裝爲方法攔截器,利用MethodInterceptor機制)
4)、如果有攔截器鏈,把需要執行的目標對象,目標方法,攔截器鏈等信息傳入創建一個 CglibMethodInvocation 對象, 並調用 Object retVal = mi.proceed();
5)、攔截器鏈的觸發過程;
- 1)、如果沒有攔截器執行執行目標方法,或者攔截器的索引和攔截器數組-1大小一樣(指定到了最後一個攔截器)執行目標方法;
- 2)、鏈式獲取每一個攔截器,攔截器執行invoke方法,每一個攔截器等待下一個攔截器執行完成返回以後再來執行;
攔截器鏈的機制,保證通知方法與目標方法的執行順序;
- List< Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
總結:
1)@EnableAspectJAutoProxy 開啓AOP功能
2) @EnableAspectJAutoProxy 會給容器中註冊一個組件 AnnotationAwareAspectJAutoProxyCreator
3)AnnotationAwareAspectJAutoProxyCreator是一個後置處理器;
4)容器的創建流程:
-
1)、registerBeanPostProcessors()註冊後置處理器;創建AnnotationAwareAspectJAutoProxyCreator對象
2)、finishBeanFactoryInitialization()初始化剩下的單實例bean1)、創建業務邏輯組件和切面組件 2)、AnnotationAwareAspectJAutoProxyCreator攔截組件的創建過程 3)、組件創建完之後,判斷組件是否需要增強 是:切面的通知方法,包裝成增強器(Advisor);給業務邏輯組件創建一個代理對象(cglib);
5)執行目標方法:</font>
-
1)、代理對象執行目標方法
2)、CglibAopProxy.intercept();1)、得到目標方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor) 2)、利用攔截器的鏈式機制,依次進入每一個攔截器進行執行; 3)、效果: 正常執行:前置通知-》目標方法-》後置通知-》返回通知 出現異常:前置通知-》目標方法-》後置通知-》異常通知