该系列文章是本人在学习 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 整个模块的注解

  1. /**
  2. * @since 3.1
  3. */
  4. @Target(ElementType.TYPE)
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Documented
  7. @Import(AspectJAutoProxyRegistrar.class)
  8. public @interface EnableAspectJAutoProxy {
  9. /**
  10. * 是否开启类代理,也就是是否开启 CGLIB 动态代理
  11. */
  12. boolean proxyTargetClass() default false;
  13. /**
  14. * 是否需要暴露代理对象
  15. * @since 4.3.1
  16. */
  17. boolean exposeProxy() default false;
  18. }

该注解上面有一个 @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 注解中被导入

  1. class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  4. // <1> 注册一个 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
  5. AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
  6. // <2> 获取 @EnableAspectJAutoProxy 注解的配置信息
  7. AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
  8. // <3> 如果注解配置信息不为空,则根据配置设置 AnnotationAwareAspectJAutoProxyCreator 的属性
  9. if (enableAspectJAutoProxy != null) {
  10. if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
  11. // 设置 `proxyTargetClass` 为 `true`(开启类代理,也就是开启 CGLIB 动态代理)
  12. AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  13. }
  14. if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
  15. // 设置 `exposeProxy` 为 `true`(需要暴露代理对象,也就是在 Advice 或者被拦截的方法中可以通过 AopContext 获取代理对象)
  16. AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
  17. }
  18. }
  19. }
  20. }

可以看到它注册 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 工具类

构造函数

  1. public abstract class AopConfigUtils {
  2. /**
  3. * The bean name of the internally managed auto-proxy creator.
  4. */
  5. public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
  6. "org.springframework.aop.config.internalAutoProxyCreator";
  7. /**
  8. * Stores the auto proxy creator classes in escalation order.
  9. */
  10. private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<>(3);
  11. static {
  12. // Set up the escalation list...
  13. APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
  14. APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
  15. APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
  16. }
  17. }

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

registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法

  1. @Nullable
  2. public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
  3. return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
  4. }
  5. @Nullable
  6. public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  7. BeanDefinitionRegistry registry, @Nullable Object source) {
  8. // 注册 AnnotationAwareAspectJAutoProxyCreator Bean
  9. return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  10. }
  11. @Nullable
  12. private static BeanDefinition registerOrEscalateApcAsRequired(
  13. Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
  14. Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
  15. // <1> 如果 `org.springframework.aop.config.internalAutoProxyCreator` 已注册
  16. if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
  17. // <1.1> 获取对应的 BeanDefinition 对象
  18. BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
  19. // <1.2> 如果已注册的 `internalAutoProxyCreator` 和入参的 Class 不相等,说明可能是继承关系
  20. if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
  21. // <1.2.1> 获取已注册的 `internalAutoProxyCreator` 的优先级
  22. int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
  23. // <1.2.2> 获取需要注册的 `internalAutoProxyCreator` 的优先级
  24. int requiredPriority = findPriorityForClass(cls);
  25. // InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator
  26. // 三者都是 AbstractAutoProxyCreator 自动代理对象的子类
  27. if (currentPriority < requiredPriority) {
  28. // <1.2.3> 如果需要注册的优先级更高,那取代已注册的 Class 对象
  29. apcDefinition.setBeanClassName(cls.getName());
  30. }
  31. }
  32. // <1.3> 因为已注册,则返回 `null`
  33. return null;
  34. }
  35. // <2> 没有注册,则创建一个 RootBeanDefinition 对象进行注册
  36. RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
  37. // <3> 设置来源
  38. beanDefinition.setSource(source);
  39. // <4> 设置为最高优先级
  40. beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
  41. // <5> 设置角色为**ROLE_INFRASTRUCTURE**,表示是 Spring 框架内部的 Bean
  42. beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  43. // <6> 注册自动代理的 Bean,名称为 `org.springframework.aop.config.internalAutoProxyCreator`
  44. registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
  45. // <7> 返回刚注册的 RootBeanDefinition 对象
  46. return beanDefinition;
  47. }

