淺嘗Spring註解開發_AOP原理及完整過程分析(原始碼)

蔚然丶丶發表於2022-05-04

淺嘗Spring註解開發_AOP原理及完整過程分析(原始碼)

淺嘗Spring註解開發,基於Spring 4.3.12
分析AOP執行過程及原始碼,包含AOP註解使用、AOP原理、分析AnnotationAwareAspectJAutoProxyCreator、AOP代理、攔截器、鏈式呼叫
以初學者的角度跟著雷豐陽的視訊走完AOP整個過程,設計很巧妙。補充了自己的理解,為了流程連續性,所以全放在放在一篇裡,如有錯誤請多多指教。

淺嘗Spring註解開發_自定義註冊元件、屬性賦值、自動裝配
淺嘗Spring註解開發_Bean生命週期及執行過程
淺嘗Spring註解開發_AOP原理及完整過程分析(原始碼)

AOP註解使用

AOP動態代理:指在程式執行期間動態的將某段程式碼切入到指定方法指定位置進行執行的程式設計方式

1)、將業務邏輯元件和切面類都加入到容器中,告訴Spring哪個是切面類(@Aspect)
2)、在切面類上的每一個通知方法上標註通知註解,告訴Spring何時何地執行(切入點表示式)
3)、開啟基於註解的aop模式:@EnableAspectJAutoProxy

1、匯入aop模組;Spring AOP:(spring-aspects)
2、定義一個業務邏輯類(MathCalculator);在業務邏輯執行的時候將日誌進行列印(方法之前、方法執行結束、方法出現異常,xxx)
3、定義一個日誌切面類(LogAspects):切面類裡面的方法需要動態感知MathCalculator.div執行到哪裡然後執行;
  	通知方法:
  		前置通知(@Before):logStart:在目標方法(div())執行之前執行
  		後置通知(@After):logEnd:在目標方法(div())執行結束之後執行(無論方法正常結束還是異常結束)
  		返回通知(@AfterReturning):logReturn:在目標方法(div())正常返回之後執行
  		異常通知(@AfterThrowing):logException:在目標方法(div())出現異常以後執行
  		環繞通知(@Around):動態代理,手動推進目標方法執行(joinPoint.procced())
4、給切面類的目標方法標註何時何地執行(通知註解);
5、將切面類和業務邏輯類(目標方法所在類)都加入到容器中;
6、必須告訴Spring哪個類是切面類(給切面類上加一個註解:@Aspect)
[7]、給配置類中加 @EnableAspectJAutoProxy 【開啟基於註解的aop模式】
  		在Spring中很多的 @EnableXXX;
  1. 匯入aop模組:org.springframework.spring-aspects

    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aspects</artifactId>
    			<version>4.3.12.RELEASE</version>
    		</dependency>
    
  2. 定義一個業務邏輯類(MathCalculator)。在業務邏輯執行的時候將日誌進行列印(方法之前、方法執行結束、方法出現異常,xxx)。

    //業務類
    public class MathCalculator {
    	
    	public int div(int i,int j){
    		System.out.println("MathCalculator...div...");
    		return i/j;	
    	}
    
    }
    
  3. 定義一個日誌切面類(LogAspects):切面類裡面的方法需要動態感知MathCalculator.div執行到哪裡然後執行。通知方法:

    1. 前置通知(@Before):logStart:在目標方法(div)執行之前執行
    2. 最終通知(@After):logEnd:在目標方法(div)執行結束之後執行(無論方法正常結束還是異常結束)
    3. 後置通知/返回通知(@AfterReturning):logReturn:在目標方法(div)正常返回之後執行
    4. 異常通知(@AfterThrowing):logException:在目標方法(div)出現異常以後執行
    5. 環繞通知(@Around):動態代理,手動推進目標方法執行(joinPoint.proceed())
  4. 給切面類的目標方法標註何時何地執行(通知註解)。

    //@Aspect: 告訴Spring當前類是一個切面類
    @Aspect
    public class LogAspects {
    	
    	//抽取公共的切入點表示式,空方法,只使用註解
    	//1、本類引用使用:pointCut()
    	//2、其他的切面引用使用:com.xxx.aop.LogAspects.pointCut()
    	@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    	public void pointCut(){};
    	
    	//前置通知,JoinPoint可以獲取切入點資訊
    	//@Before在目標方法之前切入,切入點表示式(指定在哪個方法切入),JoinPoint封裝了切面方法資訊
    	@Before("pointCut()")
    	public void logStart(JoinPoint joinPoint){
    		Object[] args = joinPoint.getArgs();
    		System.out.println(""+joinPoint.getSignature().getName()+"執行...@Before:引數列表是:{"+Arrays.asList(args)+"}");
    	}
    	
    	//最終通知
    	@After("com.atguigu.aop.LogAspects.pointCut()")
    	public void logEnd(JoinPoint joinPoint){
    		System.out.println(""+joinPoint.getSignature().getName()+"結束...@After");
    	}
    	
    	//後置通知/返回通知,需要標明哪個引數接收返回值
    	//JoinPoint一定要出現在參數列的第一位
    	@AfterReturning(value="pointCut()",returning="result")
    	public void logReturn(JoinPoint joinPoint,Object result){
    		System.out.println(""+joinPoint.getSignature().getName()+"正常返回...@AfterReturning:執行結果:{"+result+"}");
    	}
    	
    	//異常通知,需要標明哪個引數接收異常
    	@AfterThrowing(value="pointCut()",throwing="exception")
    	public void logException(JoinPoint joinPoint,Exception exception){
    		System.out.println(""+joinPoint.getSignature().getName()+"異常...異常資訊:{"+exception+"}");
    	}
    
    }
    
  5. 將切面類和業務邏輯類(目標方法所在類)都加入到容器中。

  6. 必須告訴Spring哪個類是切面類(給切面類上加一個註解:@Aspect)。

  7. 給配置類中加 @EnableAspectJAutoProxy (開啟基於註解的aop模式)

    • 在Spring中很多的 @EnableXXX
    //開啟基於註解的aop模式
    @EnableAspectJAutoProxy
    @Configuration
    public class MainConfigOfAOP {
    	 
    	//業務邏輯類加入容器中
    	@Bean
    	public MathCalculator calculator(){
    		return new MathCalculator();
    	}
    
    	//切面類加入到容器中
    	@Bean
    	public LogAspects logAspects(){
    		return new LogAspects();
    	}
    }
    
  8. 測試

    • 必須使用Spring容器呼叫

      public class IOCTest_AOP {
      	
      	@Test
      	public void test01(){
      		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
      		
      		//1、不要自己建立物件
      //		MathCalculator mathCalculator = new MathCalculator();
      //		mathCalculator.div(1, 1);
      		MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
      		
      		mathCalculator.div(1, 0);
      		
      		applicationContext.close();
      	}
      
      }
      
    • 輸出

      div執行...@Before:引數列表是{[1,0]}
      //業務方法執行
      MathCalculator...div...
      div結束...@After
      div異常...異常資訊:{java.lang.ArithmeticException: / by zero}
      

