Spring源碼分析之@EnableAspectJAutoProxy註解分析

縱觀整個Spring的發展歷史,註解的配置正逐步替代xml的配置,到SpringBoot時代,徹底能夠用註解的配置替換繁雜的xml配置,例如咱們須要開啓AOP功能只要在代碼上配置上@EnableAspectJAutoProxy。廢話很少說,咱們這節來分析下@EnableAspectJAutoProxy註解的背後的實現。java

@EnableAspectJAutoProxy配置參數

先看下@EnableAspectJAutoProxy屬性詳情:spring

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	boolean proxyTargetClass() default false;

}

@EnableAspectJAutoProxy有2個配置參數proxyTargetClass和@Import,@Import是個固定配置,寫死成AspectJAutoProxyRegistrar類型,Spring在解析此註解配置時會建立AspectJAutoProxyRegistrar並調用registerBeanDefinitions方法。數組

proxyTargetClass配置

在Spring中,動態代理有2種實現方式:緩存

  • 基於CGLIB來實現框架

  • 基於Java原生的Proxy實現,這種方式原類必需要定義接口。ide

這個參數就是表示動態代理實現方式,若是值設置true,表示須要代理類都基於CGLIB來實現;默認狀況下值是設置成false表示若是原類若是定義了接口則經過Proxy實現不然基於CGLIB來實現。post

@Import配置

在Spring中@Import能夠配置3種類型:ui

  • 在基於@Configuration的類上引入beanthis

    這個配置比較簡單,直接在配置了@Configuration的類上配置@Import引入bean便可,舉個例子:lua

@Configuration
@Import(value={Bean.class})
public class Config {
 
}

以上例子就會把Bean加入到Spring容器。

  • 基於ImportSelector引入bean

若是引入類須要通過註解上的參數來決定可使用此方式。

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

AnnotationMetadata類型能夠獲取註解上的參數配置。@EnableTransactionManagement就是經過次方式配置,來看下他配置:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	boolean proxyTargetClass() default false;

	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;

}
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}

以上例子就是經過獲取AnnotationMetadata裏的配置來決定引入那些bean。

  • 基於ImportBeanDefinitionRegistrar引入bean

在解析@Import配置傳入AnnotationMetadata和BeanDefinitionRegistry兩個參數到registerBeanDefinitions方法。

這種方式更加靈活,能夠直接經過BeanDefinitionRegistry將本身想要的bean加入到Spring容器。

@EnableAspectJAutoProxy註解就是經過這種方式將代理建立器AnnotationAwareAspectJAutoProxyCreator加入到到Spring容器。

@EnableAspectJAutoProxy背後的實現

AOP的組成

Spring的AOP的實現是基於Aspectj項目的註解及註解的解析實現,其核心的組件仍是Spring本身的實現包括Advisor(切面),Pointcut(切點),Advice(加強)。

  • 1.什麼是切面

切面就是將非邏輯代碼抽離到一個指定位置,讓編寫邏輯代碼的人感受不到非邏輯代碼的存在,實際執行卻能讓非邏輯代碼發揮效果,說白了就是切點和加強的組合

  • 2.什麼是切點

切點是對邏輯代碼加強的位置,好比在邏輯代碼執行前加強。

  • 2.什麼是加強

加強就是對切點位置的具體的實現,好比在邏輯代碼執行前記錄操做日誌,而記錄操做日誌這個操做的具體實現就是加強

舉個例子:

@Aspect    //聲明一個切面
public class LogAspect {
 @After(value="@annotation(com.just.spring4.ch1.aop.TestAction)")   //經過@after註解聲明一個建言,並使用@Pointcut定義的切點
    public void after(JoinPoint joinPoint){
  System.out.println("記錄日誌");
    }
}

@Aspect註解的LogAspect類就是切面。

@After註解的並匹配上的方法就是切點。

而System.out.println("記錄日誌")輸出就是加強。

Advisor

Advisor是一個接口它的實現表明了切面,切面包含了切點和加強,先看下Advisor接口的定義:

public interface Advisor {
	Advice getAdvice();
	boolean isPerInstance();
}

getAdvice()方法返回了一個加強組件Advice。isPerInstance()方法在Spring框架中暫未被使用。

根據上面定義Advisor接口其實少了一個切點組件返回,因此Advisor通常不會被直接實現,Spring定義了2個接口來擴展Advisor的實現:

  • IntroductionAdvisor
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
	ClassFilter getClassFilter();
	void validateInterfaces() throws IllegalArgumentException;

}

getClassFilter()方法返回了一個切點組件ClassFilter,它是Class的切點,來匹配Class是否知足條件來加強實現。validateInterfaces()方法的功能是Advice是否實現指定的接口。

IntroductionAdvisor主要作Class的匹配而不關心Method匹配狀況。

  • PointcutAdvisor
public interface PointcutAdvisor extends Advisor {
	Pointcut getPointcut();
}