可以看到会注册一个 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 配置文件:

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

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

AopNamespaceHandler

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

  1. public class AopNamespaceHandler extends NamespaceHandlerSupport {
  2. /**
  3. * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
  4. * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
  5. * and '{@code scoped-proxy}' tags.
  6. */
  7. @Override
  8. public void init() {
  9. // In 2.0 XSD as well as in 2.1 XSD.
  10. // <aop:config /> 标签的解析器
  11. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
  12. // <aop:aspectj-autoproxy /> 标签的解析器
  13. registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
  14. // <aop:scoped-proxy /> 标签的解析器
  15. registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
  16. // Only in 2.0 XSD: moved to context namespace as of 2.1
  17. registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  18. }
  19. }
  20. public abstract class NamespaceHandlerSupport implements NamespaceHandler {
  21. private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
  22. private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
  23. protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
  24. this.parsers.put(elementName, parser);
  25. }
  26. protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
  27. this.decorators.put(elementName, dec);
  28. }
  29. }

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

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

继续看到 NamespaceHandlerSupport 这个方法:

  1. @Override
  2. @Nullable
  3. public BeanDefinition parse(Element element, ParserContext parserContext) {
  4. // <1> 获得元素对应的 BeanDefinitionParser 对象
  5. BeanDefinitionParser parser = findParserForElement(element, parserContext);
  6. // <2> 执行解析
  7. return (parser != null ? parser.parse(element, parserContext) : null);
  8. }
  9. @Nullable
  10. private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
  11. // 获得元素名
  12. String localName = parserContext.getDelegate().getLocalName(element);
  13. // 获得 BeanDefinitionParser 对象
  14. BeanDefinitionParser parser = this.parsers.get(localName);
  15. if (parser == null) {
  16. parserContext.getReaderContext().fatal(
  17. "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
  18. }
  19. return parser;
  20. }
  21. @Override
  22. @Nullable
  23. public BeanDefinitionHolder decorate(
  24. Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
  25. // 根据标签名获取 BeanDefinitionDecorator 对象
  26. BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
  27. return (decorator != null ? decorator.decorate(node, definition, parserContext) : null);
  28. }
  29. @Nullable
  30. private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
  31. BeanDefinitionDecorator decorator = null;
  32. // 获得元素名
  33. String localName = parserContext.getDelegate().getLocalName(node);
  34. if (node instanceof Element) {
  35. decorator = this.decorators.get(localName);
  36. }
  37. else if (node instanceof Attr) {
  38. decorator = this.attributeDecorators.get(localName);
  39. }
  40. else {
  41. parserContext.getReaderContext().fatal(
  42. "Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
  43. }
  44. if (decorator == null) {
  45. parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
  46. (node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
  47. }
  48. return decorator;
  49. }

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

<aop:aspectj-autoproxy />

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

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

AspectJAutoProxyBeanDefinitionParser

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

  1. class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
  2. @Override
  3. @Nullable
  4. public BeanDefinition parse(Element element, ParserContext parserContext) {
  5. // 解析 <aop:aspectj-autoproxy /> 标签
  6. // 注册 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
  7. // 过程和 @EnableAspectJAutoProxy
  8. AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
  9. // 解析 <aop:include /> 子标签,用于指定需要开启代理的路径
  10. extendBeanDefinition(element, parserContext);
  11. return null;
  12. }
  13. private void extendBeanDefinition(Element element, ParserContext parserContext) {
  14. BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
  15. if (element.hasChildNodes()) {
  16. addIncludePatterns(element, parserContext, beanDef);
  17. }
  18. }
  19. private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
  20. ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
  21. NodeList childNodes = element.getChildNodes();
  22. for (int i = 0; i < childNodes.getLength(); i++) {
  23. Node node = childNodes.item(i);
  24. if (node instanceof Element) {
  25. Element includeElement = (Element) node;
  26. TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
  27. valueHolder.setSource(parserContext.extractSource(includeElement));
  28. includePatterns.add(valueHolder);
  29. }
  30. }
  31. if (!includePatterns.isEmpty()) {
  32. includePatterns.setSource(parserContext.extractSource(element));
  33. beanDef.getPropertyValues().add("includePatterns", includePatterns);
  34. }
  35. }
  36. }

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

