Spring Aop深度解析(未完...)

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)使用
在這裏插入圖片描述
到這裏,我們大致梳理一下步驟

  1. 使用註解@EnableAspectJAutoProxy
  2. 註解中import導入組件 AspectJAutoProxyRegistrar
  3. 組件中要註冊Bean
  4. 註冊了一個什麼樣的對象呢? AnnotationAwareAspectJAutoProxyCreator.class
  5. 研究這個註解給容器中註冊的這個組件的功能?

我們仔細看一下組件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 {
類圖詳細分析:

在這裏插入圖片描述
到這裏,我們大致梳理一下步驟
這是前面的步驟:

  1. 使用註解@EnableAspectJAutoProxy
  2. 註解中import導入組件 AspectJAutoProxyRegistrar
  3. 組件中要註冊Bean
  4. 註冊了一個什麼樣的對象呢? AnnotationAwareAspectJAutoProxyCreator.class
  5. 研究這個註解給容器中註冊的這個組件的功能?
    ===========================================
    新發現:
  6. 通過類圖知道,這個組件具有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
      
    7)、把BeanPostProcessor註冊到BeanFactory中;
    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方法,每一個攔截器等待下一個攔截器執行完成返回以後再來執行;
      攔截器鏈的機制,保證通知方法與目標方法的執行順序;

總結:

1)@EnableAspectJAutoProxy 開啓AOP功能
2) @EnableAspectJAutoProxy 會給容器中註冊一個組件 AnnotationAwareAspectJAutoProxyCreator
3)AnnotationAwareAspectJAutoProxyCreator是一個後置處理器;
4)容器的創建流程:

  • 1)、registerBeanPostProcessors()註冊後置處理器;創建AnnotationAwareAspectJAutoProxyCreator對象
    2)、finishBeanFactoryInitialization()初始化剩下的單實例bean

    				1)、創建業務邏輯組件和切面組件
    				2)、AnnotationAwareAspectJAutoProxyCreator攔截組件的創建過程
    				3)、組件創建完之後,判斷組件是否需要增強
    			是:切面的通知方法,包裝成增強器(Advisor);給業務邏輯組件創建一個代理對象(cglib);
    

5)執行目標方法:</font>

  • 1)、代理對象執行目標方法
    2)、CglibAopProxy.intercept();

      			1)、得到目標方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor)
      			2)、利用攔截器的鏈式機制,依次進入每一個攔截器進行執行;
      			3)、效果:
      		正常執行:前置通知-》目標方法-》後置通知-》返回通知
      		出現異常:前置通知-》目標方法-》後置通知-》異常通知
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章