Spring AOP 原理原始碼深度剖析

方誌朋發表於2019-02-27

本文轉載於公眾號:吉姆餐廳ak

概述
AOP(Aspect-Oriented Programming) 面向切面程式設計。Spring Aop 在 Spring框架中的地位舉足輕重,主要用於實現事務、快取、安全等功能。本篇主要是對原始碼進行深度分析。

主要介紹以下三個方面:

Spring AOP 多種代理機制相關核心類介紹。
Spring Boot 中AOP註解方式原始碼分析。
Spring Boot 1.x 版本和 2.x版本 AOP 預設配置的變動。
Spring AOP 多種代理機制相關核心類介紹
先介紹一些Spring Aop中一些核心類,大致分為三類:

advisorCreator,繼承 spring ioc的擴充套件介面 beanPostProcessor,主要用來掃描獲取 advisor。
advisor:顧問的意思,封裝了spring aop中的切點和通知。
advice:通知,也就是aop中增強的方法。
對以上三類核心類對應的 UML 分別來看。

advisorCreator:


AbstractAutoProxyCreator:Spring 為Spring AOP 模組暴露的可擴充套件抽象類,也是 AOP 中最核心的抽象類。Nepxion Matrix 框架便是基於此類對AOP進行擴充套件和增強。

BeanNameAutoProxyCreator:根據指定名稱建立代理物件(阿里大名鼎鼎的連線池框架druid也基於此類做了擴充套件)。通過設定 advisor,可以對指定的 beanName 進行代理。支援模糊匹配。

AbstractAdvisorAutoProxyCreator:功能比較強大,預設掃描所有Advisor的實現類。相對於根據Bean名稱匹配,該類更加靈活。動態的匹配每一個類,判斷是否可以被代理,並尋找合適的增強類,以及生成代理類。

DefaultAdvisorAutoProxyCreator:AbstractAdvisorAutoProxyCreator的預設實現類。可以單獨使用,在框架中使用AOP,儘量不要手動建立此物件。

AspectJAwareAdvisorAutoProxyCreator:Aspectj的實現方式,也是Spring Aop中最常用的實現方式,如果用註解方式,則用其子類AnnotationAwareAspectJAutoProxyCreator。

AnnotationAwareAspectJAutoProxyCreator:目前最常用的AOP使用方式。spring aop 開啟註解方式之後,該類會掃描所有@Aspect()註釋的類,生成對應的adviosr。目前SpringBoot框架中預設支援的方式,自動配置。

advisor:


StaticMethodMatcherPointcut:靜態方法切面,抽象類。定義了一個classFilter,通過重寫getClassFilter()方法來指定切面規則。另外實現了StaticMethodMatcher介面,通過重寫matches來指定方法匹配規則。

StaticMethodMatcherPointcutAdvisor:靜態方法匹配切面顧問,同未抽象類,擴充套件了切面排序方法。

NameMatchMethodPointcut:名稱匹配切面,通過指定方法集合變數mappedNames,模糊匹配。

NameMatchMethodPointcutAdvisor:方法名稱切面顧問,內部封裝了
NameMatchMethodPointcut,通過設定方法名稱模糊匹配規則和通知來實現切面功能。

RegexpMethodPointcutAdvisor:正規表示式切面顧問,可設定多個正規表示式規則,通過內部封裝的
JdkRegexpMethodPointcut解析正規表示式。

DefaultPointcutAdvisor:預設切面顧問,比較靈活。可自由組合切面和通知。

InstantiationModelAwarePointcutAdvisorImpl:springboot自動裝配的顧問型別,也是最常用的一種顧問實現。在註解實現的切面中,所有@Aspect類,都會被解析成該物件。

advice:


AspectJMethodBeforeAdvice:前置通知,AspectJ中 before 屬性對應的通知(@Before標註的方法會被解析成該通知),,在切面方法執行之前執行。
AspectJAfterReturningAdvice:後置通知,AspectJ中 afterReturning 屬性對應的通知(@AfterReturning 標註的方法會被解析成該通知),在切面方法執行之後執行,如果有異常,則不執行。
注意:該通知與AspectJMethodBeforeAdvice對應。
AspectJAroundAdvice:環繞通知,AspectJ中 around 屬性對應的通知(@Around標註的方法會被解析成該通知),在切面方法執行前後執行。
AspectJAfterAdvice:返回通知,AspectJ中 after 屬性對應的通知(@After 標註的方法會被解析成該通知),不論是否異常都會執行。
可以看出 Spring AOP 提供的實現方式很多,但是殊途同歸。

具體使用方式已上傳至 github:
https://github.com/admin801122/springboot2-spring5-studying


Spring Boot 中AOP註解方式原始碼分析
Spring Aop使用方式很多,從上面的 API 也可以看出。本篇就基於最常用的註解實現方式,對原始碼深入分析。

@Aspect
@Component
public class LogableAspect {

    @Pointcut("@annotation(com.springboot2.spring5.springAop.aspect.Logable)")
    public void aspect() {
    }
    
    @Around("aspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        //...
        Object returnValue =  point.proceed(point.getArgs());
        //...
        return returnValue;
    }
}

這是實際專案中,使用AOP最常見的形式,基於註解實現。如今springboot大行其道,我們就從springboot中的EnableAspectJAutoProxy自動配置開始。

大致流程主要分為三個步驟:
1: 建立AnnotationAwareAspectJAutoProxyCreator物件
2: 掃描容器中的切面,建立PointcutAdvisor物件
3: 生成代理類

分別來分析以上三個步驟。

1: 建立AnnotationAwareAspectJAutoProxyCreator物件
首先來看AnnotationAwareAspectJAutoProxyCreator物件初始化的過程。springboot中,aop同樣以自動裝配的方式,所以還是要從spring.factories開始:

# Auto Configure
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
        AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
    public static class JdkDynamicAutoProxyConfiguration {

    }

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
    public static class CglibAutoProxyConfiguration {

    }

}

具體來看:
(1)該配置類的載入前提是什麼?

@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
        AnnotatedElement.class })

條件註解依賴的配置類均被引入到spring-boot-starter-aop中,只需引入該依賴即可自動裝配。
而且可以看到spring.aop.auto預設為true,並不需要手動開啟。
所以很多同學在springboot專案中使用 aop 的時候,習慣在啟動類上引入@EnableAspectJAutoProxy,其實完全不必要。保證專案中有spring-boot-starter-aop依賴即可。

(2)上述程式碼通過spring.aop.proxy-target-class變數來控制proxyTargetClass的變數,最終都會載入@EnableAspectJAutoProxy配置。
spring.aop.proxy-target-class預設為true,該變數相當關鍵,控制 spring aop 代理類的生成方式,具體後面詳細介紹。

繼續跟進EnableAspectJAutoProxy:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //註冊 AnnotationAwareAspectJAutoProxyCreator
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        //將 aop 代理方式相關的變數設定到 AopConfigUtils,建立代理類時會讀取變數
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
            @Nullable Object source) {

        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

上述程式碼可以看到註冊了一個切面相關BeanDefinition,正是上面提到的類:
AnnotationAwareAspectJAutoProxyCreator,並設定了代理方式配置變數: proxyTargetClass,預設為true。

這裡只是建立BeanDefinition,並沒有例項化和初始化該物件。那什麼時候會觸發呢?
上面的 uml 圖可以看到,該類繼承的頂層介面為 BeanPostProcessor。我們知道BeanPostProcessor實現類會提前初始化,由PostProcessorRegistrationDelegate觸發,具體細節之前部落格有提到:
SpringBoot2 | @SpringBootApplication註解 自動化配置流程原始碼分析(三)

該類又繼承BeanFactoryAware,所以在其在例項化 bean 後,會觸發setBeanFactory()方法,最終會觸發
initBeanFactory方法:

    @Override
    protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        if (this.aspectJAdvisorFactory == null) {
            //advisor 工廠類
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
        //用於建立 advisor
        this.aspectJAdvisorsBuilder =
                new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }

至此,AnnotationAwareAspectJAutoProxyCreator BeanDefinition建立完畢。

2: 掃描容器中的切面,建立PointcutAdvisor物件
在spring ioc流程載入的過程中,會觸發 beanPostProcessor 擴充套件介面,
而AnnotationAwareAspectJAutoProxyCreator又是SmartInstantiationAwareBeanPostProcessor的子類,所以該擴充套件介面正是 aop 實現的入口。

該介面的觸發在例項化 bean 之後,初始化 bean之前,具體來看:

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            //advisedBeans用於儲存不可代理的bean,如果包含直接返回
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            //判斷當前bean是否可以被代理,然後存入advisedBeans
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // Create proxy here if we have a custom TargetSource.
        // Suppresses unnecessary default instantiation of the target bean:
        // The TargetSource will handle target instances in a custom fashion.
        //到這裡說明該bean可以被代理,所以去獲取自定義目標類,如果沒有定義,則跳過。
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            //如果最終可以獲得代理類,則返回代理類,直接執行例項化後置通知方法
            return proxy;
        }

        return null;
    }