AopNamespaceUtils

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

  1. public abstract class AopNamespaceUtils {
  2. public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
  3. private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
  4. public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  5. ParserContext parserContext, Element sourceElement) {
  6. // <1> 注册 AnnotationAwareAspectJAutoProxyCreator 自动代理对象(如果没有注册的话),设置为优先级最高
  7. BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  8. parserContext.getRegistry(), parserContext.extractSource(sourceElement));
  9. // <2> 则根据 <aop:aspectj-autoproxy /> 标签的配置设置 AnnotationAwareAspectJAutoProxyCreator 的属性
  10. useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
  11. // <3> 将注册的 BeanDefinition 也放入 `parserContext` 上下文中
  12. registerComponentIfNecessary(beanDefinition, parserContext);
  13. }
  14. private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
  15. // 如果 <aop:aspectj-autoproxy /> 标签不为空
  16. if (sourceElement != null) {
  17. boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
  18. if (proxyTargetClass) {
  19. // 设置 `proxyTargetClass` 为 `true`(开启类代理,也就是开启 CGLIB 动态代理)
  20. AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
  21. }
  22. boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
  23. if (exposeProxy) {
  24. // 设置 `exposeProxy` 为 `true`(需要暴露代理对象,也就是在 Advice 或者被拦截的方法中可以通过 AopContext 获取代理对象)
  25. AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
  26. }
  27. }
  28. }
  29. private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
  30. if (beanDefinition != null) {
  31. parserContext.registerComponent(
  32. new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
  33. }
  34. }
  35. }

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

<aop:scoped-proxy />

  1. <beans>
  2. <bean id="echoService" class="org.geekbang.thinking.in.spring.aop.overview.DefaultEchoService" >
  3. <aop:scoped-proxy />
  4. </bean>
  5. </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 装饰器

  1. class ScopedProxyBeanDefinitionDecorator implements BeanDefinitionDecorator {
  2. private static final String PROXY_TARGET_CLASS = "proxy-target-class";
  3. @Override
  4. public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
  5. boolean proxyTargetClass = true;
  6. if (node instanceof Element) {
  7. Element ele = (Element) node;
  8. if (ele.hasAttribute(PROXY_TARGET_CLASS)) {
  9. proxyTargetClass = Boolean.valueOf(ele.getAttribute(PROXY_TARGET_CLASS));
  10. }
  11. }
  12. // Register the original bean definition as it will be referenced by the scoped proxy
  13. // and is relevant for tooling (validation, navigation).
  14. // 创建一个 ScopedProxyFactoryBean 类型的 RootBeanDefinition 对象并注册
  15. // ScopedProxyFactoryBean 用于装饰 `definition`,进行 AOP 代理
  16. BeanDefinitionHolder holder = ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);
  17. String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());
  18. parserContext.getReaderContext().fireComponentRegistered(
  19. new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));
  20. return holder;
  21. }
  22. }