getPointcut()返回了一個切點組件Pointcut,Pointcut和ClassFilter不一樣Pointcut包裝了ClassFilter和MethodMatcher,也就是說Pointcut即匹配Class也匹配Method,2者同時知足狀況下才能加強實現。

Pointcut

Pointcut表示切入的位置,在Spring中Pointcut接口是作一個匹配的功能包括Class和Method的匹配,只有匹配上才能作進一步加強。Pointcut接口以下

public interface Pointcut {
	ClassFilter getClassFilter();
	MethodMatcher getMethodMatcher();
	Pointcut TRUE = TruePointcut.INSTANCE;
}

getClassFilter()返回的ClassFilter是Class匹配器,getMethodMatcher()返回的MethodMatcher是Class匹配器,但不必定每一個匹配器都會有做用,舉個例子:

  • AnnotationMatchingPointcut

AnnotationMatchingPointcut是註解的切面類,它能夠匹配Class上的註解或者Method的上的註解,看下它的構造方法:

public AnnotationMatchingPointcut(
			Class<? extends Annotation> classAnnotationType, Class<? extends Annotation> methodAnnotationType) {

		Assert.isTrue((classAnnotationType != null || methodAnnotationType != null),
				"Either Class annotation type or Method annotation type needs to be specified (or both)");

		if (classAnnotationType != null) {
			this.classFilter = new AnnotationClassFilter(classAnnotationType);
		}
		else {
			this.classFilter = ClassFilter.TRUE;
		}

		if (methodAnnotationType != null) {
			this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType);
		}
		else {
			this.methodMatcher = MethodMatcher.TRUE;
		}
	}

構造方法有2個參數classAnnotationType表示Class上的註解,methodAnnotationType表示Method上的註解,2參數必傳一個若是其中一個不傳,會設置ClassFilter.TRUE或MethodMatcher.TRUE表示全類型匹配,這種匹配通常對整個類或單個方法進行加強。

  • StaticMethodMatcherPointcut

StaticMethodMatcherPointcut是抽象類,自己是作一種規範,其規範必需要實現MethodMatcher的匹配邏輯來匹配Method,ClassFilter可不實現,不實現會全類型匹配。

  • ComposablePointcut

Advice

Advice是加強的接口,Spring提供不少加強的實現。舉例以下:

  • AspectJMethodBeforeAdvice

    對應AspectJ中的@Before註解的加強實現,在方法執行前加強。

  • AspectJAfterAdvice

    對應AspectJ中的@After註解的加強實現,在方法執行後加強。

  • AspectJAfterReturningAdvice

    對應AspectJ中的@AfterReturning註解的加強實現,在方法執行後並獲取返回值,能夠根據返回值作加強。

  • AspectJAfterThrowingAdvice

    對應AspectJ中的@AfterThrowing註解的加強實現,在方法執行後並獲取執行錯誤信息,能夠根據錯誤信息作加強。

  • AspectJAroundAdvice

    對應AspectJ中的@Around註解的加強實現,在方法執行先後均可以作加強。

如何實現AOP

實現AOP關鍵有2個類AspectJAutoProxyRegistrar和AnnotationAwareAspectJAutoProxyCreator。

利用AspectJAutoProxyRegistrar註冊建立代理類

前面提到Spring經過@EnableAspectJAutoProxy的@Import配置在解析註解時會建立AspectJAutoProxyRegistrar並調用registerBeanDefinitions方法。

看下它的實現

public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAJAutoProxy.getBoolean("proxyTargetClass")) {
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
	}

代碼的功能分2部分,第1部分將AnnotationAwareAspectJAutoProxyCreator類型包裝成BeanDefinition註冊到Spring容器。第2部分將proxyTargetClass的配置設置到此BeanDefinition裏。

這裏AnnotationAwareAspectJAutoProxyCreator類是實現AOP的核心後面詳細說明。

利用AnnotationAwareAspectJAutoProxyCreator建立代理對象

先看下AnnotationAwareAspectJAutoProxyCreator的結構

能夠看到AnnotationAwareAspectJAutoProxyCreator實現了BeanPostProcessor接口,這個接口作什麼用的呢?

先看下這個接口的定義:

public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

在Spring容器中BeanDefinition被建立成bean後會依次調用實現了這個接口的類裏postProcessBeforeInitialization和postProcessAfterInitialization,這2個方法做用能夠對bean作修改就是能夠對bean作代理或對bean作屬性修改,而2這個方法先後執行分別對應bean初始化先後:

好了,咱們知道AnnotationAwareAspectJAutoProxyCreator就是利用BeanPostProcessor來作代理。

AnnotationAwareAspectJAutoProxyCreator代理是放在postProcessAfterInitialization方法裏處理,因此對代理對象自己的初始化不受影響。

來看下它的實現:

@Override
	public Object postProcessAfterInitialization(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;
	}

先查看earlyProxyReferences緩存判斷是否已經建立代理。(在循環引用狀況下會調用getEarlyBeanReference提早建立代理),若是還未建立調用wrapIfNecessary方法去建立代理,咱們看下其實現:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (beanName != null && 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;
		}
		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;
	}

