SpringBoot2 | Spring AOP 原理深度源碼分析(八)

微信公衆號:吉姆餐廳ak 學習更多源碼知識,歡迎關注。 ios

在這裏插入圖片描述


SpringBoot2 | SpringBoot啓動流程源碼分析(一)git

SpringBoot2 | SpringBoot啓動流程源碼分析(二)github

SpringBoot2 | @SpringBootApplication註解 自動化配置流程源碼分析(三)正則表達式

SpringBoot2 | SpringBoot Environment源碼分析(四)spring

SpringBoot2 | SpringBoot自定義AutoConfiguration | SpringBoot自定義starter(五)express

SpringBoot2 | SpringBoot監聽器源碼分析 | 自定義ApplicationListener(六)編程

SpringBoot2 | 條件註解@ConditionalOnBean原理源碼深度解析(七)數組

SpringBoot2 | Spring AOP 原理源碼深度剖析(八)緩存

SpringBoot2 | SpingBoot FilterRegistrationBean 註冊組件 | FilterChain 責任鏈源碼分析(九)安全

SpringBoot2 | BeanDefinition 註冊核心類 ImportBeanDefinitionRegistrar (十)

SpringBoot2 | Spring 核心擴展接口 | 核心擴展方法總結(十一)


概述

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名稱匹配,該類更加靈活。動態的匹配每個類,判斷是否能夠被代理,並尋找合適的加強類,以及生成代理類。

  • DefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator的默認實現類。能夠單獨使用,在框架中使用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:默認切面顧問,比較靈活。可自由組合切面和通知。

  • InstantiationModelAwarePointcutAdvisorImplspringboot自動裝配的顧問類型,也是最經常使用的一種顧問實現。在註解實現的切面中,全部@Aspect類,都會被解析成該對象。


advice:

在這裏插入圖片描述

  • AspectJMethodBeforeAdvice:前置通知,AspectJ中 before 屬性對應的通知(@Before標註的方法會被解析成該通知),,在切面方法執行以前執行。
  • AspectJAfterReturningAdvice:後置通知,AspectJ中 afterReturning 屬性對應的通知(@AfterReturning 標註的方法會被解析成該通知),在切面方法執行以後執行,若是有異常,則不執行。 注意:該通知與AspectJMethodBeforeAdvice對應。
  • AspectJAroundAdvice:環繞通知,AspectJ中 around 屬性對應的通知(@Around標註的方法會被解析成該通知),在切面方法執行先後執行。
  • AspectJAfterAdvice:返回通知,AspectJ中 after 屬性對應的通知(@After 標註的方法會被解析成該通知),不管是否異常都會執行。

能夠看出 Spring AOP 提供的實現方式不少,可是異曲同工。

具體使用方式已上傳至 github: github.com/admin801122…
在這裏插入圖片描述

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])));
	}
複製代碼

主要就是判斷AdvisedSupportinterfaces變量中是否設置了接口,

意思是若是一個類實現了接口,把接口設置到該方法的變量中,可是不是必定會設置到該變量中,具體設置接口的代碼以下:

能夠看到若是用默認配置也就是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的擴展接口中來建立代理類。

相關文章
相關標籤/搜索