死磕Spring之AOP篇 – Spring AOP注解驱动与XML配置

该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读。

Spring 版本:5.1.14.RELEASE

在开始阅读 Spring AOP 源码之前,需要对 Spring IoC 有一定的了解,可查看我的 《死磕Spring之IoC篇 – 文章导读》 这一系列文章

了解 AOP 相关术语,可先查看 《Spring AOP 常见面试题) 》 这篇文章

该系列其他文章请查看:《死磕 Spring 之 AOP 篇 – 文章导读》

通过前面关于 Spring AOP 的所有文章,我们对 Spring AOP 的整个 AOP 实现逻辑进行了比较详细的分析,例如 Spring AOP 的自动代理,JDK 动态代理或 CGLIB 动态代理两种方式创建的代理对象的拦截处理过程等内容都有讲到。本文将会分析 Spring AOP 的注解驱动,如何引入 AOP 模块,包括如何处理 Spring AOP 的 XML 配置。

在 Spring AOP 中可以通过 @EnableAspectJAutoProxy 注解驱动整个 AOP 模块,我们先一起来看看这个注解。

@EnableAspectJAutoProxy 注解

org.springframework.context.annotation.EnableAspectJAutoProxy,开启 Spring AOP 整个模块的注解

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

	/**
	 * 是否开启类代理,也就是是否开启 CGLIB 动态代理
	 */
	boolean proxyTargetClass() default false;

	/**
	 * 是否需要暴露代理对象
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;
}

该注解上面有一个 @Import 注解,它的 valueAspectJAutoProxyRegistrar.class 类。

这里先讲一下 @Import 注解的原理,在 Spring IoC 初始化完 BeanFactory 后会有一个 BeanDefinitionRegistryPostProcessor 对其进行后置处理,对配置类(例如 @Configuration 注解标注的 Bean)进行处理,如果这个 BeanDefinition 包含 @Import 注解,则获取注解的值,进行下面的处理:

  • 如果是一个 ImportSelector 对象,则调用其 String[] selectImports(AnnotationMetadata) 方法获取需要导入的 Bean 的名称
  • 否则,如果是一个 ImportBeanDefinitionRegistrar 对象,先保存起来,在后面调用其 registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry) 方法,支持注册相关 Bean
  • 否则,会注册这个 Bean

关于 @Import 注解不熟悉的小伙伴查看我的另一篇 《死磕Spring之IoC篇 – @Bean 等注解的实现原理》 文章

所以说 @EnableAspectJAutoProxy 注解需要标注在能够被 Spring 扫描的类上面,例如 @Configuration 标注的类。其中 AspectJAutoProxyRegistrar 就是 ImportBeanDefinitionRegistrar 的实现类,我们一起来看看。

AspectJAutoProxyRegistrar

org.springframework.context.annotation.AspectJAutoProxyRegistrar,在 @EnableAspectJAutoProxy 注解中被导入

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

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

		// <1> 注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		// <2> 获取 @EnableAspectJAutoProxy 注解的配置信息
		AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		// <3> 如果注解配置信息不为空,则根据配置设置 AnnotationAwareAspectJAutoProxyCreator 的属性
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				// 设置 `proxyTargetClass` 为 `true`(开启类代理,也就是开启 CGLIB 动态代理)
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				// 设置 `exposeProxy` 为 `true`(需要暴露代理对象,也就是在 Advice 或者被拦截的方法中可以通过 AopContext 获取代理对象)
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
}

可以看到它注册 BeanDefinition 的过程如下:

  1. 通过 AopConfigUtils 注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
  2. 获取 @EnableAspectJAutoProxy 注解的配置信息
  3. 如果注解配置信息不为空,则根据配置设置 AnnotationAwareAspectJAutoProxyCreator 的属性
    • 如果 proxyTargetClasstrue,则进行设置(开启类代理,也就是开启 CGLIB 动态代理)
    • 如果 exposeProxytrue,则进行设置(需要暴露代理对象,也就是在 Advice 或者被拦截的方法中可以通过 AopContext 获取代理对象)

可以看到会注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象,是不是很熟悉,就是在前面文章讲到的自动代理对象,那么就开启了 Spring AOP 自动代理,也就是开启了 Spring AOP 整个模块。

AopConfigUtils

org.springframework.aop.config.AopConfigUtils,AOP 工具类

构造函数

public abstract class AopConfigUtils {

	/**
	 * The bean name of the internally managed auto-proxy creator.
	 */
	public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
			"org.springframework.aop.config.internalAutoProxyCreator";

	/**
	 * Stores the auto proxy creator classes in escalation order.
	 */
	private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);

	static {
		// Set up the escalation list...
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
	}
}

上面定义了 AspectJAwareAdvisorAutoProxyCreator 几种子类的优先级,排在后面优先级越高

registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法

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

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {

    // 注册 AnnotationAwareAspectJAutoProxyCreator Bean
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // <1> 如果 `org.springframework.aop.config.internalAutoProxyCreator` 已注册
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        // <1.1> 获取对应的 BeanDefinition 对象
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        // <1.2> 如果已注册的 `internalAutoProxyCreator` 和入参的 Class 不相等,说明可能是继承关系
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            // <1.2.1> 获取已注册的 `internalAutoProxyCreator` 的优先级
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            // <1.2.2> 获取需要注册的 `internalAutoProxyCreator` 的优先级
            int requiredPriority = findPriorityForClass(cls);
            // InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator
            // 三者都是 AbstractAutoProxyCreator 自动代理对象的子类
            if (currentPriority < requiredPriority) {
                // <1.2.3> 如果需要注册的优先级更高,那取代已注册的 Class 对象
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        // <1.3> 因为已注册,则返回 `null`
        return null;
    }

    // <2> 没有注册,则创建一个 RootBeanDefinition 对象进行注册
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    // <3> 设置来源
    beanDefinition.setSource(source);
    // <4> 设置为最高优先级
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    // <5> 设置角色为**ROLE_INFRASTRUCTURE**,表示是 Spring 框架内部的 Bean
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // <6> 注册自动代理的 Bean,名称为 `org.springframework.aop.config.internalAutoProxyCreator`
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    // <7> 返回刚注册的 RootBeanDefinition 对象
    return beanDefinition;
}

可以看到会注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理 Bean,过程如下:

  1. 如果 org.springframework.aop.config.internalAutoProxyCreator 已注册
    1. 获取对应的 BeanDefinition 对象
    2. 如果已注册的 internalAutoProxyCreator 和入参的 Class 不相等,说明可能是继承关系
      1. 获取已注册的 internalAutoProxyCreator 的优先级
      2. 获取需要注册的 internalAutoProxyCreator 的优先级
      3. 如果需要注册的优先级更高,那取代已注册的 Class 对象,InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator
    3. 否则,因为已注册,则返回 null
  2. 否则,没有注册,则创建一个 RootBeanDefinition 对象进行注册
  3. 设置来源
  4. 设置为最高优先级
  5. 设置角色为ROLE_INFRASTRUCTURE,表示是 Spring 框架内部的 Bean
  6. 注册自动代理的 Bean,名称为 org.springframework.aop.config.internalAutoProxyCreator
  7. 返回刚注册的 RootBeanDefinition 对象

整个过程很简单,如果已注册自动代理对象,则判断当前需要注册的是否优先级更高,如果更高则修改其对应的 Class 名称;如果没有注册,那么注册这个代理对象,设置优先级最高。

————————————

AOP XML 配置解析过程

再开始之前对于 Spring XML 配置文件不熟悉的小伙伴可以看看我的 《死磕Spring之IoC篇 – 解析自定义标签(XML 文件)》 这篇文章。在 Spring 中对于非默认命名空间的标签需要通过指定的 NamespaceHandler 来处理,在 Spring 的 XML 配置文件中都是在 <beans /> 标签内定义数据,需要指定 http://www.springframework.org/schema/beans 作为命名空间,那么对于 http://www.springframework.org/schema/aop 就需要指定 NamespaceHandler 来处理。我们来看到 spring-aop 模块下的 META-INF/spring.handlers 配置文件:

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

Spring AOP 相关的标签需要 AopNamespaceHandler 进行处理

AopNamespaceHandler

org.springframework.aop.config.AopNamespaceHandler,继承 NamespaceHandlerSupport 抽象类,Spring AOP 命名空间下的标签处理器

public class AopNamespaceHandler extends NamespaceHandlerSupport {

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		// <aop:config /> 标签的解析器
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		// <aop:aspectj-autoproxy /> 标签的解析器
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		// <aop:scoped-proxy /> 标签的解析器
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

public abstract class NamespaceHandlerSupport implements NamespaceHandler {

	private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
    
    private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
    
    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
        this.parsers.put(elementName, parser);
    }
    
    protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
		this.decorators.put(elementName, dec);
	}
}

在这个 NamespaceHandler 的 init() 初始化方法中,会往 parsers 中注册几个标签解析器或者装饰器:

  • <aop:config />:ConfigBeanDefinitionParser
  • <aop:aspectj-autoproxy />:AspectJAutoProxyBeanDefinitionParser
  • <aop:scoped-proxy />:ScopedProxyBeanDefinitionDecorator

继续看到 NamespaceHandlerSupport 这个方法:

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // <1> 获得元素对应的 BeanDefinitionParser 对象
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    // <2> 执行解析
    return (parser != null ? parser.parse(element, parserContext) : null);
}
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 获得元素名
    String localName = parserContext.getDelegate().getLocalName(element);
    // 获得 BeanDefinitionParser 对象
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
                "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
}

