Spring AOP @EnableAspectJAutoProxy 实现原理分析

介绍

@EnableAspectJAutoProxy 注解是 Spring AOP 框架提供给用户开启 AspectJ 注解支持的一个开关。将其添加到 Spring 配置类上,然后就可以在 Spring Bean 上使用 AspectJ 注解,将 bean 配置为一个 Aspect。

Spring AOP 对 AspectJ 的依赖是可选的,如果使用这个注解还需要显式的引入 aspectjweaver.jar。如果你的项目中用到了 spring-boot-starter-aop,Spring 会自动开启对 AspectJ 的支持,无需额外操作。

实现分析

《Spring AOP 与 AspectJ 》一篇中,我们提到,Spring AOP 是通过在运行时创建代理来实现的,那么这里就跟着 @EnableAspectJAutoProxy 注解的实现来进行分析。Spring @Enable* 注解的实现大多相同,可以参考《Spring 框架中的 @Enable* 注解是怎样实现的?》,这里将侧重 AOP 相关的实现部分。

注解说明

@EnableAspectJAutoProxy 源码如下。

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

	// 是否使用 CGLIB 创建代理
	boolean proxyTargetClass() default false;

	// 代理是否暴露在 ThreadLocal 中,以便通过 AopContext 获取
	boolean exposeProxy() default false;

}

注解定义了两个属性。

proxyTargetClass 属性用于指示是否优先使用 CGLIB 创建代理,Spring 内部会进行检测,即便配置为 false,如果不能使用 JDK 动态代理创建代理对象,那么 Spring 仍将选择 CGLIB,通常情况保持默认配置即可。

exposeProxy 属性用于配置是否在 ThreadLocal 中暴露代理对象,如果配置为 true,则可以在目标类的方法中使用 AopContext#currentProxy 获取代理对象。通常情况,目标类对代理类应该是无感知的,因此这里一般也保持默认配置。

@EnableAspectJAutoProxy 注解上添加了元注解 @Import,这意味着将使用 AspectJAutoProxyRegistrar 新引入一些 bean 到 Spring 的上下文中。

Bean 注册

跟踪 AspectJAutoProxyRegistrar 源码。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

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

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}

AspectJAutoProxyRegistrar 实现了接口 ImportBeanDefinitionRegistrar,主要利用 AopConfigUtils 这个工具类做了3步。

  1. 向 BeanDefinitionRegistry 注册了某个 bean。
  2. 强制配置是否使用 CGLIB 创建代理。
  3. 强制配置是否在目标方法执行前将代理对象暴露到 ThreadLocal。

后面两个步骤较为简单,先分析 AopConfigUtils。registerAspectJAnnotationAutoProxyCreatorIfNecessary 注册 bean 的逻辑,跟踪代码如下。

public abstract class AopConfigUtils {

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}

	@Nullable
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}	
}

到了这里,我们可以小结如下,@EnableAspectJAutoProxy 注解主要向 Spring 应用上下文中注册了一个类型为 AnnotationAwareAspectJAutoProxyCreator 的 bean。

自动代理创建分析

下面分析 AnnotationAwareAspectJAutoProxyCreator 如何与 IOC 容器整合创建代理的。这个类自身相对简单,实现逻辑主要在父类中。查看类图如下。
AnnotationAwareAspectJAutoProxyCreator 类图看到这里的小伙伴,想必心里一脸懵逼,这个类层次结构怎么如此深?不用过于担心,下面我会对主要的流程进行分析。

如果想创建 bean 的代理,Spring 一定会在 bean 的生命周期中处理,如果你对 Spring Bean 的生命周期不熟悉,可以先参考《Java 面试必备的 Spring Bean 生命周期总结》。Spring 提供了 InstantiationAwareBeanPostProcessor,Spring 会使用这个接口创建的 bean 取代用户定义的 bean。

通过类图可以看到 AnnotationAwareAspectJAutoProxyCreator 正是间接实现了这个接口,直接实现这个接口的父类 AbstractAutoProxyCreator 创建代理 bean 对象的代码如下。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

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

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}
		
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			// TargetSource 不为空才创建代理
			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;
	}
	
}

AbstractAutoProxyCreator 在 bean 实例化前判断是否定义了获取 TargetSource 的 TargetSourceCreator,如果是则创建代理作为 bean 实例。不过很明显,我们使用 Spring 时并不会自己定义一个 TargetSourceCreator。因此这里通常也会返回一个 null。

不过也不用担心,AbstractAutoProxyCreator 还实现了接口 SmartInstantiationAwareBeanPostProcessor,Spring 还会通过这个接口获取 bean 的早期引用,Spring AOP 通过这个接口为早期创建的 bean 创建了代理对象。跟踪源码。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
		
	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}
	
}

这里将原始 bean 进行包装,继续跟踪包装原始 bean 的 #wrapIfNecessary 方法。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
		
	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			// 给定 bean 已经被包装为代理,无需再次包装
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			// 给定 bean 无需被代理,不再包装
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			// 给定 bean 无需被代理,不再包装
			return bean;
		}

		// Create proxy if we have advice.
		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;
	}
}

#wrapIfNecessary 方法主要判断是否需要创建代理,如果需要则调用 #getAdvicesAndAdvisorsForBean 收集 Advisor,然后调用 #createProxy 方法创建代理。创建代理的逻辑比较简单,主要是使用 PointcutAdvisor 中的 Pointcut 确定要拦截的目标方法,然后将 Advisor 中的 Advice 转换为 MethodInvocation 拦截目标方法的执行。这里我们将重点放到与 AspectJ 相关的收集 Advisor 的代码。

#getAdvicesAndAdvisorsForBean 是一个抽象方法,查看子类实现如下。

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

}

这里主要调用 #findEligibleAdvisors 方法收集 Advisor,继续跟踪。

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

	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;
	}
	
}

#findEligibleAdvisors 方法主要调用 #findCandidateAdvisors 收集可以应用到目标对象 的 Advisor。而AnnotationAwareAspectJAutoProxyCreator 覆盖了 #findCandidateAdvisors 这个方法通过 AspectJ 的注解来收集 Advisor 。查看源码如下。

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
	@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}
}

aspectJAdvisorsBuilder.buildAspectJAdvisors 方法就是用来通过 AspectJ 注解收集 Advisor 的,其实现相对复杂一些,这里不再分析。

总结

到了这里,总结如下。

Spring @EnableAspectJAutoProxy 注解引入类型为 AnnotationAwareAspectJAutoProxyCreator 的 bean,这个 bean 会根据 AspectJ 的注解收集 Advisor ,通过收集的 Advisor 创建代理,底层又会把 Advisor 中的 Advice 转换为 MethodInvocation 拦截目标方法的执行,感兴趣的可以自行阅读相关源码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值