<aop:config />

  1. <beans>
  2. <aop:aspectj-autoproxy/>
  3. <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
  4. <aop:config>
  5. <aop:pointcut id="anyPublicStringMethod" expression="execution(public String *(..))"/>
  6. <aop:advisor advice-ref="echoServiceMethodInterceptor" pointcut-ref="anyPublicStringMethod" />
  7. <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig">
  8. <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/>
  9. <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  10. <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  11. <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/>
  12. <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  13. <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  14. <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  15. </aop:aspect>
  16. </aop:config>
  17. </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(..) 方法

  1. @Override
  2. @Nullable
  3. public BeanDefinition parse(Element element, ParserContext parserContext) {
  4. CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
  5. parserContext.pushContainingComponent(compositeDef);
  6. // <1> 解析 <aop:config /> 标签
  7. // 注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话),设置为优先级最高
  8. // 过程和 @EnableAspectJAutoProxy、<aop:aspectj-autoproxy /> 差不多
  9. configureAutoProxyCreator(parserContext, element);
  10. // <2> 获取 <aop:config /> 的子标签,遍历进行处理
  11. List<Element> childElts = DomUtils.getChildElements(element);
  12. for (Element elt: childElts) {
  13. // 获取子标签的名称
  14. String localName = parserContext.getDelegate().getLocalName(elt);
  15. if (POINTCUT.equals(localName)) {
  16. // <2.1> 处理 <aop:pointcut /> 子标签,解析出 AspectJExpressionPointcut 对象并注册
  17. parsePointcut(elt, parserContext);
  18. }
  19. else if (ADVISOR.equals(localName)) {
  20. // <2.2> 处理 <aop:advisor /> 子标签,解析出 DefaultBeanFactoryPointcutAdvisor 对象并注册,了指定 Advice 和 Pointcut(如果有)
  21. parseAdvisor(elt, parserContext);
  22. }
  23. else if (ASPECT.equals(localName)) {
  24. // <2.3> 处理 <aop:aspectj /> 子标签,解析出所有的 AspectJPointcutAdvisor 对象并注册,里面包含了 Advice 对象和对应的 Pointcut 对象
  25. // 同时存在 Pointcut 配置,也会解析出 AspectJExpressionPointcut 对象并注册
  26. parseAspect(elt, parserContext);
  27. }
  28. }
  29. // <3> 将 `parserContext` 上下文中已注册的 BeanDefinition 合并到上面 `compositeDef` 中(暂时忽略)
  30. parserContext.popAndRegisterContainingComponent();
  31. return null;
  32. }

该方法的处理过程如下:

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

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

    过程和 @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 />

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

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

  1. // ConfigBeanDefinitionParser.java
  2. private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
  3. // <1> 获取 <aop:pointcut /> 标签的 `id` 和 `expression` 配置
  4. String id = pointcutElement.getAttribute(ID);
  5. String expression = pointcutElement.getAttribute(EXPRESSION);
  6. AbstractBeanDefinition pointcutDefinition = null;
  7. try {
  8. this.parseState.push(new PointcutEntry(id));
  9. // <2> 创建一个 AspectJExpressionPointcut 类型的 RootBeanDefinition 对象
  10. pointcutDefinition = createPointcutDefinition(expression);
  11. // <3> 设置来源
  12. pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
  13. String pointcutBeanName = id;
  14. // <4> 注册这个 AspectJExpressionPointcut 对象
  15. if (StringUtils.hasText(pointcutBeanName)) {
  16. // <4.1> 如果 `id` 配置不为空,则取其作为名称
  17. parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
  18. }
  19. else {
  20. // <4.2> 否则,自动生成名称,也就是取 `className`
  21. pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
  22. }
  23. // <5> 将注册的 BeanDefinition 包装成 ComponentDefinition 放入 `parserContext` 上下文中,暂时忽略
  24. parserContext.registerComponent(
  25. new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
  26. }
  27. finally {
  28. this.parseState.pop();
  29. }
  30. return pointcutDefinition;
  31. }

解析过程大致如下:

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

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

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

  4. 注册这个 AspectJExpressionPointcut 对象

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

<aop:advisor />

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

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

  1. // ConfigBeanDefinitionParser.java
  2. private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
  3. // <1> 解析 <aop:advisor /> 标签
  4. // 创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象,并指定了 Advice
  5. AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
  6. // <2> 获取 `id` 属性
  7. String id = advisorElement.getAttribute(ID);
  8. try {
  9. this.parseState.push(new AdvisorEntry(id));
  10. String advisorBeanName = id;
  11. // <3> 注册第 `1` 步创建的 RootBeanDefinition
  12. if (StringUtils.hasText(advisorBeanName)) {
  13. // <3.1> 如果 `id` 不为空,则取其作为名称
  14. parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
  15. }
  16. else {
  17. // <3.2> 否则,生成一个名称,也就是 `className`
  18. advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
  19. }
  20. // <4> 获取这个 Advisor 对应的 Pointcut(也许就是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的名称)
  21. Object pointcut = parsePointcutProperty(advisorElement, parserContext);
  22. // <4.1> 如果是 AspectJExpressionPointcut
  23. if (pointcut instanceof BeanDefinition) {
  24. // 第 `1` 步创建的 RootBeanDefinition 添加 `pointcut` 属性,指向这个 AspectJExpressionPointcut
  25. advisorDef.getPropertyValues().add(POINTCUT, pointcut);
  26. parserContext.registerComponent(
  27. new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
  28. }
  29. // <4.2> 否则,如果是一个引用的 Pointcut 的名称
  30. else if (pointcut instanceof String) {
  31. // 第 `1` 步创建的 RootBeanDefinition 添加 `pointcut` 属性,指向这个名称对应的引用
  32. advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
  33. parserContext.registerComponent(
  34. new AdvisorComponentDefinition(advisorBeanName, advisorDef));
  35. }
  36. }
  37. finally {
  38. this.parseState.pop();
  39. }
  40. }