@Override
@Nullable
public BeanDefinitionHolder decorate(
        Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
    // 根据标签名获取 BeanDefinitionDecorator 对象
    BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
    return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
}
@Nullable
private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
    BeanDefinitionDecorator decorator = null;
    // 获得元素名
    String localName = parserContext.getDelegate().getLocalName(node);
    if (node instanceof Element) {
        decorator = this.decorators.get(localName);
    }
    else if (node instanceof Attr) {
        decorator = this.attributeDecorators.get(localName);
    }
    else {
        parserContext.getReaderContext().fatal(
                "Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
    }
    if (decorator == null) {
        parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
                (node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
    }
    return decorator;
}

会根据标签名称找到对应的 BeanDefinitionParser 解析器进行解析,那么现在思路清晰了,上面不同的标签对应着不同的 BeanDefinitionParser 或者 BeanDefinitionDecorator,我们来看看是怎么处理的。

<aop:aspectj-autoproxy />

<beans>
    <aop:aspectj-autoproxy proxy-target-class="false" expose-proxy="false" />
</beans>

这个标签的作用和 @EnableAspectJAutoProxy 注解相同,开启整个 Spring AOP 模块,原理也相同,注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象

AspectJAutoProxyBeanDefinitionParser

org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser<aop:aspectj-autoproxy /> 标签对应 BeanDefinitionParse 解析器

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 解析 <aop:aspectj-autoproxy /> 标签
		// 注册 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
		// 过程和 @EnableAspectJAutoProxy
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		// 解析 <aop:include /> 子标签,用于指定需要开启代理的路径
		extendBeanDefinition(element, parserContext);
		return null;
	}

	private void extendBeanDefinition(Element element, ParserContext parserContext) {
		BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
		if (element.hasChildNodes()) {
			addIncludePatterns(element, parserContext, beanDef);
		}
	}

	private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
		ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
		NodeList childNodes = element.getChildNodes();
		for (int i = 0; i < childNodes.getLength(); i++) {
			Node node = childNodes.item(i);
			if (node instanceof Element) {
				Element includeElement = (Element) node;
				TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
				valueHolder.setSource(parserContext.extractSource(includeElement));
				includePatterns.add(valueHolder);
			}
		}
		if (!includePatterns.isEmpty()) {
			includePatterns.setSource(parserContext.extractSource(element));
			beanDef.getPropertyValues().add("includePatterns", includePatterns);
		}
	}
}

<aop:aspectj-autoproxy /> 标签的解析过程先通过 AopNamespaceUtils 工具类注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象,然后继续解析 <aop:include /> 子标签,用于指定需要开启代理的路径

AopNamespaceUtils

org.springframework.aop.config.AopNamespaceUtils,Spring AOP XML 配置文件解析工具类

public abstract class AopNamespaceUtils {

	public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
	private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";

