Spring源码分析之@EnableAspectJAutoProxy注解分析

原创
2019/05/09 18:58
阅读数 1.1K

纵观整个Spring的发展历史,注解的配置正逐步替代xml的配置,到SpringBoot时代,完全可以用注解的配置替换繁杂的xml配置,例如我们需要开启AOP功能只要在代码上配置上@EnableAspectJAutoProxy。废话不多说,我们这节来分析下@EnableAspectJAutoProxy注解的背后的实现。

@EnableAspectJAutoProxy配置参数

先看下@EnableAspectJAutoProxy属性详情:

@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实现,这种方式原类必须要定义接口。

这个参数就是表示动态代理实现方式,如果值设置true,表示需要代理类都基于CGLIB来实现;默认情况下值是设置成false表示如果原类如果定义了接口则通过Proxy实现否则基于CGLIB来实现。

@Import配置

在Spring中@Import可以配置3种类型:

  • 在基于@Configuration的类上引入bean

    这个配置比较简单,直接在配置了@Configuration的类上配置@Import引入bean即可,举个例子:


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

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部