解析过程大致如下:

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

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

  1. <beans>
  2. <aop:aspectj-autoproxy/>
  3. <bean id="aspectXmlConfig" class="org.geekbang.thinking.in.spring.aop.features.aspect.AspectXmlConfig"/>
  4. <aop:config>
  5. <aop:aspect id="AspectXmlConfig" ref="aspectXmlConfig">
  6. <aop:pointcut id="anyPublicMethod" expression="execution(public * *(..))"/>
  7. <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  8. <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  9. <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/>
  10. <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  11. <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  12. <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/>
  13. </aop:aspect>
  14. </aop:config>
  15. </beans>

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

  1. private void parseAspect(Element aspectElement, ParserContext parserContext) {
  2. // <1> 获取 `id` 和 `ref` 属性
  3. String aspectId = aspectElement.getAttribute(ID);
  4. String aspectName = aspectElement.getAttribute(REF);
  5. try {
  6. this.parseState.push(new AspectEntry(aspectId, aspectName));
  7. // <2> 定义两个集合 `beanDefinitions`、`beanReferences`
  8. // 解析出来的 BeanDefinition
  9. List<BeanDefinition> beanDefinitions = new ArrayList<>();
  10. // 需要引用的 Bean
  11. List<BeanReference> beanReferences = new ArrayList<>();
  12. // <3> 获取所有的 <aop:declare-parents /> 子标签,遍历进行处理
  13. List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
  14. for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
  15. Element declareParentsElement = declareParents.get(i);
  16. // <3.1> 解析 <aop:declare-parents /> 子标签
  17. // 解析出 DeclareParentsAdvisor 对象,添加至 `beanDefinitions`
  18. beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
  19. }
  20. // We have to parse "advice" and all the advice kinds in one loop, to get the
  21. // ordering semantics right.
  22. // <4> 获取 <aop:aspectj /> 所有的子节点,遍历进行处理
  23. NodeList nodeList = aspectElement.getChildNodes();
  24. boolean adviceFoundAlready = false;
  25. for (int i = 0; i < nodeList.getLength(); i++) {
  26. Node node = nodeList.item(i);
  27. // <4.1> 如果是 <aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing /> 标签,则进行处理
  28. if (isAdviceNode(node, parserContext)) {
  29. // <4.2> 如果第一次进来,那么就是配置了 Advice,则 `ref` 必须指定一个 Bean,因为这些 Advice 的 `method` 需要从这个 Bean 中获取
  30. if (!adviceFoundAlready) {
  31. adviceFoundAlready = true;
  32. if (!StringUtils.hasText(aspectName)) {
  33. parserContext.getReaderContext().error(
  34. "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
  35. aspectElement, this.parseState.snapshot());
  36. return;
  37. }
  38. // <4.2.1> 往 `beanReferences` 添加需要引用的 Bean
  39. beanReferences.add(new RuntimeBeanReference(aspectName));
  40. }
  41. // <4.3> 根据 Advice 标签进行解析
  42. // 创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册
  43. AbstractBeanDefinition advisorDefinition = parseAdvice(
  44. aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
  45. // <4.4> 添加至 `beanDefinitions` 中
  46. beanDefinitions.add(advisorDefinition);
  47. }
  48. }
  49. // <5> 将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中
  50. // 并放入 `parserContext` 上下文中,暂时忽略
  51. AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
  52. aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
  53. parserContext.pushContainingComponent(aspectComponentDefinition);
  54. // <6> 获取所有的 <aop:pointcut /> 子标签,进行遍历处理
  55. List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
  56. for (Element pointcutElement : pointcuts) {
  57. // <6.1> 解析出 AspectJExpressionPointcut 对象并注册
  58. parsePointcut(pointcutElement, parserContext);
  59. }
  60. parserContext.popAndRegisterContainingComponent();
  61. } finally {
  62. this.parseState.pop();
  63. }
  64. }