⭐AOP原理

看給容器中註冊了什麼元件,這個元件什麼時候工作,這個元件的功能是什麼?

  1. 先將AnnotationAwareAspectJAutoProxyCreator(註釋感知AspectJ自動代理建立器)注入容器
    0. AnnotationAwareAspectJAutoProxyCreator[BeanPostProcessor]本身也是Bean,Bean注入的過程如下:
    1. InstantiationAwareBeanPostProcessor例項化後置處理器
    2. 建立Bean例項
    3. InstantiationAwareBeanPostProcessor例項化後置處理器
    4. Bean屬性賦值
    5. 初始化Bean
      0. 每一個[自定義等]初始化都要執行,過程如下:
      1. 處理Aware介面的方法回撥
      2. BeanPostProcessor初始化後置處理器前置方法
      3. [自定義]初始化
      4. BeanPostProcessor初始化後置處理器後置方法

⭐開始時幹了啥?@EnableAspectJAutoProxy註解

@EnableAspectJAutoProxy最終給容器中註冊一個AnnotationAwareAspectJAutoProxyCreator(註解裝配模式的AspectJ切面自動代理建立器)

  1. 進入@EnableAspectJAutoProxy
  2. 發現註解@Import(AspectJAutoProxyRegistrar.class)給容器中匯入AspectJAutoProxyRegistrar元件
  3. 它實現了ImportBeanDefinitionRegistart,這是一個介面,可以實現自定義註冊元件(在之前的注入元件文章中講過)
  4. 它給容器自定義註冊bean定義資訊(BeanDefinetion) ,就是org.springframework.aop.config.internalAutoProxyCreator,型別是AnnotationAwareAspectJAutoProxyCreator(註解裝配模式的AspectJ切面自動代理建立器)
  5. 並且判斷@EnableAspectJAutoProxy註解並處理資訊

AnnotationAwareAspectJAutoProxyCreator繼承關係

AnnotationAwareAspectJAutoProxyCreator
	-> extends AspectJAwareAdvisorAutoProxyCreator
		-> extends AbstractAdvisorAutoProxyCreator
			-> extends AbstractAutoProxyCreator
				implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware(關注後置處理器(在bean初始化完成前後做事情)、自動裝配BeanFactory)

⭐大有來頭?分析AnnotationAwareAspectJAutoProxyCreator

檢視AnnotationAwareAspectJAutoProxyCreator繼承關係中這些類的方法,找出與後置處理器和Bean工廠有關的方法,打斷點觀察,包括上面注入的ioc入口、業務和AOP。從父類開始:

注意區別兩個不同的後置處理器

  • postProcessBeforeInstantiation  例項化前的後處理(這個是[Smart]InstantiationAwareBeanPostProcessor的)

  • postProcessBeforeInitialization  初始化前的後處理(這個是BeanPostProcessor的)

  • AbstractAutoProxyCreator(抽象自動代理建立者)
    • AbstractAutoProxyCreator.setBeanFactory()
    • AbstractAutoProxyCreator.有後置處理器的邏輯(postProcessBeforeInstantiation(例項化前的後置處理器))
  • AbstractAdvisorAutoProxyCreator.[重寫]setBeanFactory()-->{ initBeanFactory() }
    • 給AbstractAdvisorAutoProxyCreator.setBeanFactory()打上斷點
  • AnnotationAwareAspectJAutoProxyCreator.[重寫]initBeanFactory()