	public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {

		// <1> 注册 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		// <2> 则根据 <aop:aspectj-autoproxy /> 标签的配置设置 AnnotationAwareAspectJAutoProxyCreator 的属性
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		// <3> 将注册的 BeanDefinition 也放入 `parserContext` 上下文中
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

	private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
		// 如果 <aop:aspectj-autoproxy /> 标签不为空
		if (sourceElement != null) {
			boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
			if (proxyTargetClass) {
				// 设置 `proxyTargetClass` 为 `true`(开启类代理,也就是开启 CGLIB 动态代理)
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
			if (exposeProxy) {
				// 设置 `exposeProxy` 为 `true`(需要暴露代理对象,也就是在 Advice 或者被拦截的方法中可以通过 AopContext 获取代理对象)
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

	private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
		if (beanDefinition != null) {
			parserContext.registerComponent(
					new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
		}
	}
}

注册 AnnotationAwareAspectJAutoProxyCreator 自动代理对象的过程和 @EnableAspectJAutoProxy 注解类型,这里不再做讲述

<aop:scoped-proxy />

<beans>
    <bean id="echoService" class="org.geekbang.thinking.in.spring.aop.overview.DefaultEchoService" >
    	<aop:scoped-proxy />
	</bean>
</beans>

<aop:scoped-proxy /> 标签需要定义在 <bean /> 中,用来装饰这个 Bean,会生成一个 ScopedProxyFactoryBean 类型的 RootBeanDefinition 对象并注册。ScopedProxyFactoryBean 是一个 FactoryBean,在其 getObject() 方法中返回的是一个代理对象。也就是说 <aop:scoped-proxy /> 标签可以将一个 Bean 进行 AOP 代理。

ScopedProxyBeanDefinitionDecorator

org.springframework.aop.config.ScopedProxyBeanDefinitionDecorator<aop:scoped-proxy /> 标签对应的 BeanDefinitionDecorator 装饰器

class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {

	private static final String PROXY_TARGET_CLASS = "proxy-target-class";

	@Override
	public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
		boolean proxyTargetClass = true;
		if (node instanceof Element) {
			Element ele = (Element) node;
			if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
				proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
			}
		}

		// Register the original bean definition as it will be referenced by the scoped proxy
		// and is relevant for tooling (validation, navigation).
		// 创建一个 ScopedProxyFactoryBean 类型的 RootBeanDefinition 对象并注册
		// ScopedProxyFactoryBean 用于装饰 `definition`,进行 AOP 代理
		BeanDefinitionHolder holder = ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
		String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
		parserContext.getReaderContext().fireComponentRegistered(
				new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
		return holder;
	}
}

<aop:config />

<beans>
    <aop:aspectj-autoproxy/>
    <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
    <aop:config>
        <aop:pointcut id="anyPublicStringMethod" expression="execution(public String *(..))"/>
        <aop:advisor advice-ref="echoServiceMethodInterceptor" pointcut-ref="anyPublicStringMethod" />
        <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig">
            <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/>
            <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/>
            <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        </aop:aspect>
    </aop:config>
</beans>

<aop:config> 标签内可以定义 AspectJ 切面的相关信息,例如 Pointcut、Advisor 和 Advice;同时也会注册一个 Spring AOP 自动代理对象(如果有必要的话),不过 是注册 AspectJAwareAdvisorAutoProxyCreator,只能解析处理 Spring IoC 中 Advisor 类型的 Bean,无法解析 @AspectJ 等相关注解,所以我们最好使用 <aop:aspectj-autoproxy/> 标签来驱动 Spring AOP 模块。

ConfigBeanDefinitionParser

org.springframework.aop.config.ConfigBeanDefinitionParser<aop:config /> 标签对应的 BeanDefinitionParser 解析器,我们来看到它的 parse(..) 方法

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);

    // <1> 解析 <aop:config /> 标签
    // 注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话),设置为优先级最高
    // 过程和 @EnableAspectJAutoProxy、<aop:aspectj-autoproxy /> 差不多
    configureAutoProxyCreator(parserContext, element);

    // <2> 获取 <aop:config /> 的子标签,遍历进行处理
    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
        // 获取子标签的名称
        String localName = parserContext.getDelegate().getLocalName(elt);
        if (POINTCUT.equals(localName)) {
            // <2.1> 处理 <aop:pointcut /> 子标签,解析出 AspectJExpressionPointcut 对象并注册
            parsePointcut(elt, parserContext);
        }
        else if (ADVISOR.equals(localName)) {
            // <2.2> 处理 <aop:advisor /> 子标签,解析出 DefaultBeanFactoryPointcutAdvisor 对象并注册,了指定 Advice 和 Pointcut(如果有)
            parseAdvisor(elt, parserContext);
        }
        else if (ASPECT.equals(localName)) {
            // <2.3> 处理 <aop:aspectj /> 子标签,解析出所有的 AspectJPointcutAdvisor 对象并注册,里面包含了 Advice 对象和对应的 Pointcut 对象
            // 同时存在 Pointcut 配置,也会解析出 AspectJExpressionPointcut 对象并注册
            parseAspect(elt, parserContext);
        }
    }

    // <3> 将 `parserContext` 上下文中已注册的 BeanDefinition 合并到上面 `compositeDef` 中(暂时忽略)
    parserContext.popAndRegisterContainingComponent();
    return null;
}

该方法的处理过程如下:

  1. 解析 <aop:config /> 标签,注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话),设置为优先级最高

    private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
        // 注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话)
        AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
    }
    

    过程和 @EnableAspectJAutoProxy<aop:aspectj-autoproxy /> 的解析过程差不多,这里不再进行展述

  2. 获取 <aop:config /> 的子标签,遍历进行处理

    1. 调用 parsePointcut(..) 方法,处理 <aop:pointcut /> 子标签,解析出 AspectJExpressionPointcut 对象并注册
    2. 调用 parseAdvisor(..) 方法,处理 <aop:advisor /> 子标签,解析出 DefaultBeanFactoryPointcutAdvisor 对象并注册,了指定 Advice 和 Pointcut(如果有)
    3. 调用 parseAspect(..) 方法,处理 <aop:aspectj /> 子标签,解析出所有的 AspectJPointcutAdvisor 对象并注册,里面包含了 Advice 对象和对应的 Pointcut 对象;同时存在 Pointcut 配置,也会解析出 AspectJExpressionPointcut 对象并注册

我们依次来看看你上面三种子标签的处理过程

<aop:pointcut />

<beans>
    <aop:aspectj-autoproxy/>
    <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
    <aop:config>
        <aop:pointcut id="anyPublicStringMethod" expression="execution(public String *(..))"/>
    </aop:config>
</beans>

处理过程在 parsePointcut(..) 方法中,如下:

// ConfigBeanDefinitionParser.java
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
    // <1> 获取 <aop:pointcut /> 标签的 `id` 和 `expression` 配置
    String id = pointcutElement.getAttribute(ID);
    String expression = pointcutElement.getAttribute(EXPRESSION);

    AbstractBeanDefinition pointcutDefinition = null;

    try {
        this.parseState.push(new PointcutEntry(id));
        // <2> 创建一个 AspectJExpressionPointcut 类型的 RootBeanDefinition 对象
        pointcutDefinition = createPointcutDefinition(expression);
        // <3> 设置来源
        pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

        String pointcutBeanName = id;
        // <4> 注册这个 AspectJExpressionPointcut 对象
        if (StringUtils.hasText(pointcutBeanName)) {
            // <4.1> 如果 `id` 配置不为空,则取其作为名称
            parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
        }
        else {
            // <4.2> 否则,自动生成名称,也就是取 `className`
            pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
        }

        // <5> 将注册的 BeanDefinition 包装成 ComponentDefinition 放入 `parserContext` 上下文中,暂时忽略
        parserContext.registerComponent(
                new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
    }
    finally {
        this.parseState.pop();
    }

    return pointcutDefinition;
}

解析过程大致如下:

  1. 获取 <aop:pointcut /> 标签的 idexpression 配置

  2. 根据 expression 表达式创建一个 AspectJExpressionPointcut 类型的 RootBeanDefinition 对象,如下:

    protected AbstractBeanDefinition createPointcutDefinition(String expression) {
        // <1> 创建一个 AspectJExpressionPointcut 类型的 RootBeanDefinition 对象
        RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
        // <2> 设置为原型模式,需要保证每次获取到的 Pointcut 对象都是新的,防止在某些地方被修改而影响到其他地方
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        // <3> 设置为是 Spring 内部合成的
        beanDefinition.setSynthetic(true);
        // <4> 添加 `expression` 属性值
        beanDefinition.getPropertyValues().add(EXPRESSION, expression);
        // <5> 返回刚创建的 RootBeanDefinition 对象
        return beanDefinition;
    }
    
  3. 设置来源

  4. 注册这个 AspectJExpressionPointcut 对象

    1. 如果 id 配置不为空,则取其作为名称
    2. 否则,自动生成名称,也就是取 className
  5. 将注册的 BeanDefinition 包装成 ComponentDefinition 放入 parserContext 上下文中,暂时忽略

<aop:advisor />

<beans>
    <aop:aspectj-autoproxy/>
    <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
    <aop:config>
        <aop:pointcut id="anyPublicStringMethod" expression="execution(public String *(..))"/>
        <aop:advisor advice-ref="echoServiceMethodInterceptor" pointcut-ref="anyPublicStringMethod" />
    </aop:config>
</beans>

处理过程在 parseAdvisor(..) 方法中,如下:

// ConfigBeanDefinitionParser.java
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
    // <1> 解析 <aop:advisor /> 标签
    // 创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象,并指定了 Advice
    AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
    // <2> 获取 `id` 属性
    String id = advisorElement.getAttribute(ID);

    try {
        this.parseState.push(new AdvisorEntry(id));
        String advisorBeanName = id;
        // <3> 注册第 `1` 步创建的 RootBeanDefinition
        if (StringUtils.hasText(advisorBeanName)) {
            // <3.1> 如果 `id` 不为空,则取其作为名称
            parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
        }
        else {
            // <3.2> 否则,生成一个名称,也就是 `className`
            advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
        }

        // <4> 获取这个 Advisor 对应的 Pointcut(也许就是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的名称)
        Object pointcut = parsePointcutProperty(advisorElement, parserContext);
        // <4.1> 如果是 AspectJExpressionPointcut
        if (pointcut instanceof BeanDefinition) {
            // 第 `1` 步创建的 RootBeanDefinition 添加 `pointcut` 属性,指向这个 AspectJExpressionPointcut
            advisorDef.getPropertyValues().add(POINTCUT, pointcut);
            parserContext.registerComponent(
                    new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
        }
        // <4.2> 否则,如果是一个引用的 Pointcut 的名称
        else if (pointcut instanceof String) {
            // 第 `1` 步创建的 RootBeanDefinition 添加 `pointcut` 属性,指向这个名称对应的引用
            advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
            parserContext.registerComponent(
                    new AdvisorComponentDefinition(advisorBeanName, advisorDef));
        }
    }
    finally {
        this.parseState.pop();
    }
}