解析过程大致如下:

  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 子标签

  1. private AbstractBeanDefinition parseAdvice(
  2. String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
  3. List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
  4. try {
  5. this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
  6. // create the method factory bean
  7. // <1> 创建 MethodLocatingFactoryBean 类型的 RootBeanDefinition
  8. // 因为通过标签配置的 Advice 对应的方法在其他 Bean 中,那么可以借助于 FactoryBean 来进行创建
  9. RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
  10. // <1.1> 获取 `targetBeanName` 和 `method` 并进行设置
  11. methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
  12. methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
  13. // <1.2> 设置这个 Bean 是由 Spring 内部合成的
  14. methodDefinition.setSynthetic(true);
  15. // create instance factory definition
  16. // <2> 创建一个 SimpleBeanFactoryAwareAspectInstanceFactory 类型的 RootBeanDefinition
  17. RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
  18. // <2.1> 设置了 AspectJ 对应的 名称,用于获取这个 AspectJ 的实例对象
  19. aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
  20. // <2.2> 设置这个 Bean 是由 Spring 内部合成的
  21. aspectFactoryDef.setSynthetic(true);
  22. // register the pointcut
  23. // <3> 创建一个 Advice 对象,包含了对应的 Pointcut
  24. AbstractBeanDefinition adviceDef = createAdviceDefinition(
  25. adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
  26. beanDefinitions, beanReferences);
  27. // configure the advisor
  28. // <4> 创建一个 AspectJPointcutAdvisor 类型的 RootBeanDefinition 对象,用于包装上面创建的 Advice
  29. // Spring AOP 中的 Advice 都是放入 Advisor “容器” 中
  30. RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
  31. // <4.1> 设置来源
  32. advisorDefinition.setSource(parserContext.extractSource(adviceElement));
  33. // <4.2> 将上面创建的 Advice 对象作为构造器入参
  34. advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
  35. // <4.3> 设置 `order` 优先级
  36. if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
  37. advisorDefinition.getPropertyValues().add(
  38. ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
  39. }
  40. // register the final advisor
  41. // <5> 注册这个 AspectJPointcutAdvisor,自动生成名字
  42. parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
  43. // <6> 返回这个已注册的 AspectJPointcutAdvisor
  44. return advisorDefinition;
  45. }
  46. finally {
  47. this.parseState.pop();
  48. }
  49. }