來看一下判定 bean 是否被代理的方法依據:

    @Override
    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return (super.isInfrastructureClass(beanClass) ||
                (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        //判定當前類是否有 Aspect 註解,如果有,則不能被代理
        return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
    }

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        //判定當前bean是否是 Advice、Pointcut、Advisor、AopInfrastructureBean等子類或實現類,如果是,則不能被代理
        boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass) ||
                AopInfrastructureBean.class.isAssignableFrom(beanClass);
        if (retVal && logger.isTraceEnabled()) {
            logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
        }
        return retVal;
    }

重點來看 shouldSkip方法:

    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        // TODO: Consider optimization by caching the list of the aspect names
        //獲取所有的候選顧問類 Advisor
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        for (Advisor advisor : candidateAdvisors) {
            if (advisor instanceof AspectJPointcutAdvisor &&
                    ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
                return true;
            }
        }
        return super.shouldSkip(beanClass, beanName);
    }

上述程式碼通過findCandidateAdvisors()方法來獲取所有的候選 advisor:

@Override
    protected List<Advisor> findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        //獲得 Advisor 實現類
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        //將@Aspect註解類, 解析成Advisor 
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

繼續跟進buildAspectJAdvisors方法,會觸發
ReflectiveAspectJAdvisorFactory中的getAdvisors方法:

@Override
    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        //從 aspectMetadata 中獲取 Aspect()標註的類 class物件
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //獲取Aspect()標註的類名
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        validate(aspectClass);

        // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
        // so that it will only instantiate once.
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

        List<Advisor> advisors = new LinkedList<>();
        //遍歷該類所有方法,根據方法判斷是否能獲取到對應 pointCut,如果有,則生成 advisor 物件
        for (Method method : getAdvisorMethods(aspectClass)) {
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // If it's a per target aspect, emit the dummy instantiating aspect.
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        // Find introduction fields.
        //獲取 @DeclareParents 註解修飾的屬性(並不常用)
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

繼續來看getAdvisor方法:

    @Override
    @Nullable
    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {

        validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
        //根據候選方法名,來獲取對應的 pointCut
        AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
        if (expressionPointcut == null) {
            return null;
        }
        //如果能獲取到 pointCut,則將切點表示式 expressionPointcut、當前
        物件ReflectiveAspectJAdvisorFactory、 方法名等包裝成 advisor 物件
        return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }

    protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
        //定義class物件陣列,如果方法中有以下註解中任何一種,則返回該註解
        Class<?>[] classesToLookFor = new Class<?>[] {
                Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
        for (Class<?> c : classesToLookFor) {
            AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }
        return null;
    }

InstantiationModelAwarePointcutAdvisorImpl的構造方法會觸發構造通知物件:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        //......
        //根據註解型別,匹配對應的通知型別
        switch (aspectJAnnotation.getAnnotationType()) {
            //前置通知
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //最終通知
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //後置通知
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            //異常通知
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            //環繞通知
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //切面
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        //......
    }

可以看到,根據@Aspect類中方法的註解型別,生成對應的advice,並通過通知的構造方法,將通知增強方法,切面表示式傳入到通知當中。

到這裡InstantiationModelAwarePointcutAdvisorImpl物件構造完畢。

3: 生成代理類
上面建立advisor的邏輯發生在擴充套件介面中的postProcessBeforeInstantiation,例項化之前執行,如果有自定義的TargetSource指定類,則則直接生成代理類,並直接執行初始化之後的方法postProcessAfterInitialization。這種情況使用不多,常規代理類還是在postProcessAfterInitialization中建立,也就是 IOC 最後一個擴充套件方法。

    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            //處理迴圈依賴的判斷
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        //獲取到合適的advisor,如果為空。如果不為空,則生成代理類。
        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;
    }


