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被调用完成,才执行其被代理对象的方法。

相关文章
相关标签/搜索