Spring AOP 源码解析:注解式切面增强机制
IoC 和 AOP 被称为 Spring 两大基础模块,支撑着上层扩展的实现和运行。虽然 AOP 同样建立在 IoC 的实现基础之上,但是作为对 OOP(Object-Oriented Programing) 的补充,AOP(Aspect-Oriented Programming) 在程序设计领域拥有其不可替代的适用场景和地位。Spring AOP 作为 AOP 思想的实现,被誉为 Spring 框架的基础模块也算是实至名归。Spring 在 1.0 版本的时候就引入了对 AOP 的支持,并且随着版本的迭代逐渐提供了基于 XML 配置、注解,以及 schema 配置的使用方式,考虑到实际开发中使用注解配置的方式相对较多,所以本文主要分析注解式 AOP 的实现和运行机制。
注解式 AOP 示例
首先我们还是通过一个简单的示例演示一下注解式 AOP 的具体使用。假设我们声明了一个 IService 接口,并提供了相应的实现类 ServiceImpl,如下:
public interface IService {
void sayHello();
void sayHelloTo(String name);
void sayByebye();
void sayByebyeTo(String name);
}
@Service
public class ServiceImpl implements IService {
@Override
public void sayHello() {
this.sayHelloTo("zhenchao");
}
@Override
public void sayHelloTo(String name) {
System.out.println("hello, " + name);
}
@Override
public void sayByebye() {
this.sayByebyeTo("zhenchao");
}
@Override
public void sayByebyeTo(String name) {
System.out.println("byebye, " + name);
}
}
现在我们希望借助 Spring AOP 实现对方法调用的打点功能。首先我们需要定义一个切面:
@Aspect
@Component
public class MetricAspect {
@Before("execution(* sayHello*(..))")
public void beforeMetrics4sayHello(JoinPoint point) {
System.out.println("[BEFORE] metrics for method: " + point.getSignature().getName());
}
@Around("execution(* say*(..))")
public Object aroundMetrics4say(ProceedingJoinPoint point) throws Throwable {
System.out.println("[AROUND] before metrics for method: " + point.getSignature().getName());
Object obj = point.proceed();
System.out.println("[AROUND] after metrics for method: " + point.getSignature().getName());
return obj;
}
@After("execution(* sayByebye*(..))")
public void afterMetrics4sayByebye(JoinPoint point) {
System.out.println("[AFTER] metrics for method: " + point.getSignature().getName());
}
}
通过 @Aspect
注解标记 MetricAspect 是一个切面,通过注解 @Before
、@After
,以及 @Around
,我们在切面中定义了相应的前置、后置,以及环绕增强。然后我们需要在 XML 配置中添加一行如下配置以启用注解式 AOP:
<aop:aspectj-autoproxy/>
现在,我们就算大功告成了。
当然,上面的实现只是注解式 AOP 使用的一个简单示例,并没有覆盖所有的特性。对于 Spring AOP 特性的介绍不属于本文的范畴,不过我们还是会在下面分析源码的过程中进行针对性的介绍。
注解式 AOP 实现机制
下面从启用注解式 AOP 的那一行配置切入,即 <aop:aspectj-autoproxy/>
标签。前面在分析 Spring IoC 实现的文章中,曾专门分析过 Spring 默认标签和自定义标签的解析过程。对于一个标签而言,除了标签的定义,还需要有对应的标签的解析器,并在 Spring 启动时将标签及其解析器注册到 Spring 容器中。标签 <aop:aspectj-autoproxy />
的注册过程由 AopNamespaceHandler#init
方法实现:
// 注册 标签及其解析器
this.registerBeanDefinitionParser(“aspectj-autoproxy”, new AspectJAutoProxyBeanDefinitionParser());
AspectJAutoProxyBeanDefinitionParser 类是标签 <aop:aspectj-autoproxy />
的解析器,该类实现了 BeanDefinitionParser 接口,并实现了 BeanDefinitionParser#parse
接口方法,属于标准的标签解析器定义。Spring 容器在启动时会调用 AspectJAutoProxyBeanDefinitionParser#parse
方法解析标签,实现如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册标签解析器,默认使用 AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 解析 <aop:include /> 子标签,记录到 BeanDefinition 到 includePatterns 属性中
this.extendBeanDefinition(element, parserContext);
return null;
}
该方法做了两件事情:注册标签解析器和处理 <aop:include />
子标签。本文我们重点来看标签解析器的注册过程,即 >
AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
方法:
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
// 1. 注册或更新代理创建器 ProxyCreator 的 BeanDefinition 对象
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2. 获取并处理标签的 proxy-target-class 和 expose-proxy 属性
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3. 注册组件,并发布事件通知
registerComponentIfNecessary(beanDefinition, parserContext);
}
我们在代码注释中标明了该方法所做的 3 件事情,其中 1 和 2 是我们分析的关键,首先来看 1 过程所做的事情:
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果名为 org.springframework.aop.config.internalAutoProxyCreator 的 bean 已经在册
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 已经在册的 ProxyCreator 与当前期望的类型不一致,则依据优先级进行选择
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
// 选择优先级高的 ProxyCreator 更新注册
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 没有对应在册的 ProxyCreator,注册一个新的
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
上述实现的逻辑还是挺简单的,即注册一个名为 org.springframework.aop.config.internalAutoProxyCreator
的 BeanDefinition,我们称之为代理创建器(ProxyCreator)。这里使用的默认实现为 AnnotationAwareAspectJAutoProxyCreator 类,如果存在多个候选实现,则选择优先级最高的进行注册。
接下来看一下过程 2,这一步主要是用来解析标签 <aop:aspectj-autoproxy/>
的 proxy-target-class
和 expose-proxy
属性配置,由 AopNamespaceUtils#useClassProxyingIfNecessary
方法实现:
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
/*
* 获取并处理 proxy-target-class 属性:
* - false 表示使用 java 原生动态代理
* - true 表示使用 CGLib 动态
*
* 但是对于一些没有接口实现的类来说,即使设置为 false 也会使用 CGlib 进行代理
*/
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
// 为之前注册的 ProxyCreator 添加一个名为 proxyTargetClass 的属性,值为 true
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
/*
* 获取并处理 expose-proxy 标签,实现对于内部方法调用的 AOP 增强
*/
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
// 为之前注册的 ProxyCreator 添加一个名为 exposeProxy 的属性,值为 true
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
其中 proxy-target-class
属性用来配置是否使用 CGLib 代理,而 expose-proxy
属性则用来配置是否对内部方法调用启用 AOP 增强。属性 proxy-target-class
的作用大家应该都比较熟悉,下面介绍一下 expose-prox