解析过程大致如下:

  1. 解析 <aop:advisor /> 标签,创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象,并指定了 Advice

    private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
        // <1> 创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
        // <2> 设置来源
        advisorDefinition.setSource(parserContext.extractSource(advisorElement));
    
        // <3> 获取 `advice-ref` 属性配置,必须配置一个对应的 Advice
        String adviceRef = advisorElement.getAttribute(ADVICE_REF);
        if (!StringUtils.hasText(adviceRef)) {
            parserContext.getReaderContext().error(
                    "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
        }
        else {
            // <4> 将 `advice-ref` 添加至 `adviceBeanName` 属性,也就是指向这个 Advice 引用
            advisorDefinition.getPropertyValues().add(
                    ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
        }
    
        // <5> 根据 `order` 配置为 RootBeanDefinition 设置优先级
        if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
            advisorDefinition.getPropertyValues().add(
                    ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
        }
    
        // <6> 返回刚创建的 RootBeanDefinition
        return advisorDefinition;
    }
    
  2. 获取 id 属性

  3. 注册第 1 步创建的 RootBeanDefinition

    1. 如果 id 不为空,则取其作为名称
    2. 否则,生成一个名称,也就是 className
  4. 获取这个 Advisor 对应的 Pointcut(也许就是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的名称)

    1. 如果是 AspectJExpressionPointcut,第 1 步创建的 RootBeanDefinition 添加 pointcut 属性,指向这个 AspectJExpressionPointcut
    2. 否则,如果是一个引用的 Pointcut 的名称,第 1 步创建的 RootBeanDefinition 添加 pointcut 属性,指向这个名称对应的引用

<aop:aspect />

<beans>
    <aop:aspectj-autoproxy/>
    <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
    <aop:config>
        <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig">
            <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/>
            <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/>
            <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
            <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
        </aop:aspect>
    </aop:config>
</beans>

处理过程在 parseAspect(..) 方法中,如下:

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    // <1> 获取 `id` 和 `ref` 属性
    String aspectId = aspectElement.getAttribute(ID);
    String aspectName = aspectElement.getAttribute(REF);

    try {
        this.parseState.push(new AspectEntry(aspectId, aspectName));
        // <2> 定义两个集合 `beanDefinitions`、`beanReferences`
        // 解析出来的 BeanDefinition
        List<BeanDefinition> beanDefinitions = new ArrayList<>();
        // 需要引用的 Bean
        List<BeanReference> beanReferences = new ArrayList<>();

        // <3> 获取所有的 <aop:declare-parents /> 子标签,遍历进行处理
        List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
        for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
            Element declareParentsElement = declareParents.get(i);
            // <3.1> 解析 <aop:declare-parents /> 子标签
            // 解析出 DeclareParentsAdvisor 对象,添加至 `beanDefinitions`
            beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
        }

        // We have to parse "advice" and all the advice kinds in one loop, to get the
        // ordering semantics right.
        // <4> 获取 <aop:aspectj /> 所有的子节点,遍历进行处理
        NodeList nodeList = aspectElement.getChildNodes();
        boolean adviceFoundAlready = false;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            // <4.1> 如果是 <aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing /> 标签,则进行处理
            if (isAdviceNode(node, parserContext)) {
                // <4.2> 如果第一次进来,那么就是配置了 Advice,则 `ref` 必须指定一个 Bean,因为这些 Advice 的 `method` 需要从这个 Bean 中获取
                if (!adviceFoundAlready) {
                    adviceFoundAlready = true;
                    if (!StringUtils.hasText(aspectName)) {
                        parserContext.getReaderContext().error(
                                "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                                aspectElement, this.parseState.snapshot());
                        return;
                    }
                    // <4.2.1> 往 `beanReferences` 添加需要引用的 Bean
                    beanReferences.add(new RuntimeBeanReference(aspectName));
                }
                // <4.3> 根据 Advice 标签进行解析
                // 创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册
                AbstractBeanDefinition advisorDefinition = parseAdvice(
                        aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                // <4.4> 添加至 `beanDefinitions` 中
                beanDefinitions.add(advisorDefinition);
            }
        }

        // <5> 将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中
        // 并放入 `parserContext` 上下文中,暂时忽略
        AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
                aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
        parserContext.pushContainingComponent(aspectComponentDefinition);

        // <6> 获取所有的 <aop:pointcut /> 子标签,进行遍历处理
        List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
        for (Element pointcutElement : pointcuts) {
            // <6.1> 解析出 AspectJExpressionPointcut 对象并注册
            parsePointcut(pointcutElement, parserContext);
        }
        parserContext.popAndRegisterContainingComponent();
    } finally {
        this.parseState.pop();
    }
}

解析过程大致如下:

  1. 获取 idref 属性
  2. 定义两个集合 beanDefinitionsbeanReferences,分别保存解析出来的 BeanDefinition 和需要引用的 Bean
  3. 获取所有的 <aop:declare-parents /> 子标签,遍历进行处理
    1. 解析 <aop:declare-parents /> 子标签,解析出 DeclareParentsAdvisor 对象并注册,添加至 beanDefinitions
  4. 获取 <aop:aspectj /> 所有的子节点,遍历进行处理
    1. 如果是 <aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing /> 标签,则进行处理
    2. 如果第一次进来,那么就是配置了 Advice,则 ref 必须指定一个 Bean,因为这些 Advice 的 method 需要从这个 Bean 中获取
      1. beanReferences 添加需要引用的 Bean
    3. 根据 Advice 标签进行解析,创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册
    4. 添加至 beanDefinitions
  5. 将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中,并放入 parserContext 上下文中,暂时忽略
  6. 获取所有的 <aop:pointcut /> 子标签,进行遍历处理
    1. 解析出 AspectJExpressionPointcut 对象并注册,前面已经讲过了