⭐我註冊我?註冊AnnotationAwareAspectJAutoProxyCreator

“我”(指BeanPostProcessor)註冊“我”(指AnnotationAwareAspectJAutoProxyCreator),這裡是說AnnotationAwareAspectJAutoProxyCreator本質是BeanPostProcessor,它自己要按照BeanPostProcessor的方式註冊

  通過後置處理器注入了一個後置處理器

執行過程

啟動程式,首先建立容器

  1. AnnotationConfigApplicationContext傳入配置類,建立ioc容器

  2. 註冊配置類register(),呼叫refresh()重新整理容器

  3. refresh()中有registerBeanPostProcessors(beanFactory)註冊bean的後置處理器(註釋:註冊攔截 Bean 建立的 Bean 處理器),用來攔截bean的建立

  4. 後置處理器的註冊邏輯:

  5. beanFactory.getBeanNamesForType()先獲取ioc容器已經定義了的需要建立物件的所有BeanPostProcessor(為什麼更啟動就有已定義的BeanPostProcessor?是因為在啟動時有一個註解@EnableAspectJAutoProxy已經註冊了一個AnnotationAwareAspectJAutoProxyCreator和容器中其他一些預設的,只是定義還沒建立物件,在上面講過)

    1. 根據獲取的postProcessorNames,發現裡面有一個org.springframework.aop.config.internalAutoProxyCreator,就是通過@EnableAspectJAutoProxy註冊的AnnotationAwareAspectJAutoProxyCreator型別的屬性的名字
    2. 接下來按照postProcessorNames來建立物件
    3. beanFactory.addBeanPostProcessor()給容器中加入別的BeanPostProcessor
    4. 通過postProcessorNames判斷是否有實現Ordered優先順序介面的,分別放在一起分步處理
      1. 優先註冊實現了PriorityOrdered介面的BeanPostProcessor
      2. 再給容器中註冊實現了Ordered介面的BeanPostProcessor
      3. 註冊沒實現優先順序介面的BeanPostProcessor
      4. 執行每個優先順序中的beanFactory.getBean(ppName, BeanPostProcessor.class),跳轉到下一呼叫建立Bean
    5. 註冊BeanPostProcessor,實際上就是建立BeanPostProcessor物件,儲存在容器中(重要,每次建立Bean都要執行,BeanPostProcessor本質也是Bean)
      0. 下面建立internalAutoProxyCreatorBeanPostProcessor型別是AnnotationAwareAspectJAutoProxyCreator
      1. 建立Bean的例項(先從beanFactory.getBean()獲取名字,由getBean()->doGetBean(),在doGetBean()中調getSingleton()獲取單例項的bean,第一次沒有,所以用singletonFactory.getObject()->createBean()->doCreateBean()->createBeanInstance()建立bean例項,下面進行初始化。如同之前講過的BeanPostProcessor原理)
      2. populateBean(beanName,mbd,instanceWrapper):給bean的各種屬性賦值
      3. initializeBean(beanName,exposedObject,mbd):初始化bean
        1. invokeAwareMethods(beanName,bean):處理Aware介面的方法回撥
          1. 先判斷是否是BeanFactoryAware型別,然後到達AbstractAutoProxyCreator.setBeanFactory(beanFactory)方法,就是上面分析AnnotationAwareAspectJAutoProxyCreator的父類AbstractAutoProxyCreator
          2. 然後執行initBeanFactory(beanFactory)初始化BeanFacotry
        2. applyBeanPostProcessorsBeforeInitialization():應用後置處理器的postProcessBeforeInitialization()
        3. invokeInitMethods(beanName,wrappedBean,mbd):執行自定義的初始化方法
        4. applyBeanPostProcessorsAfterInitialization():執行後置處理器的postProcessAfterInitialization()
      4. BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)建立成功
    6. BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)註冊到BeanFactory中,beanFactory.addBeanPostProcessor(postProcessor)BeanFactory新增新的BeanPostProcessor
      以上是建立和註冊AnnotationAwareAspectJAutoProxyCreator的過程