前2個if先判斷是否已經建立再判斷是否須要建立代理對象, 若是須要則調用getAdvicesAndAdvisorsForBean方法獲取切面Advisor,再根據Advisor調用createProxy來建立代理對下,咱們分兩部分來說。

解析和獲取Advisor

getAdvicesAndAdvisorsForBean方法的實如今AbstractAdvisorAutoProxyCreator類中實現,委託給了findEligibleAdvisors方法去獲取,看下它的實現:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

第一步調用findCandidateAdvisors方法先獲取註冊到Spring容器中的Advisor,在獲取並解析註解了@Aspect的bean中的全部Advisor。

@Override
	protected List<Advisor> findCandidateAdvisors() {
		List<Advisor> advisors = super.findCandidateAdvisors();
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		return advisors;
	}

this.aspectJAdvisorsBuilder.buildAspectJAdvisors()這塊代碼就是利用了aspectj來解析註解了@Aspect。

第二步調用findAdvisorsThatCanApply來過濾Advisor,Advisor(切點)包含了Pointcut(切點)和Advice(加強),findAdvisorsThatCanApply方法的過濾就是利用Advisor中Pointcut匹配Class或Method來達到過濾的目的。

第三步調用extendAdvisors方法,extendAdvisors在AnnotationAwareAspectJAutoProxyCreator做用就是在全部的advisors節點最前面插入一個Advisor(有advisors節點前提下),此Advisor比較特殊它的Pointcut是全類型匹配的(匹配全部Class和Method),它主要功能是在於它的Advice(加強),它的Advice實現是ExposeInvocationInterceptor類,看類的名稱就知道,對外暴露的類,就是全部Advice調用鏈的第一環,ExposeInvocationInterceptor做用就是將調用信息存在ThreadLocal實現的上下文信息裏,供調用鏈後續的Advice獲取使用,能夠看下它實現:

第四步若是存在advisors節點則調用sortAdvisors對其排序,這排序規則是根據Advisor裏的order字段排序,固然若是存在第三步所說的特殊Advice它會排在最前面。

回到wrapIfNecessary方法獲取到advisors接下去就是建立代理了。

建立代理對象

建立代理對象調用的createProxy方法,來看下它的實現:

protected Object createProxy(
			Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		for (Advisor advisor : advisors) {
			proxyFactory.addAdvisor(advisor);
		}

proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}

代理對象的建立主要依託於代理工廠ProxyFactory類來建立,看下它的繼承關係:

它和AnnotationAwareAspectJAutoProxyCreator都是繼承了ProxyConfig類。

createProxy方法第一步先經過ProxyConfig裏的copyFrom方法將AnnotationAwareAspectJAutoProxyCreator裏的配置拷貝至ProxyFactory。

第二步從新設置proxyTargetClass,Java的Proxy代理能對象的前提是此對象必須實現了接口,這步若是原先proxyTargetClass設置false,須要先判斷其是否實現了接口而且其接口非InitializingBean,DisposableBean,Aware,Spring自帶接口。

第三步設置Advisors,而且凍結設置使後面不能修改Advisors。

最後調用ProxyFactory裏的getProxy方法去代理對象。

ProxyFactory裏代理的是有2種:

  • 基於CGLIB來實現

  • 基於Java原生的Proxy實現

主要取決於proxyTargetClass參數。

不管是CGLIB建立代理的CglibAopProxy仍是Java原生的Proxy實現建立JdkDynamicAopProxy都是基於接口AopProxy:

public interface AopProxy {
 
	Object getProxy();
 
	Object getProxy(ClassLoader classLoader);

}

AopProxy有2個方法,基於默認ClassLoader建立代理和基於用戶自定義建立。

以JdkDynamicAopProxy實現爲例看下實現。

JdkDynamicAopProxy實現了InvocationHandler,也就是代理方法的調用,會分發到自己。

看下其getProxy方法若是建立代理:

@Override
	public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

先獲取須要代理的接口,而後標記equals或hashCode方法是是否被覆蓋,供調用時用,最後建立代理對象。

調用流程
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		...

		try {
			...
			
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}
			
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			if (chain.isEmpty()) {
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				retVal = invocation.proceed();
			}

			...
			
			return retVal;
		}
		...
	}

先獲取代理的advice(加強器)數組(這裏命名chain),若是advice直接調用被代理對象的方法,不然調用invocation.proceed()方法。invocation.proceed()的調用過程先是鏈式調用advice,最後執行其被代理對象的方法。看下proceed的實現:

public Object proceed() throws Throwable {
		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);
		}
	}

此方法的advice的鏈式調用的原理是遞歸調用:

proceed() ->  invoke(this) -> proceed() ->...->invoke(this)->proceed()->invokeJoinpoint()

每作一次代碼的加強,currentInterceptorIndex指針加1,直到全部的advice被調用完成,才執行其被代理對象的方法。

相關文章
相關標籤/搜索