处理过程大致如下:

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

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

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

    1. private AbstractBeanDefinition createAdviceDefinition(
    2. Element adviceElement, ParserContext parserContext, String aspectName, int order,
    3. RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
    4. List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    5. // <1> 根据 Advice 标签创建对应的 Advice
    6. // <aop:before /> -> AspectJMethodBeforeAdvice
    7. // <aop:after /> -> AspectJAfterAdvice
    8. // <aop:after-returning /> -> AspectJAfterReturningAdvice
    9. // <aop:after-throwing /> -> AspectJAfterThrowingAdvice
    10. // <aop:around /> -> AspectJAroundAdvice
    11. RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    12. // <1.1> 设置来源
    13. adviceDefinition.setSource(parserContext.extractSource(adviceElement));
    14. // <1.2> 设置引用的 AspectJ 的名称
    15. adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    16. // <1.3> 设置优先级
    17. adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    18. if (adviceElement.hasAttribute(RETURNING)) {
    19. adviceDefinition.getPropertyValues().add(
    20. RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    21. }
    22. if (adviceElement.hasAttribute(THROWING)) {
    23. adviceDefinition.getPropertyValues().add(
    24. THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    25. }
    26. if (adviceElement.hasAttribute(ARG_NAMES)) {
    27. adviceDefinition.getPropertyValues().add(
    28. ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
    29. }
    30. // <2> 获取 Advice 的构造器参数对象 `cav`
    31. // 设置 1. 引用的方法、2. Pointcut(也许是引用的 Pointcut 的名称)、3. 引用的方法所属 AspectJ 对象
    32. // 你点进这些 Advice 类型的对象中看看构造方法就知道怎么回事,例如:AspectJMethodBeforeAdvice
    33. ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
    34. // <2.1> 往 `cav` 添加 Advice 对应的方法作为入参
    35. cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
    36. // <2.2> 解析出对应的 Pointcut 对象(可能是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的一个运行时引用对象)
    37. Object pointcut = parsePointcutProperty(adviceElement, parserContext);
    38. // <2.2.1> 如果是 AspectJExpressionPointcut
    39. if (pointcut instanceof BeanDefinition) {
    40. // 往 `cav` 添加 `pointcut` 入参
    41. cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
    42. // 添加至 `beanDefinitions`
    43. beanDefinitions.add((BeanDefinition) pointcut);
    44. }
    45. // <2.2.2> 否则,如果是 引用的 Pointcut
    46. else if (pointcut instanceof String) {
    47. // 根据引用的 Pointcut 的名称生成一个引用对象
    48. RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
    49. // 往构 `cav` 添加 `pointcut` 入参
    50. cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
    51. // 添加至 `pointcutRef`
    52. beanReferences.add(pointcutRef);
    53. }
    54. // <2.3> 往 `cav` 添加 Advice 对应的方法所在 Bean 作为入参
    55. cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    56. // <3> 返回为 Advice 创建的 RootBeanDefinition 对象
    57. return adviceDefinition;
    58. }
    59. private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
    60. String elementName = parserContext.getDelegate().getLocalName(adviceElement);
    61. if (BEFORE.equals(elementName)) {
    62. return AspectJMethodBeforeAdvice.class;
    63. }
    64. else if (AFTER.equals(elementName)) {
    65. return AspectJAfterAdvice.class;
    66. }
    67. else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
    68. return AspectJAfterReturningAdvice.class;
    69. }
    70. else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
    71. return AspectJAfterThrowingAdvice.class;
    72. }
    73. else if (AROUND.equals(elementName)) {
    74. return AspectJAroundAdvice.class;
    75. }
    76. else {
    77. throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
    78. }
    79. }
  4. 创建一个 AspectJPointcutAdvisor 类型的 RootBeanDefinition 对象,用于包装上面创建的 Advice,Spring AOP 中的 Advice 都是放入 Advisor “容器” 中

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

  6. 返回这个已注册的 AspectJPointcutAdvisor

------------------------------------

Spring Boot 注解驱动

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

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. <version>2.3.5.RELEASE</version>
  5. </dependency>

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

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aop</artifactId>
  4. <version>5.2.10.RELEASE</version>
  5. <scope>compile</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.aspectj</groupId>
  9. <artifactId>aspectjweaver</artifactId>
  10. <version>1.9.6</version>
  11. <scope>compile</scope>
  12. </dependency>

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

AopAutoConfiguration

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

  1. @Configuration
  2. @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
  3. @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
  4. public class AopAutoConfiguration {
  5. @Configuration
  6. @EnableAspectJAutoProxy(proxyTargetClass = false)
  7. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
  8. public static class JdkDynamicAutoProxyConfiguration {
  9. }
  10. @Configuration
  11. @EnableAspectJAutoProxy(proxyTargetClass = true)
  12. @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
  13. public static class CglibAutoProxyConfiguration {
  14. }
  15. }

可以看到这个 @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 模块。