部分原始碼

  • 註冊後置處理器

    	public static void registerBeanPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
    		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    		// 註冊記錄資訊訊息的BeanPostProcessorChecker
    		// bean是在BeanPostProcessor例項化期間建立的
    		// bean不適合被所有BeanPostProcessors處理。
    		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
    		// 在實現prioritordered、Ordered和其他功能的BeanPostProcessors之間分離。
    		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
    		List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
    		List<String> orderedPostProcessorNames = new ArrayList<String>();
    		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
    		for (String ppName : postProcessorNames) {
    			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    				priorityOrderedPostProcessors.add(pp);
    				if (pp instanceof MergedBeanDefinitionPostProcessor) {
    					internalPostProcessors.add(pp);
    				}
    			}
    			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    				orderedPostProcessorNames.add(ppName);
    			}
    			else {
    				nonOrderedPostProcessorNames.add(ppName);
    			}
    		}
    
    		// 首先,註冊實現prioritordered的BeanPostProcessors。
    		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
    		// 接下來,註冊實現Ordered的BeanPostProcessors。
    		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
    		for (String ppName : orderedPostProcessorNames) {
    			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    			orderedPostProcessors.add(pp);
    			if (pp instanceof MergedBeanDefinitionPostProcessor) {
    				internalPostProcessors.add(pp);
    			}
    		}
    		sortPostProcessors(orderedPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
    		// 現在,註冊所有常規的BeanPostProcessors。
    		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
    		for (String ppName : nonOrderedPostProcessorNames) {
    			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
    			nonOrderedPostProcessors.add(pp);
    			if (pp instanceof MergedBeanDefinitionPostProcessor) {
    				internalPostProcessors.add(pp);
    			}
    		}
    		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
    		// 最後,重新註冊所有內部BeanPostProcessors。
    		sortPostProcessors(internalPostProcessors, beanFactory);
    		registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
    		// 將用於檢測內部bean的後處理器重新註冊為applicationlistener,
    		// 將它移動到處理器鏈的末端(用於獲取代理等)。
    		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    	}
    
  • 屬性賦值與初始化

    		// 初始化bean例項
    		Object exposedObject = bean;
    		try {
    			// 屬性賦值
    			populateBean(beanName, mbd, instanceWrapper);
    			if (exposedObject != null) {
    				exposedObject = initializeBean(beanName, exposedObject, mbd);
    			}
    		}
    
  • 初始化BeanFacotry

        public void setBeanFactory(BeanFactory beanFactory) {
            super.setBeanFactory(beanFactory);
            if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
                throw new IllegalArgumentException("AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
            } else {
                //初始化BeanFacotry 
               this.initBeanFactory((ConfigurableListableBeanFactory)beanFactory);
            }
        }
    

執行時棧資訊

  • beanFactory.getBeanNamesForType()先獲取ioc容器已經定義了的需要建立物件的所有BeanPostProcessor

    image

  • 建立bean

    image

  • BeanPostProcessor註冊到BeanFactory中,給BeanFactory新增BeanPostProcessor

    image

⭐你比我快?AnnotationAwareAspectJAutoProxyCreator執行時機

“你”(指AnnotationAwareAspectJAutoProxyCreator)比“我”(指BeanPostProcessor)快,這裡是說AnnotationAwareAspectJAutoProxyCreator早於某些BeanPostProcessor執行,可以提前例項化一些元件

  上面程式碼執行後,註冊了AnnotationAwareAspectJAutoProxyCreator,它是一個後置處理器,當其他元件在建立物件時,都要經過建立Bean例項、給Bean屬性賦值、初始化及前後處理器,所以它可以攔截到這些元件的建立過程,接下來要看它作為後置處理器都做了什麼?

執行流程

接著上面的AbstractAutoProxyCreator.setBeanFactory(beanFactory)執行,然後呼叫AbstractAutoProxyCreator.postProcessBeforeInstantiation(),注意區分[Smart]InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()和BeanPostProcessor.postProcessBeforeInitialization(),這一點上面分析AnnotationAwareAspectJAutoProxyCreator時也有提到,為什麼會呼叫這個方法?在整個程式開始時,AnnotationConfigApplicationContext建立ioc容器註冊配置,register()呼叫refresh()重新整理容器,上面第(3)步執行的是registerBeanPostProcessors(beanFactory)註冊bean的後置處理器方法,在它下面有一個finishBeanFactoryInitialization(beanFactory)方法

  1. finishBeanFactoryInitialization(beanFactory):完成BeanFactory初始化工作,建立剩下的單例項bean

    1. 遍歷獲取容器中所有的Bean,依次建立物件(這一步還沒建立,只是獲取,下一步才建立)

      1. DefaultListableBeanFactory.getBean(beanName)->AbstractBeanFactory.getBean()->doGetBean()->getSingleton(beanName, new ObjectFactory<Object>())=>建立Bean例項,返回一個sharedInstance屬性,註釋是"Create bean instance"(建立bean例項)
      2. 觀察之前的程式碼,發現sharedInstance已經建立Object sharedInstance = getSingleton(beanName)註釋是"Eagerly check singleton cache for manually registered singletons(急切地檢查單例快取中是否有手動註冊的單例)"
      3. 可以發現Bean不是直接建立出來的
    2. 建立bean

      1. 先從快取中獲取當前bean,如果能獲取到,說明bean是之前被建立過的,直接使用,否則再建立。只要建立好的Bean都會被快取起來

      2. 執行getSingleton(beanName, new ObjectFactory<Object>())引數中的匿名內部類方法

      3. createBean():建立bean

        1. createBean()建立bean包括RootBeanDefinition mbd是bean的定義資訊等,繼續執行

        2. resolveBeforeInstantiation(beanName, mbdToUse)註釋是"Give BeanPostProcessors a chance to return a proxy instead of the target bean instance"(讓BeanPostProcessors有機會返回一個代理而不是目標bean例項)

          1. 解析BeforeInstantiation,希望後置處理器在此能返回一個代理物件

          2. 如果能返回代理物件就使用,如果不能就繼續,後置處理器先嚐試返回物件:

            • 分析:
            • AnnotationAwareAspectJAutoProxyCreator會在任何bean建立之前先嚐試返回bean的例項
            • InstantiationAwareBeanPostProcessor是在建立Bean例項之前先嚐試用後置處理器返回物件的
            • BeanPostProcessor是在Bean物件建立完成[自定義]初始化前後呼叫的
            • InstantiationAwareBeanPostProcessor是在構造方法前後執行(就在這一步),BeanPostProcessor是在Bean屬性賦值[初始化之後],自定義初始化前後執行(就在下一步)】
            //拿到所有後置處理器,如果是InstantiationAwareBeanPostProcessor就執行postProcessBeforeInstantiation
            bean = applyBeanPostProcessorsBeforeInstantiation():
            if (bean != null) {
                bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
            }
            //------------
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
            	ibp.postProcessBeforeInstantiation(beanClass, beanName);
            }
            
        3. doCreateBean(beanName, mbdToUse, args):真正的去建立一個bean例項,包括createBeanInstance()建立Bean例項、populateBean()屬性賦值、initializeBean()初始化Bean、Aware介面、前置後置等,和上一節的(3.4)一樣

