跳至主要內容

8.Spring AOP注解驱动与XML配置

Java突击队大约 24 分钟

8.Spring AOP注解驱动与XML配置

Spring 版本:5.1.14.RELEASE

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

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

该系列其他文章请查看:《死磕 Spring 之 AOP 篇 - 文章导读》open in new window

通过前面关于 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 等注解的实现原理》open in new window 文章

所以说@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 文件)》open in new window 这篇文章。在 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,SpringAOP中的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 模块。

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址:open in new window