上面第 4.3 会解析相关 Advice 标签,我们一起来看看

<aop:aspect /> 的 Advice 子标签

private AbstractBeanDefinition parseAdvice(
        String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
        List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    try {
        this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

        // create the method factory bean
        // <1> 创建 MethodLocatingFactoryBean 类型的 RootBeanDefinition
        // 因为通过标签配置的 Advice 对应的方法在其他 Bean 中,那么可以借助于 FactoryBean 来进行创建
        RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
        // <1.1> 获取 `targetBeanName` 和 `method` 并进行设置
        methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
        methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
        // <1.2> 设置这个 Bean 是由 Spring 内部合成的
        methodDefinition.setSynthetic(true);

        // create instance factory definition
        // <2> 创建一个 SimpleBeanFactoryAwareAspectInstanceFactory 类型的 RootBeanDefinition
        RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
        // <2.1> 设置了 AspectJ 对应的 名称,用于获取这个 AspectJ 的实例对象
        aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
        // <2.2> 设置这个 Bean 是由 Spring 内部合成的
        aspectFactoryDef.setSynthetic(true);

        // register the pointcut
        // <3> 创建一个 Advice 对象,包含了对应的 Pointcut
        AbstractBeanDefinition adviceDef = createAdviceDefinition(
                adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
                beanDefinitions, beanReferences);

        // configure the advisor
        // <4> 创建一个 AspectJPointcutAdvisor 类型的 RootBeanDefinition 对象,用于包装上面创建的 Advice
        // Spring AOP 中的 Advice 都是放入 Advisor “容器” 中
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
        // <4.1> 设置来源
        advisorDefinition.setSource(parserContext.extractSource(adviceElement));
        // <4.2> 将上面创建的 Advice 对象作为构造器入参
        advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
        // <4.3> 设置 `order` 优先级
        if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
            advisorDefinition.getPropertyValues().add(
                    ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
        }

        // register the final advisor
        // <5> 注册这个 AspectJPointcutAdvisor,自动生成名字
        parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

        // <6> 返回这个已注册的 AspectJPointcutAdvisor
        return advisorDefinition;
    }
    finally {
        this.parseState.pop();
    }
}

处理过程大致如下:

  1. 创建 MethodLocatingFactoryBean 类型的 RootBeanDefinition,因为通过标签配置的 Advice 对应的方法在其他 Bean 中,那么可以借助于 FactoryBean 来进行创建

    1. 获取 targetBeanNamemethod 并进行设置
    2. 设置这个 Bean 是由 Spring 内部合成的
  2. 创建一个 SimpleBeanFactoryAwareAspectInstanceFactory 类型的 RootBeanDefinition

    1. 设置了 AspectJ 对应的 名称,用于获取这个 AspectJ 的实例对象
    2. 设置这个 Bean 是由 Spring 内部合成的
  3. 创建一个 Advice 对象,包含了对应的 Pointcut

    private AbstractBeanDefinition createAdviceDefinition(
            Element adviceElement, ParserContext parserContext, String aspectName, int order,
            RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
            List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    
        // <1> 根据 Advice 标签创建对应的 Advice
        // <aop:before /> -> AspectJMethodBeforeAdvice
        // <aop:after /> -> AspectJAfterAdvice
        // <aop:after-returning /> -> AspectJAfterReturningAdvice
        // <aop:after-throwing /> -> AspectJAfterThrowingAdvice
        // <aop:around /> -> AspectJAroundAdvice
        RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
        // <1.1> 设置来源
        adviceDefinition.setSource(parserContext.extractSource(adviceElement));
        // <1.2> 设置引用的 AspectJ 的名称
        adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
        // <1.3> 设置优先级
        adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    
        if (adviceElement.hasAttribute(RETURNING)) {
            adviceDefinition.getPropertyValues().add(
                    RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
        }
        if (adviceElement.hasAttribute(THROWING)) {
            adviceDefinition.getPropertyValues().add(
                    THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
        }
        if (adviceElement.hasAttribute(ARG_NAMES)) {
            adviceDefinition.getPropertyValues().add(
                    ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
        }
    
        // <2> 获取 Advice 的构造器参数对象 `cav`
        // 设置 1. 引用的方法、2. Pointcut(也许是引用的 Pointcut 的名称)、3. 引用的方法所属 AspectJ 对象
        // 你点进这些 Advice 类型的对象中看看构造方法就知道怎么回事,例如:AspectJMethodBeforeAdvice
        ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
        // <2.1> 往 `cav` 添加 Advice 对应的方法作为入参
        cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
    
        // <2.2> 解析出对应的 Pointcut 对象(可能是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的一个运行时引用对象)
        Object pointcut = parsePointcutProperty(adviceElement, parserContext);
        // <2.2.1> 如果是 AspectJExpressionPointcut
        if (pointcut instanceof BeanDefinition) {
            // 往 `cav` 添加 `pointcut` 入参
            cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
            // 添加至 `beanDefinitions`
            beanDefinitions.add((BeanDefinition) pointcut);
        }
        // <2.2.2> 否则,如果是 引用的 Pointcut
        else if (pointcut instanceof String) {
            // 根据引用的 Pointcut 的名称生成一个引用对象
            RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
            // 往构 `cav` 添加 `pointcut` 入参
            cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
            // 添加至 `pointcutRef`
            beanReferences.add(pointcutRef);
        }
    
        // <2.3> 往 `cav` 添加 Advice 对应的方法所在 Bean 作为入参
        cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    
        // <3> 返回为 Advice 创建的 RootBeanDefinition 对象
        return adviceDefinition;
    }
    
    private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
        String elementName = parserContext.getDelegate().getLocalName(adviceElement);
        if (BEFORE.equals(elementName)) {
            return AspectJMethodBeforeAdvice.class;
        }
        else if (AFTER.equals(elementName)) {
            return AspectJAfterAdvice.class;
        }
        else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
            return AspectJAfterReturningAdvice.class;
        }
        else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
            return AspectJAfterThrowingAdvice.class;
        }
        else if (AROUND.equals(elementName)) {
            return AspectJAroundAdvice.class;
        }
        else {
            throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
        }
    }
    
  4. 创建一个 AspectJPointcutAdvisor 类型的 RootBeanDefinition 对象,用于包装上面创建的 Advice,Spring AOP 中的 Advice 都是放入 Advisor “容器” 中

  5. 注册这个 AspectJPointcutAdvisor,自动生成名字

  6. 返回这个已注册的 AspectJPointcutAdvisor