部分原始碼

  • 建立一個 Bean 例項

    	/**
    	 * 此類的中心方法:建立一個 Bean 例項,
    	 * 填充 Bean 例項、應用後處理器等。
    	 * @see #doCreateBean
    	 */
    	@Override
    	protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Creating instance of bean '" + beanName + "'");
    		}
    		RootBeanDefinition mbdToUse = mbd;
    
    		// 確保此時實際解析了 Bean 類,並且
    		// 在動態解析類的情況下克隆 Bean 定義
    		// 不能儲存在共享的合併 Bean 定義中。
    		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    			mbdToUse = new RootBeanDefinition(mbd);
    			mbdToUse.setBeanClass(resolvedClass);
    		}
    
    		// 準備方法覆蓋。
    		try {
    			mbdToUse.prepareMethodOverrides();
    		}
    		catch (BeanDefinitionValidationException ex) {
    			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
    					beanName, "Validation of method overrides failed", ex);
    		}
    
    		try {
    			// 給 BeanPostProcessors 一個返回代理而不是目標 Bean 例項的機會。
    			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    			if (bean != null) {
    				return bean;
    			}
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
    					"BeanPostProcessor before instantiation of bean failed", ex);
    		}
    		// 實際建立指定的bean
    		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    		if (logger.isDebugEnabled()) {
    			logger.debug("Finished creating instance of bean '" + beanName + "'");
    		}
    		return beanInstance;
    	}
    
  • 嘗試返回一個代理

    	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    		Object bean = null;
    		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
    			// 確保此時實際解析了 Bean 類。
    			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    				Class<?> targetType = determineTargetType(beanName, mbd);
    				if (targetType != null) {
    					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
    					if (bean != null) {
    						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    					}
    				}
    			}
    			mbd.beforeInstantiationResolved = (bean != null);
    		}
    		return bean;
    	}
    

執行時棧資訊

  • 準備例項化

    image

⭐你先我後?建立AOP代理

“你”(指AnnotationAwareAspectJAutoProxyCreator[型別是InstantiationAwareBeanPostProcessor])先“我”(指其他某些BeanPostProcessor)後

  經過上面的流程,已經知道建立Bean需要呼叫AnnotationAwareAspectJAutoProxyCreator[型別是InstantiationAwareBeanPostProcessor]的後置處理器,然後才是初始化前後的BeanPostProcessor後置處理器,下面關心在AnnotationAwareAspectJAutoProxyCreator.postProcessBeforeInstantiation()中都做了什麼操作

