死磕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
注解,它的 value
是 AspectJAutoProxyRegistrar.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 的过程如下:
- 通过
AopConfigUtils
注册一个AnnotationAwareAspectJAutoProxyCreator
自动代理对象(如果没有注册的话),设置为优先级最高 - 获取
@EnableAspectJAutoProxy
注解的配置信息 - 如果注解配置信息不为空,则根据配置设置
AnnotationAwareAspectJAutoProxyCreator
的属性- 如果
proxyTargetClass
为true
,则进行设置(开启类代理,也就是开启 CGLIB 动态代理) - 如果
exposeProxy
为true
,则进行设置(需要暴露代理对象,也就是在 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,过程如下:
- 如果
org.springframework.aop.config.internalAutoProxyCreator
已注册- 获取对应的 BeanDefinition 对象
- 如果已注册的
internalAutoProxyCreator
和入参的 Class 不相等,说明可能是继承关系- 获取已注册的
internalAutoProxyCreator
的优先级 - 获取需要注册的
internalAutoProxyCreator
的优先级 - 如果需要注册的优先级更高,那取代已注册的 Class 对象,
InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator
- 获取已注册的
- 否则,因为已注册,则返回
null
- 否则,没有注册,则创建一个 RootBeanDefinition 对象进行注册
- 设置来源
- 设置为最高优先级
- 设置角色为ROLE_INFRASTRUCTURE,表示是 Spring 框架内部的 Bean
- 注册自动代理的 Bean,名称为
org.springframework.aop.config.internalAutoProxyCreator
- 返回刚注册的 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;
}
该方法的处理过程如下:
-
解析
<aop:config />
标签,注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话),设置为优先级最高private void configureAutoProxyCreator(ParserContext parserContext, Element element) { // 注册 AspectJAwareAdvisorAutoProxyCreator 自动代理对象(如果需要的话) AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element); }
过程和
@EnableAspectJAutoProxy
、<aop:aspectj-autoproxy />
的解析过程差不多,这里不再进行展述 -
获取
<aop:config />
的子标签,遍历进行处理- 调用
parsePointcut(..)
方法,处理<aop:pointcut />
子标签,解析出 AspectJExpressionPointcut 对象并注册 - 调用
parseAdvisor(..)
方法,处理<aop:advisor />
子标签,解析出 DefaultBeanFactoryPointcutAdvisor 对象并注册,了指定 Advice 和 Pointcut(如果有) - 调用
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;
}
解析过程大致如下:
-
获取
<aop:pointcut />
标签的id
和expression
配置 -
根据
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; }
-
设置来源
-
注册这个 AspectJExpressionPointcut 对象
- 如果
id
配置不为空,则取其作为名称 - 否则,自动生成名称,也就是取
className
- 如果
-
将注册的 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();
}
}
解析过程大致如下:
-
解析
<aop:advisor />
标签,创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象,并指定了 Adviceprivate 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; }
-
获取
id
属性 -
注册第
1
步创建的 RootBeanDefinition- 如果
id
不为空,则取其作为名称 - 否则,生成一个名称,也就是
className
- 如果
-
获取这个 Advisor 对应的 Pointcut(也许就是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的名称)
- 如果是 AspectJExpressionPointcut,第
1
步创建的 RootBeanDefinition 添加pointcut
属性,指向这个 AspectJExpressionPointcut - 否则,如果是一个引用的 Pointcut 的名称,第
1
步创建的 RootBeanDefinition 添加pointcut
属性,指向这个名称对应的引用
- 如果是 AspectJExpressionPointcut,第
<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();
}
}
解析过程大致如下:
- 获取
id
和ref
属性 - 定义两个集合
beanDefinitions
、beanReferences
,分别保存解析出来的 BeanDefinition 和需要引用的 Bean - 获取所有的
<aop:declare-parents />
子标签,遍历进行处理- 解析
<aop:declare-parents />
子标签,解析出 DeclareParentsAdvisor 对象并注册,添加至beanDefinitions
- 解析
- 获取
<aop:aspectj />
所有的子节点,遍历进行处理- 如果是
<aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing />
标签,则进行处理 - 如果第一次进来,那么就是配置了 Advice,则
ref
必须指定一个 Bean,因为这些 Advice 的method
需要从这个 Bean 中获取- 往
beanReferences
添加需要引用的 Bean
- 往
- 根据 Advice 标签进行解析,创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册
- 添加至
beanDefinitions
中
- 如果是
- 将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中,并放入
parserContext
上下文中,暂时忽略 - 获取所有的
<aop:pointcut />
子标签,进行遍历处理- 解析出 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();
}
}
处理过程大致如下:
-
创建 MethodLocatingFactoryBean 类型的 RootBeanDefinition,因为通过标签配置的 Advice 对应的方法在其他 Bean 中,那么可以借助于 FactoryBean 来进行创建
- 获取
targetBeanName
和method
并进行设置 - 设置这个 Bean 是由 Spring 内部合成的
- 获取
-
创建一个 SimpleBeanFactoryAwareAspectInstanceFactory 类型的 RootBeanDefinition
- 设置了 AspectJ 对应的 名称,用于获取这个 AspectJ 的实例对象
- 设置这个 Bean 是由 Spring 内部合成的
-
创建一个 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 + "]."); } }
-
创建一个 AspectJPointcutAdvisor 类型的 RootBeanDefinition 对象,用于包装上面创建的 Advice,Spring AOP 中的 Advice 都是放入 Advisor “容器” 中
-
注册这个 AspectJPointcutAdvisor,自动生成名字
-
返回这个已注册的 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
这个包,Aspect
、Advice
和AnnotatedElement
三个 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 模块。