上述方法通過呼叫getAdvicesAndAdvisorsForBean()方法來獲取advisor,該方法最終會呼叫findEligibleAdvisors(),Eligible意為有資格的,合適的。具體來看下:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //這裡會對獲取的advisor進行篩選
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        //新增一個預設的advisor,執行時用到。
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

最終的篩選規則在AopUtils中:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        //......
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            } 
            //呼叫 canApply 方法,遍歷所有的方法進行匹配
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        //......
    }

呼叫canApply方法,遍歷被代理類的所有的方法,跟進切面表示式進行匹配,如果有一個方法匹配到,也就意味著該類會被代理。
匹配方法是藉助org.aspectj.weaver.internal.tools實現,也就是AspectJ框架中的工具類,有興趣的可以自行檢視。

重點來看一下代理生成方式:

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.");
            }
            //如果代理目標是介面或者Proxy型別,則走jdk型別
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

上述方法通過三個變數來進行篩選代理方法:

optimize:官方文件翻譯為設定代理是否應執行積極的優化,預設為false。
proxyTargetClass:這個在上面已經提到了,AopAutoConfiguration中指定,預設為true,也就是選擇使用 cglib 代理。可以看到該變數和optimize意義一樣,之所以這麼做,個人理解是為了可以在不同的場景中使用。
hasNoUserSuppliedProxyInterfaces:是否設定了實現介面。
hasNoUserSuppliedProxyInterfaces方法如下:

private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    }

主要就是判斷AdvisedSupport中interfaces變數中是否設定了介面,

意思是如果一個類實現了介面,把介面設定到該方法的變數中,但是不是一定會設定到該變數中,具體設定介面的程式碼如下:

可以看到如果用預設配置也就是proxyTargetClass為true時,只有一種情況會走jdk代理方法,就是代理類為介面型別(注意:代理類是介面型別,並不是指介面類是否實現了介面)或者代理類是Proxy型別,否則全部走cglib代理。所以,平時使用中,代理類大部分還是用cglib的方式來生成。

Spring Boot 1.x 版本和 2.x版本 AOP 預設配置的變動
配置類AopAutoConfiguration:

1.5x版本:

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)
    public static class CglibAutoProxyConfiguration {

    }

2.x版本:

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
    public static class CglibAutoProxyConfiguration {

    }

可以看到,在SpringBoot2.x中最主要的變化就是proxy-target-class預設為true,意味著類代理的時候全部走cglib代理方式,只有為介面代理時才走jdk代理(注意:這裡為介面代理,不是指代理目標類是否實現了介面)。所以,在使用springboot2.x的版本中,除了代理目標類是介面外,其餘的代理方式全部採用cglib型別。

總結
Springboot通過自動裝配AopAutoConfiguration配置類,預設自動開啟 AOP 功能。通過註冊
AnnotationAwareAspectJAutoProxyCreator類,來掃描建立所有的Advisor,再通過 Advisor在 Spring IOC的擴充套件介面中來建立代理類。

更多閱讀

[史上最簡單的 SpringCloud 教程彙總](https://blog.csdn.net/forezp/article/details/70148833)

[SpringBoot教程彙總](https://blog.csdn.net/forezp/article/details/70341818)

[Java面試題系列彙總](https://blog.csdn.net/forezp/article/details/85163411)

                                            
                                                           掃碼關注有驚喜

                                 

相關文章