執行過程

  1. 繼續上節倒數第二步,進入應用後置處理器中,每一個bean建立之前,呼叫InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
    0. 關心MathCalculator[業務類]LogAspect[切面類]的建立
    1. this.advisedBeans.contaionsKey(cacheKey)判斷當前bean是否在advisedBeans中(儲存了所有需要增強bean)?現在沒有
    2. isInfrastructureClass(beanClass)判斷當前bean是否是基礎型別的Advice、Pointcut、Advisor、AopInfrastructureBeanthis.aspectJAdvisorFactory.isAspect(beanClass)或者是否是切面(通過有沒有@Aspect註解判斷)
    3. shouldSkip(beanClass,beanName)是否需要跳過
      1. List<Advisor> candidateAdvisors = findCandidateAdvisors()找到候選的增強器(增強器就是自定義的LogAspect切面中通知方法),每一個封裝的通知方法的增強器是 InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void com.atguigu.aop.LogAspects.logStart(org.aspectj.lang.JoinPoint)],判斷每一個增強器是否是 AspectJPointcutAdvisor 型別的,是就返回true,現在都不是
  2. 然後建立bean物件後,(在初始化後)執行[AbstractAutoProxyCreator]BeanPostProcessor.postProcessAfterInitialization()
    0. 進入return wrapIfNecessary(bean, beanName, cacheKey)如有必要,就包裝,只有被代理類才會建立代理物件
    1. Object[] specificInterceptors=getAdvicesAndAdvisorsForBean(bean.getClass(),beanName,null)註釋是Create proxy if we have advice(如果我們有通知方法,請建立代理)獲取當前bean的所有增強器(通知方法),進入
      1. findEligibleAdvisors(beanClass,beanName)找到候選的所有的增強器(找哪些通知方法是需要切入當前bean方法的)
      2. 獲取到能在bean使用的增強器,如切入點表示式
      3. 給增強器排序
    2. 已經獲取到可用增強器,儲存當前bean在已增強集合中this.advisedBeans.put()
    3. 如果當前bean需要增強,建立當前bean的代理物件Object proxy = createProxy()
      1. 獲取所有增強器(通知方法)
      2. 儲存到proxyFactory
      3. 建立代理物件proxyFactory.getProxy(getProxyClassLoader()):Spring自動決定兩種方式
        • JdkDynamicAopProxy(config):jdk動態代理(需要介面);
        • ObjenesisCglibAopProxy(config):cglib的動態代理;
      4. 建立完成後得到的bean的增強代理物件
    4. 給容器中返回當前元件使用cglib增強了的代理物件
    5. 以後容器中獲取到的就是這個元件的代理物件MathCalculator(業務類)$EnhancerBySpringCGLIB,執行目標方法的時候,代理物件就會執行通知方法的流程

部分原始碼

  • 如果必要包裝

    	//必要時包裝給定的bean,即,如果它符合被代理的條件。
    	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		//...
    		// 如果我們有建議,請建立代理。
    		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    		if (specificInterceptors != DO_NOT_PROXY) {
    			// 儲存可用增強器
    			this.advisedBeans.put(cacheKey, Boolean.TRUE);
    			// 建立代理物件
    			Object proxy = createProxy(
    					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    			return proxy;
    		}
    
    		this.advisedBeans.put(cacheKey, Boolean.FALSE);
    		return bean;
    	}
    
    
  • 建立代理

    	// 建立一個CGLIB代理或JDK動態代理。
    	@Override
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    			Class<?> targetClass = config.getTargetClass();
    			if (targetClass == null) {
    				throw new AopConfigException("TargetSource cannot determine target class: " +
    						"Either an interface or a target is required for proxy creation.");
    			}
    			// 判斷是否介面
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				// 建立JDK動態代理
    				return new JdkDynamicAopProxy(config);
    			}
    			// 建立CGLIB代理
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    

⭐如此包裝?獲取攔截器鏈MethodInterceptor

如此“包裝”(將切入點增強器包裝為方法攔截器)

  接著上面程式碼執行,在上面已經獲取到被代理後的物件,現在需要把所有的增強方法轉為MethodInterceptor型別。

執行過程

  1. (被代理後的)目標方法執行:容器中儲存了元件的代理物件(cglib增強後的物件),這個物件裡面儲存了詳細資訊(比如增強器,目標物件,xxx)

    1. debug 從ioc容器中的目標方法執行開始進入下一步(或者:入-出-出-入-出-入-出-入)

    2. CglibAopProxy.intercept():攔截目標方法的執行

    3. 根據ProxyFactory物件獲取將要執行的目標方法攔截器鏈:List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)

      1. List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length)儲存所有攔截器,攔截器列表長度就是config.getAdvisors()長度,等於5。包括一個預設的ExposeInvocationInterceptor 和 4個(自定義)增強器
      2. 遍歷所有的增強器,如果是切入點的增強器,將其轉為攔截器MethodInterceptorMethodInterceptor[] interceptor = registry.getInterceptors(advisor)
        3. 將增強器轉為List<MethodInterceptor>攔截器
        1. 如果是MethodInterceptor,直接加入到集合中
        2. 如果不是,使用AdvisorAdapter將增強器轉為MethodInterceptor
          3. 轉換完成返回MethodInterceptor陣列
    4. 或者本身是Interceptor,或者由其他型別轉為MethodInterceptor,最終返回攔截器列表(此次是5個)

  2. 如果沒有攔截器鏈chain.isEmpty(),直接執行目標方法methodProxy.invoke()

    1. 如果有攔截器鏈,把需要執行的目標物件,目標方法,攔截器鏈等資訊傳入建立一個CglibMethodInvocation 物件,並呼叫 Object retVal = new CglibMethodInvocation(proxy,traget,method,args,targetClass,chain,methodProxy).proceed()繼續執行