死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置的更多相关文章

  1. Spring 中的事务操作、注解、以及 XML 配置

    事务 事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割.例如我们的转账这个业务,就需要进行数据库事务的处理. 转账中至少会涉及到两条 SQ ...

  2. 死磕Spring之AOP篇 - Spring AOP常见面试题

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

  3. 死磕Spring之AOP篇 - Spring 事务详解

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

  4. 白话Spring(基础篇)---AOP(execution表达式)

    作为AOP的最后一节内容,我们来简单总结一下切面表达式上见的书写方法.下面的那内容有参考其他博文,在此先对开源博客的各位大神表示感谢! -------------------------------- ...

  5. 白话Spring(基础篇)---AOP(execution表达式)(转)

    [一知半解,就是给自己挖坑] 作为AOP的最后一节内容,我们来简单总结一下切面表达式上见的书写方法.下面的那内容有参考其他博文,在此先对开源博客的各位大神表示感谢! ----------------- ...

  6. Spring第12篇—— Spring对Hibernate的SessionFactory的集成功能

    由于Spring和Hibernate处于不同的层次,Spring关心的是业务逻辑之间的组合关系,Spring提供了对他们的强大的管理能力, 而Hibernate完成了OR的映射,使开发人员不用再去关心 ...

  7. 死磕Spring之AOP篇 - Spring AOP总览

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

  8. 死磕Spring之AOP篇 - Spring AOP自动代理(一)入口

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

  9. 死磕Spring之AOP篇 - Spring AOP自动代理(二)筛选合适的通知器

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

随机推荐

  1. Hive安装与配置——2.3.5版本

    Hive安装配置 Hive是一个数据仓库基础工具在Hadoop中用来处理结构化数据.它架构在Hadoop之上,提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行,使查询和分 ...

  2. javaMail (java代码发送邮件)

    第一在邮件账户设置开启以下两个 需要发送短信获取  授权码. 代码如下: package com.hjb.javaMail; import javax.mail.*; import javax.mai ...

  3. 翻译:《实用的 Python 编程》02_07_Objects

    目录 | 上一节 (2.6 列表推导式) | 下一节 (3 从程序组织) 2.7 对象 本节介绍有关 Python 内部对象模型的更多详细信息,并讨论一些与内存管理,拷贝和类型检查有关的问题. 赋值 ...

  4. 2021-2-27:Linux 下如何优化 Java MMAP 写入

    主要是调整 pdflush 相关参数. 在linux操作系统中,写操作是异步的,即写操作返回的时候数据并没有真正写到磁盘上,而是先写到了系统cache里,随后由pdflush内核线程将系统中的脏页写到 ...

  5. Spring IoC - 循环依赖

    Spring 复习 3.循环依赖 3.1 定义 循环依赖指多个对象的创建过程中均需要注入对方对象,如下所示 class A{ B b; public A(){ } public A(B b){ thi ...

  6. Course2.1 Graph Paper Programming

    Overview 通过日常生活中的活动来体验程序算法,目标时能够将现实世界的场景与程序场景关联起来. Objective 抓住将现实世界问题转换为程序的难点: 你认为非常明确的指令在计算机看来可能还是 ...

  7. wxWidgets源码分析(8) - MVC架构

    目录 MVC架构 wxDocManager文档管理器 模板类创建文档对象 视图对象的创建 创建顺序 框架菜单命令的执行过程 wxDocParentFrame菜单入口 wxDocManager类的处理 ...

  8. Python列表元组和字典解析式

    目录 列表解析式List comprehensive 集合解析式Set comprehensive 字典解析式Dict comprehensive 总结 以下内容基于Python 3x 列表解析式Li ...

  9. 后端程序员之路 6、Python fabric

    直接写shell固然也很好,但是用python来写脚本,也是美滋滋.fabric是一个封装部署.多机操作等功能的python库. Welcome to Fabric! - Fabric documen ...

  10. POJ-3281(最大流+EK算法)

    Dining POJ-3281 这道题目其实也是网络流中求解最大流的一道模板题. 只要建模出来以后直接套用模板就行了.这里的建模还需要考虑题目的要求:一种食物只能给一只牛. 所以这里可以将牛拆成两个点 ...