————————————

Spring Boot 注解驱动

在 Spring Boot 中使用 Spring AOP 我们通常会这样进行 Maven 引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.3.5.RELEASE</version>
</dependency>

上面这个依赖内部会引入这样两个依赖:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.2.10.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.6</version>
  <scope>compile</scope>
</dependency>

引入相关依赖后,同样可以使用 @EnableAspectJAutoProxy 注解来驱动整个 Spring AOP 依赖,不过在 Spring AOP 中你不需要显示地使用这个注解,因为在 spring-boot-autoconfigure 中,有一个 AOP 自动配置类,我们一起来看看

AopAutoConfiguration

org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,Spring Boot 中的 AOP 自动配置类

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {

	}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {

	}
}

可以看到这个 @Configuration 配置类中有两个条件注解,都是基于 @Conditional 扩展的注解,如下:

  • @ConditionalOnClass 注解:value 中的所有 Class 对象在当前 JVM 必须存在才会注入当前配置类;

    因为你通过 Spring Boot 引入了 aspectjweaver 这个包,AspectAdviceAnnotatedElement 三个 Class 对象也就存在了,而 EnableAspectJAutoProxy 这个注解本身就存在 Spring 中,所以这个注解是满足条件的

  • @ConditionalOnProperty 注解:指定的配置为 true 才会注入当前配置类

    这个注解会判断 spring.aop.auto 是否为 true,没有配置默认为 true,所以这个注解也是满足条件的

所以得到的结论就是,当你引入 spring-boot-starter-aop 依赖后,Spring Boot 中会注入 AopAutoConfiguration 这个配置类,在这个配置类中的静态内部类使用了 @EnableAspectJAutoProxy 这个注解,那么也就会注册 Spring AOP 自动代理对象。

总结

通过本文,我们可以知道 @EnableAspectJAutoProxy 这个模块驱动注解会借助 @Import 注解注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象,也就开启了 Spring AOP 自动代理,驱动了整个 Spring AOP 模块。

除了注解的方式,Spring 一样也支持 <aop:aspectj-autoproxy /> XML 配置的方式注册一个自动代理对象,驱动整个 Spring AOP 模块;也有 <aop:scoped-proxy /> 标签支持装饰某个 Bean,使其进行 AOP 代理。当然,Spring 也支持 <aop:config /> 标签配置 AspectJ 切面的相关内容,包括 Poincut、Advice 和 Advisor 等配置。

同时,在使用 Spring Boot 中引入 spring-boot-starter-aop 依赖后,不需要显示地使用 @EnableAspectJAutoProxy 注解来开启 Spring AOP 的自动代理,因为在 spring-boot-autoconfigure 中,有一个 AopAutoConfiguration 自动配置类,会使用这个注解驱动了整个 Spring AOP 模块。

文章来自全网采集,如有侵权请联系我们删除,aarocn@163.com
愉悦心情 » 死磕Spring之AOP篇 – Spring AOP注解驱动与XML配置

提供最优质的资源集合

立即查看 了解详情