部分原始碼

  • 代理物件剛執行時的攔截方法

    		@Override
    		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    			Object oldProxy = null;
    			boolean setProxyContext = false;
    			Class<?> targetClass = null;
    			Object target = null;
    			try {
    				if (this.advised.exposeProxy) {
    					// 在必要時使呼叫可用。
    					oldProxy = AopContext.setCurrentProxy(proxy);
    					setProxyContext = true;
    				}
    				// 可能為空。儘可能晚地減少我們“擁有”目標的時間,以防它來自池...
    				target = getTarget();
    				if (target != null) {
    					targetClass = target.getClass();
    				}
    				// 獲取攔截器鏈
    				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    				Object retVal;
    				// 檢查是否有攔截器鏈
    				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
    					// 我們可以跳過建立方法呼叫:只需直接呼叫目標即可。請注意,最終的呼叫程式必須是 InvokerInterceptor,因此我們知道它只對目標執行反射操作,並且沒有熱插拔或花哨的代理。
    					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    					retVal = methodProxy.invoke(target, argsToUse);
    				}
    				else {
    					// 我們需要建立一個方法呼叫(執行目標方法)...
    					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    				}
    				retVal = processReturnType(proxy, target, method, retVal);
    				return retVal;
    			}
    			//...
    		}
    
  • 包裝攔截器

    	@Override
    	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    			Advised config, Method method, Class<?> targetClass) {
    
    		// 這有點棘手...我們必須首先處理介紹,但我們需要保持最終列表中的秩序。
    		List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    		boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    
    		for (Advisor advisor : config.getAdvisors()) {
    			if (advisor instanceof PointcutAdvisor) {
    				// 有條件地新增它。
    				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
    				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
    					// 轉為攔截器
    					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
    					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
    					if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
    						if (mm.isRuntime()) {
    							// 在 getInterceptors()方法中建立新的物件例項不是問題,因為我們通常快取建立的鏈。
    							for (MethodInterceptor interceptor : interceptors) {
    								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
    							}
    						}
    						else {
    							interceptorList.addAll(Arrays.asList(interceptors));
    						}
    					}
    				}
    			}
    			else if (advisor instanceof IntroductionAdvisor) {
    				// ...將其餘型別都轉為攔截器
    			}
    		return interceptorList;
    	}
    

⭐喵啊?鏈式呼叫通知方法

妙啊,執行過程設計的很有趣

  所有準備都做好了(建立代理,增強器排序(倒序),增強器包裝獲取攔截器鏈),現在開始呼叫通知方法

執行過程

  1. 攔截器鏈的觸發過程:
    1. 如果沒有攔截器執行執行目標方法,或者攔截器的索引和攔截器陣列-1大小一樣(執行到了最後一個攔截器)執行目標方法
    2. 鏈式獲取每一個攔截器,攔截器執行invoke方法,每一個攔截器等待下一個攔截器執行完成返回以後再來執行。攔截器鏈的機制,保證通知方法與目標方法的執行順序

5個攔截器(1個預設+4個自定義)的執行順序

image

執行上一節最後一步,獲取到攔截器鏈後執行new CglibMethodInvocation(proxy,traget,method,args,targetClass,chain,methodProxy).proceed()proceed()方法

  1. 進入proceed()方法,先判斷currentInterceptorIndex索引的值,預設是-1。this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1 -->{return invokeJoinpoint()}
  2. 判斷是否有攔截器,如果沒有 0 - 1 = -1,正好等於預設值,沒有攔截器,就直接執行目標方法(業務)method.invoke(target,args)
  3. 判斷是否是否要執行目標方法。程式多次執行,每載入一個攔截器,就+1,直到最後一個攔截器時,this.currentInterceptorIndex=4(從-1到4),判斷4 == 5 - 1,攔截器載入完了,開始執行目標方法(業務)。
  4. 開始執行
    1. -1 == 5 - 1 不通過,表示有攔截器,執行this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex),從-1到0,獲取第0號攔截器。
    2. 第 [0] 號攔截器是預設攔截器ExposenvocationInterceptor呼叫invoke(this),引數this就是被代理目標類(業務類)CgliMethodInvocation,進入invoke(this),裡面呼叫mi[MethodInvocation].proceed()這個proceed()就是本節第(1)步的proceed(),每個攔截器都會執行
    3. 獲取索引為 [1] 的攔截器AspectJAfterThrowingAdvice前置攔截器->判斷->自增->invoke(this)->mi.proceed()
    4. 獲取索引為 [2] 的攔截器AfterReturingAdviceInterceptor返回通知攔截器->判斷->自增->invoke(this)->mi.proceed()
    5. 獲取索引為 [3] 的攔截器AspectJAfterAdvice->判斷->自增->invoke(this)->mi.proceed()
    6. 獲取索引為 [4] 的攔截器MethodBeforeAdviceInterceptor前置攔截器->判斷->自增->invoke(this){ 這裡與之前不同,因為是前置通知,所以要在目標方法執行前先執行增強方法,this.advice.before(mi.getMethod(),mi.getArguments(),mi.getThis())此時執行自定義的前置通知 }->mi.proceed()
    7. 執行完最後一個攔截器,滿足條件4 = 5 - 1,所以進入判斷執行invokeJoinpoint()利用反射執行目標方法(業務),到達底部同時準備返回
  5. 開始返回
  6. 執行完目標方法,返回到第 [3] 個攔截器AspectJAfterAdvice執行最終通知,不管是否有異常finally{ invokeAdviceMethod(getJoinPointMatch(),null,null) }
  7. 繼續返回,到第 [2] 個攔截器,AfterReturingAdviceInterceptor執行返回通知,但是目標方法拋了異常,此處程式碼將異常繼續上拋throws Throwable,所以此處不做任何執行,如果沒有異常,就正常呼叫返回通知this.advice.afterReturning()
  8. 繼續返回,到第 [1] 個攔截器,AfterReturingAdviceInterceptor執行異常通知,它可以處理異常 catch(Throwable ex){ if(){invokeAdviceMethod(getJoinPointMatch(),null,ex)} throw ex }
  9. 繼續返回,自定義增強方法執行完了,執行第 [0] 個預設攔截器
  10. 增強方法執行過程:逆向執行攔截器,到達底部執行目標方法,返回時正向執行增強方法

部分原始碼

  • 獲取攔截器後繼續方法呼叫

    	@Override
    	public Object proceed() throws Throwable {
    		//	我們從 -1 的索引開始,並提前遞增。
    		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			return invokeJoinpoint();
    		}
    
    		// 每次將索引自增
    		Object interceptorOrInterceptionAdvice =
    				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    			// 在此處評估動態方法匹配器:靜態部分將已經過評估並發現匹配。
    			InterceptorAndDynamicMethodMatcher dm =
    					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
    				return dm.interceptor.invoke(this);
    			}
    			else {
    				// 動態匹配失敗。
    				// 跳過此偵聽器並呼叫鏈中的下一個偵聽器。
    				return proceed();
    			}
    		}
    		else {
    			// 呼叫這個
    			// 它是一個攔截器,所以我們只需呼叫它:在構造此物件之前,切入點將進行靜態計算。
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}
    
  • 依次呼叫攔截器的mi.proceed()

    • 異常通知:AspectJAfterThrowingAdvice

      	@Override
      	public Object invoke(MethodInvocation mi) throws Throwable {
      		try {
      			return mi.proceed();
      		}
      		catch (Throwable ex) {
      			if (shouldInvokeOnThrowing(ex)) {
      				invokeAdviceMethod(getJoinPointMatch(), null, ex);
      			}
      			throw ex;
      		}
      	}
      
    • 返回通知:AspectJAfterReturningAdvice

      	@Override
      	public Object invoke(MethodInvocation mi) throws Throwable {
      		Object retVal = mi.proceed();
      		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
      		return retVal;
      	}
      
    • 後置通知:AspectJAfterAdvice

      	@Override
      	public Object invoke(MethodInvocation mi) throws Throwable {
      		try {
      			return mi.proceed();
      		}
      		finally {
      			invokeAdviceMethod(getJoinPointMatch(), null, null);
      		}
      	}
      
    • 前置通知:AspectJMethodBeforeAdvice

      	@Override
      	public Object invoke(MethodInvocation mi) throws Throwable {
      		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
      		return mi.proceed();
      	}
      
  • 調完攔截器之後執行目標方法

    • 滿足條件執行目標方法

      		//	我們從-1的索引和早期的增量開始。
      		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      			return invokeJoinpoint();
      		}
      
    • 然後一層層向上返回攔截器,並處理通知(除了前置通知,因為它已經在目標方法前執行了

    • 如下:它返回到異常通知攔截器,並處理異常

      	@Override
      	public Object invoke(MethodInvocation mi) throws Throwable {
      		try {
      			return mi.proceed();
      		}
      		catch (Throwable ex) {
      			if (shouldInvokeOnThrowing(ex)) {
      				//處理異常
      				invokeAdviceMethod(getJoinPointMatch(), null, ex);
      			}
      			throw ex;
      		}
      	}
      

總結

  1. @EnableAspectJAutoProxy 開啟AOP功能
  2. @EnableAspectJAutoProxy 會給容器中註冊一個元件AnnotationAwareAspectJAutoProxyCreator
  3. AnnotationAwareAspectJAutoProxyCreator是一個後置處理器
  4. 容器的建立流程:
    1. registerBeanPostProcessors()註冊後置處理器,建立AnnotationAwareAspectJAutoProxyCreator物件
    2. finishBeanFactoryInitialization()初始化剩下的單例項bean
      1. 建立業務邏輯元件和切面元件
      2. AnnotationAwareAspectJAutoProxyCreator攔截元件的建立過程
      3. 元件建立完之後,判斷元件是否需要增強
        如果是:切面的通知方法,包裝成增強器(Advisor);給業務邏輯元件建立一個代理物件(cglib);
  5. 執行目標方法:
    1. 代理物件執行目標方法
    2. CglibAopProxy.intercept()
      1. 得到目標方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor
      2. 利用攔截器的鏈式機制,依次進入每一個攔截器進行執行;
      3. 效果:
        正常執行:前置通知-》目標方法-》後置通知-》返回通知
        出現異常:前置通知-》目標方法-》後置通知-》異常通知

相關文章