基于最新Spring 5.x,对Spring AOP中的<aop:config/>标签的解析源码进行了详细分析,这是Spring AOP源码的入口!
此前我们已经详细学习了Spring AOP的基本使用,现在我们来学习Spring AOP的源码,学习AOP源码的前提要求是会使用AOP,同时要求我们了解IoC容器初始化的相关源码,否则很多的方法和和名词会让人摸不着头脑,比如bean定义是什么?如何注册bean定义?等等,这些都是IoC源码的核心知识点,我们此前的文章花费了大量时间和文字去讲解Spring IoC容器初始化的源码,在此不做赘述,默认大家都是了解的!
Spring AOP源码(1)—<aop:config/>AOP配置标签解析
Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象
Spring AOP源码(3)—invoke代理方法的调用与执行增强
Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator
Spring AOP源码(5)—DefaultAdvisorAutoProxyCreator自动代理创建者
在此前IoC容器初始化(3)的文章中,我们说过,对于扩展标签的解析是在parseCustomElement方法中完成的,不同扩展标签的解析,是根据该标签的本地名称去从NamespaceHandlerSupport的parsers缓存中获取对应的BeanDefinitionParser解析器来完成的。
对于aop命名空间下的系列的标签的解析器,都是通过AopNamespaceHandler注册到parsers缓存中的,从该类中我们能知道所有aop系列标签及其子标签的解析器:
/**
* aop 命名空间处理器
*/
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* 注册一些列的BeanDefinitionParser,用于解析
* 、 、 标签及其子标签
*/
@Override
public void init() {
// 在2.0以及2.5之后的xsd文件中均有的标签
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
//2.5及其之后的xsd文件中,该标签被移除,因此Spring 5.x版本的XML配置中不能使用该标签了
//实际上是被移动到context命名空间中了
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
< aop:scoped-proxy/>是作用域代理标签,用于装饰bean,改变其生命周期,将暴露出来的bean的生命周期控制在正确的范围类,用的比较少。 < aop:config/>用于基于XML配置AOP,< aop:aspectj-autoproxy/>用于基于XML开启AOP注解自动配置的支持,也就是支持@Aspect切面类及其内部的AOP注解,另外还有@EnableAspectJAutoProxy注解也能够开启AOP注解自动配置的支持,用于彻底摆脱XML文件。
从现在开始,接下来的几篇文章中,我们主要学习 < aop:config/>标签、< aop:aspectj-autoproxy/>标签、@EnableAspectJAutoProxy等注解的解析源码,以及代理对象的创建源码,以及代理对象的调用源码。实际上,当学完了AOP的源码之后我们就会知道基于注解和基于XML的AOP配置最终都是殊途同归的,就像基于XML和注解的IoC容器初始化的源码一样,注解和XML的配置终究都要融于一个容器中。
我们先学习基于XML的AOP配置,也就是< aop:config/>标签的解析源码,后面我们会继续学习基于注解的AOP配置解析的相关源码。
我们从ConfigBeanDefinitionParser.parser方法的源码开始阅读!该方法的目的就是解析< aop:config/>标签及其子标签,当这些标签封装为对应类型的bean定义。
private static final String POINTCUT = "pointcut";
private static final String ADVISOR = "advisor";
private static final String ASPECT = "aspect";
/**
* ConfigBeanDefinitionParser的方法
* 解析 标签及其子标签
*
* @param element 标签元素
* @param parserContext 解析上下文
* @return 解析结果,固定返回null
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
//新建一个CompositeComponentDefinition类型的bean定义,名称就是标签名 aop:config
//内部保存了多个ComponentDefinition,基于XML的source默认为null
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
//存入解析上下文内部的containingComponents集合中,入栈顶
parserContext.pushContainingComponent(compositeDef);
/*
* 尝试向容器注入或者升级AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者bean定义,专门用于后续创建AOP代理对象
* 这个类还实现了比如BeanClassLoaderAware、BeanFactoryAware、SmartInstantiationAwareBeanPostProcessor、
* InstantiationAwareBeanPostProcessor、BeanPostProcessor …… 等一系列的自动回调接口,它们在创建代理对象的过程中非常有用
*
* 标签使用AspectJAwareAdvisorAutoProxyCreator创建代理,实际上还有很多创建者可以用于创建代理对象
* 比如 以及@EnableAspectJAutoProxy使用AnnotationAwareAspectJAutoProxyCreator
* 以及@EnableTransactionManagement使用InfrastructureAdvisorAutoProxyCreator
* 不同的标签或者注解使用不同的创建者,但容器最终只会创建一个bean定义,采用优先级最高的自动代理创建者的类型,我们后面会讲到
*/
configureAutoProxyCreator(parserContext, element);
//获取 标签下的所有子标签结点元素
List<Element> childElts = DomUtils.getChildElements(element);
//遍历解析
for (Element elt : childElts) {
//获取子标签本地名称,即去除"aop:"之后的名称
String localName = parserContext.getDelegate().getLocalName(elt);
//如果是 标签
if (POINTCUT.equals(localName)) {
/*
* 调用parsePointcut方法解析 标签
*/
parsePointcut(elt, parserContext);
}
//如果是 标签
else if (ADVISOR.equals(localName)) {
/*
* 调用parseAdvisor方法解析 标签
*/
parseAdvisor(elt, parserContext);
}
//如果是 标签
else if (ASPECT.equals(localName)) {
/*
* 调用parseAspect方法解析 标签
*/
parseAspect(elt, parserContext);
}
}
//出栈并注册,并不是注册到注册表中……,可能什么也不做
parserContext.popAndRegisterContainingComponent();
//返回null
return null;
}
//--------栈操作---------
/**
* ParserContext的属性,存放一系列的组件bean定义
* 这是一个ArrayDeque集合,可以模拟栈
*/
private final Deque<CompositeComponentDefinition> containingComponents = new ArrayDeque<>();
/**
* ParserContext的方法
* 存入containingComponents栈顶
*/
public void pushContainingComponent(CompositeComponentDefinition containingComponent) {
this.containingComponents.push(containingComponent);
}
/**
* ParserContext的方法
* 栈顶元素出栈并注册
*/
public void popAndRegisterContainingComponent() {
//注册组件
registerComponent(popContainingComponent());
}
/**
* ParserContext的方法
* 栈顶元素出栈
*/
public CompositeComponentDefinition popContainingComponent() {
return this.containingComponents.pop();
}
/**
* 注册组件,并不是注册到注册表中……
*/
public void registerComponent(ComponentDefinition component) {
//获取但不移除最新栈顶元素
CompositeComponentDefinition containingComponent = getContainingComponent();
//如果栈顶元素不为null,那么当前组件加入到栈顶元素的内部集合中
if (containingComponent != null) {
containingComponent.addNestedComponent(component);
}
//否则,通过readerContext发布组件注册事件,默认也是个空方法,啥都没干……
else {
this.readerContext.fireComponentRegistered(component);
}
}
/**
* 获取但不移除最新栈顶元素
*/
@Nullable
public CompositeComponentDefinition getContainingComponent() {
return this.containingComponents.peek();
}
configureAutoProxyCreator方法用于尝试向容器注入或者升级一个AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者bean定义,beanName为"org.springframework.aop.config.internalAutoProxyCreator",专门用于后续创建AOP代理对象。
这个类还实现了比如BeanClassLoaderAware、BeanFactoryAware、SmartInstantiationAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanPostProcessor …… 等一系列的自动回调接口,它们在创建代理对象的过程中非常有用。这些也是Spring的强扩展性的根本。
< aop:config/>标签使用AspectJAwareAdvisorAutoProxyCreator创建代理,实际上还有很多创建者可以用于创建代理对象,比如< aop:aspectj-autoproxy/>以及@EnableAspectJAutoProxy使用AnnotationAwareAspectJAutoProxyCreator,< tx:annotation-driven/>以及@EnableTransactionManagement使用InfrastructureAdvisorAutoProxyCreator,不同的标签或者注解使用不同的创建者,但容器最终只会创建一个bean定义,采用优先级最高的自动代理创建者的类型,我们后面会讲到。
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 通过 标签的解析触发调用,尝试配置AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者的bean定义到容器
*/
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
//调用AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary方法
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
可以看到,该方法直接委托AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary方法继续操作!
分为三步:
这几个方法都是通用方法,我们在后面的文章中还会见到,到时候不再赘述!
/**
* AopNamespaceUtils的方法
*
* 如有必要,注册AspectJAwareAdvisorAutoProxyCreator
*/
public static void registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
/*
* 1 尝试注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator"
* 类型为AspectJAwareAdvisorAutoProxyCreator的自动代理创建者的bean定义
*/
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
/*
* 2 解析proxy-target-class与expose-proxy属性
* proxy-target-class用于设置代理模式,默认是优先JDK动态代理,其次CGLIB代理,可以指定为CGLIB代理
* expose-proxy用于暴露代理对象,主要用来解决同一个目标类的方法互相调用时代理不生效的问题
*/
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
/*
* 3 注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中
*/
registerComponentIfNecessary(beanDefinition, parserContext);
}
AopConfigUtils的同名方法,内部继续调用registerOrEscalateApcAsRequired方法,由于解析的< aop:config />标签,因此第一个参数是AspectJAwareAdvisorAutoProxyCreator.class类型。
/**
1. AopConfigUtils的方法
2.
3. 如有必要,注册自动代理创建者,类型为AspectJAwareAdvisorAutoProxyCreator.class
*/
@Nullable
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
/*
* 继续调用registerOrEscalateApcAsRequired方法
* 由于解析的 标签,因此第一个参数是AspectJAwareAdvisorAutoProxyCreator.class
*/
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
核心方法终于到了,从源码中就可以看出“升级”或者“新增”的逻辑:
容器最终只会创建一个自动代理创建者bean定义,采用优先级最高的自动代理创建者的类型。
首先尝试获取名为"org.springframework.aop.config.internalAutoProxyCreator
"的bean定义,如果已存在,那么比较bean定义中的自动代理创建者的类型和当前参数传递的自动代理创建者的类型的优先级,如果当前参数传递的自动代理创建者的类型的优先级更高,那么那么bean定义的beanClass属性设置为使用当前参数传递的自动代理创建者的类型的className,即升级bean定义,最后返回null。
设置bean定义的order属性优先级最高,也就是说将会最先被应用并尝试创建代理对象。
新建
一个RootBeanDefinition类型的bean定义,以"org.springframework.aop.config.internalAutoProxyCreator
"为beanName,通过registerBeanDefinition注册到容器中(该方法在此前IoC容器初始化(3)的源码文章中已经讲过了)。最后返回新增的bean定义。
/**
* AopConfigUtils的属性
*
* Spring内部管理的自动代理创建者的 beanName
*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
/**
1. AopConfigUtils的方法
2.
3. 注册或者修改自动代理创建者的bean定义
4. 5. @param cls 自动代理创建者的class,用于比较优先级或者创建bean定义
6. @param registry 注册表
7. @param source 源数据
*/
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
/*
* 如果包含名为"org.springframework.aop.config.internalAutoProxyCreator"的bean定义
* 那么可能会升级bean定义的beanClass属性,Spring容器只会保存一个优先级最高的自动代理创建者的bean定义
*/
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//获取bean定义
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//如果当前bean定义的类型不是参数的类型,那么选择优先级最高的类型
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
//获取当前bean定义中的自动代理创建者的类型优先级,实际上就是存储在APC_PRIORITY_LIST集合的索引位置
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
//获取当前参数传递的自动代理创建者的类型优先级,实际上就是存储在APC_PRIORITY_LIST集合的索引位置
int requiredPriority = findPriorityForClass(cls);
//如果bean定义中的自动代理创建者的类型优先级 小于 当前参数传递的自动代理创建者的类型优先级
if (currentPriority < requiredPriority) {
//那么bean定义的beanClass属性设置为使用 当前参数传递的自动代理创建者的类型的className,即升级bean定义
apcDefinition.setBeanClassName(cls.getName());
}
}
//直接返回null,表示没有创建新的bean定义
return null;
}
//到这里,表示需要创建新的bean定义
//新建一个RootBeanDefinition类型的bean定义,beanClass使用当前参数class类型
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
//设置order属性优先级最高,也就是说将会最先被应用并尝试创建代理对象
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
/*
* 调用registerBeanDefinition方法注册这个新的BeanDefinition到注册表的缓存中,
* 名为"org.springframework.aop.config.internalAutoProxyCreator"
*
* 该方法在此前"IoC容器初始化(3)"的源码文章中已经讲过了,这是核心方法
*/
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
//返回新增的bean定义
return beanDefinition;
}
该方法用于获取自动类型创建者的优先级。实际上就是从AopConfigUtils的APC_PRIORITY_LIST集合汇总查找该class的索引位置并返回,找不到就抛出异常“Class name xxx is not a known auto-proxy creator class”。
APC_PRIORITY_LIST集合在AopConfigUtils类加载的时候就在静态块中按顺序初始化了一系列的自动代理创建者的类型,索引位置就是优先级,因此优先级大小为:InfrastructureAdvisorAutoProxyCreator.class < AspectJAwareAdvisorAutoProxyCreator.class < AnnotationAwareAspectJAutoProxyCreator.class。
实际上,在上一个方法中:
所以说,如果我们设置了< aop:config />和< aop:aspectj-autoproxy />两个标签,那么最终会注册AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者。
//---------AopConfigUtils的相关属性--------
/**
* 按升序顺序存储的自动代理创建者的类型集合
* 可以看到,默认有三种类型,优先级就是比较索引顺序的大小,因此优先级为:
* InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator
*
* 如果是解析 标签或者@EnableTransactionManagement事物注解,那么cls参数是InfrastructureAdvisorAutoProxyCreator.class
* 如果是解析 标签,那么cls参数是AspectJAwareAdvisorAutoProxyCreator.class
* 如果是解析 标签或者@EnableAspectJAutoProxy注解,那么cls参数是AnnotationAwareAspectJAutoProxyCreator.class
*/
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);
}
/**
1. AopConfigUtils的方法
2.
3. 返回当前类型的自动代理创建者的优先级,实际上就是存储的索引位置
*/
private static int findPriorityForClass(@Nullable String className) {
for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
Class<?> clazz = APC_PRIORITY_LIST.get(i);
if (clazz.getName().equals(className)) {
//返回索引
return i;
}
}
//没找到就抛出异常
throw new IllegalArgumentException(
"Class name [" + className + "] is not a known auto-proxy creator class");
}
在升级或者注册了自动代理创建者的bean定义之后,这一步用于解析、设置proxy-target-class和expose-proxy属性到这个bean定义的proxyTargetClass和exposeProxy属性中。
//------AopNamespaceUtils的相关属性--------
/**
* aop相关标签的proxy-target-class属性名常量,用于设置代理的模式
*/
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
/**
* aop相关标签的expose-proxy属性名常量,用于设置是否暴露代理对象
*/
private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
/**
* AopNamespaceUtils的方法
*
* 解析设置proxy-target-class和expose-proxy属性
*
* @param registry 注册表
* @param sourceElement 标签元素
*/
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
//获取proxy-target-class属性值,默认false,即优先采用JDK动态代理,不满足则采用CGLIB代理
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
//如果设置为true,那么强制走CGLIB代理
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//获取expose-proxy属性值,默认false,即不暴露代理对象
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
//如果设置为true,那么暴露代理对象
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
如果proxy-target-class属性为true,那么强制使用CGLIB创建代理对象,这里仅仅是设置bean定义的proxyTargetClass属性值为true,后面才会用到。这实际上是顶级父类ProxyConfig的属性。
/**
* AopConfigUtils的属性
*
* Spring内部管理的自动代理创建者的 beanName
*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
/**
* AopConfigUtils的方法
*
* 强迫AutoProxyCreator使用基于类的代理,也就是CGLIB代理
*/
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
//如果包含名为"org.springframework.aop.config.internalAutoProxyCreator"的bean定义
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//那么获取该bean定义
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//添加属性proxyTargetClass设置值为true,表示强制使用CGLIB代理
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
如果expose-proxy属性为true,那么强制暴露代理对象,这里仅仅是设置bean定义的exposeProxy属性值为true,后面才会用到。这实际上是顶级父类ProxyConfig的属性。
/**
* AopConfigUtils的属性
*
* Spring内部管理的自动代理创建者的 beanName
*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
/**
* AopConfigUtils的方法
*
* 强迫AutoProxyCreator暴露代理对象
*/
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
//如果包含名为"org.springframework.aop.config.internalAutoProxyCreator"的bean定义
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//那么获取该bean定义
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
//添加属性exposeProxy,设置值为true,表示强制暴露代理对象
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
如果bean定义不为null,即新增了bean定义,那么注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中,或者广播事件,而不是注册到注册表中。
/**
1. AopNamespaceUtils的方法
2.
3. 如果bean定义不为null,那么注册组件
4. 实际上是存入parserContext的containingComponents集合的栈顶元素的内部集合中或者广播事件
*/
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
//实际上是存入parserContext的containingComponents集合的栈顶元素的内部集合中
//这个方法我们在最开始的parse方法中就讲过了
parserContext.registerComponent(
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
}
}
parsePointcut方法用于解析< aop:pointcut/>切入点标签,并将一个< aop:pointcut/>标签封装成为一个bean定义并且注册到IoC容器缓存中:
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String EXPRESSION = "expression";
private static final String ID = "id";
/**
* 用于存储解析的阶段点位
* 内部是一个LinkedList集合,可以模拟栈
*/
private ParseState parseState = new ParseState();
/**
* ConfigBeanDefinitionParser的方法
*
* 解析 标签,也就是切入点表达式标签,生成bean定义注册到容器中
*
* @param pointcutElement 标签元素
* @param parserContext 解析上下文
*/
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
//获取id属性值
String id = pointcutElement.getAttribute(ID);
//获取expression属性值,也就是切入点表达式字符串
String expression = pointcutElement.getAttribute(EXPRESSION);
// 标签对应的bean定义
AbstractBeanDefinition pointcutDefinition = null;
try {
//新建一个PointcutEntry点位,存入parseState,压栈
this.parseState.push(new PointcutEntry(id));
//创建切入点表达式的bean定义对象,bean定义类型为RootBeanDefinition,beanClass类型为AspectJExpressionPointcut
//scope属性设置为prototype,synthetic属性设置为true,设置expression属性的值为切入点表达式字符串
pointcutDefinition = createPointcutDefinition(expression);
//设置源
pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
//切入点bean定义的默认名字设置为id
String pointcutBeanName = id;
//如果设置了id属性
if (StringUtils.hasText(pointcutBeanName)) {
/*
* 那么将beanName和BeanDefinition注册到registry的缓存中
* 这个方法在"IoC容器初始化(3)"的文章中已经讲过了
*/
parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
}
//如果没有设置id属性
else {
//那么生成beanName,随后同样注册到registry的缓存中,返回生成的beanName
pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
}
//注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中
parserContext.registerComponent(
new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
} finally {
//PointcutEntry点位出栈
this.parseState.pop();
}
//返回创建的bean定义
return pointcutDefinition;
}
createPointcutDefinition方法用于创建一个切入点bean定义,用来描述、解析一个< aop:pointcut/>切入点标签。
注意,它的synthetic属性为true,这表示切入点bean定义是一个合成的而不是不是由程序本身定义的bean,学习Spring源码以来,我们终于见到一个合成的bean定义了。还记得此前IoC源码学习时遇到的isSynthetic方法吗,就是用于判断某个bean定义是不是合成的bean定义。
private static final String EXPRESSION = "expression";
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 使用给定的切入点表达式创建AspectJExpressionPointcut类型的bean定义对象
*/
protected AbstractBeanDefinition createPointcutDefinition(String expression) {
//新建RootBeanDefinition类型的bean定义,beanClass类型为AspectJExpressionPointcut
RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
//设置scope属性为prototype
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
//设置synthetic属性为true,这表示它是一个合成的而不是不是由程序本身定义的bean
beanDefinition.setSynthetic(true);
//添加expression属性值为参数的切入点表达式字符串
beanDefinition.getPropertyValues().add(EXPRESSION, expression);
//返回创建的bean定义
return beanDefinition;
}
如果没有设置id,那么生成beanName,采用的生成器默认就是DefaultBeanNameGenerator,规则如下,具体的源码我们在"IoC容器初始化(3)"的文章中已经讲过了,在此不再赘述:
如果没指定id,那么生成的切入点beean定义的beanName就是"org.springframework.aop.aspectj. AspectJExpressionPointcut#0"、“org.springframework.aop.aspectj. AspectJExpressionPointcut#1”……
/**
* XmlReaderContext的方法
*
* 调用给定的 bean 名称生成器生成beanName,并注册 bean 定义,最后返回生成的beanName
*/
public String registerWithGeneratedName(BeanDefinition beanDefinition) {
//生成beanName,采用的生成器默认就是DefaultBeanNameGenerator
//DefaultBeanNameGenerator的生成规则就是基于XML的beanName生成规则,我们在"IoC容器初始化(3)"的文章中已经讲过了
//生成的beanName类似于 "org.springframework.aop.aspectj.AspectJExpressionPointcut#0"
String generatedName = generateBeanName(beanDefinition);
//调用registerBeanDefinition注册bean定义,我们在"IoC容器初始化(3)"的文章中已经讲过了
getRegistry().registerBeanDefinition(generatedName, beanDefinition);
//返回生成的beanName
return generatedName;
}
/**
* XmlReaderContext的方法
* 生成beanName
*/
public String generateBeanName(BeanDefinition beanDefinition) {
return this.reader.getBeanNameGenerator().generateBeanName(beanDefinition, getRegistry());
}
/**
* AbstractBeanDefinitionReader的方法
*/
@Override
public BeanNameGenerator getBeanNameGenerator() {
return this.beanNameGenerator;
}
/**
1. AbstractBeanDefinitionReader的属性,默认就是DefaultBeanNameGenerator
*/
private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
< aop:advisor/>通知器标签,它和< aop:aspect >类似,相当于一个小切面,它的advice-ref属性可以指向一个通知类bean,该bean要求实现Advice相关接口,不需要单独配置通知,但接口只有前置通知、后置通知和异常通知的方法。通常,advisor被用来管理事物,它的advice-ref属性配置对一个< tx:advice >的id引用,后面学习事物的时候就知道了。
parseAdvisor方法用于解析< aop:advisor/>通知器标签,并将一个aop:advisor/标签封装成为一个bean定义并且注册到IoC容器缓存中:
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String ID = "id";
private static final String POINTCUT = "pointcut";
/**
* ConfigBeanDefinitionParser的方法
*
* 解析 标签,也就是通知器标签,一并解析内部的 标签,生成bean定义并注册到容器注册表缓存中
*/
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
//根据 标签创建一个RootBeanDefinition类型的bean定义,beanClass类型为DefaultBeanFactoryPointcutAdvisor
//解析advice-ref和order属性,设置到bean定义的adviceBeanName和order属性中
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
//获取id属性的值
String id = advisorElement.getAttribute(ID);
try {
//新建一个AdvisorEntry点位,存入parseState,压栈
this.parseState.push(new AdvisorEntry(id));
//通知器bean定义的默认名字设置为id
String advisorBeanName = id;
if (StringUtils.hasText(advisorBeanName)) {
//如果设置了id属性,那么直接将beanName和BeanDefinition注册到registry的缓存中,这个方法在"IoC容器初始化(3)"的文章中已经讲过了
parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
} else {
//如果没有设置id属性,那么通过DefaultBeanNameGenerator生成beanName,随后同样注册到registry的缓存中,返回生成的beanName
advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
}
//解析当前 标签的pointcut或者pointcut-ref属性,获取切入点
//可能是一个切入点bean定义或者一个切入点bean定义的id
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
//如果是一个切入点bean定义,那么表示设置了pointcut属性,返回的就是根据切入点表达式创建的一个切入点bean定义
if (pointcut instanceof BeanDefinition) {
//为bean定义设置pointcut属性,值就是解析后的切入点bean定义
advisorDef.getPropertyValues().add(POINTCUT, pointcut);
//注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
}
//如果是一个字符串,那么表示设置了pointcut-ref属性,返回的就是该属性的值,表示引入的其他切入点bean定义的id
else if (pointcut instanceof String) {
//为bean定义设置pointcut属性,值就是pointcut-ref属性的值封装的一个RuntimeBeanReference,将会在运行时解析
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
//注册组件
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
}
} finally {
//AdvisorEntry点位,出栈
this.parseState.pop();
}
}
createAdvisorBeanDefinition方法用于创建一个通知器bean定义,用来描述、解析一个< aop:advisor/> 通知器标签。
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String ADVICE_REF = "advice-ref";
private static final String ORDER_PROPERTY = "order";
private static final String ADVICE_BEAN_NAME = "adviceBeanName";
/**
* ConfigBeanDefinitionParser的方法
*
* 创建beanClass类型为DefaultBeanFactoryPointcutAdvisor的bean定义,用于描述 通知器标签
*/
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
//新建RootBeanDefinition类型的bean定义,beanClass类型为DefaultBeanFactoryPointcutAdvisor
RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
//设置源
advisorDefinition.setSource(parserContext.extractSource(advisorElement));
//获取advice-ref属性的值,advice-ref可以传递一个id指向一个 标签,用来管理事务
//或者可以传递一个id或者name,指向一个实现了Advice接口的bean定义
String adviceRef = advisorElement.getAttribute(ADVICE_REF);
//如果没有设置这个属性,那就抛出异常:"'advice-ref' attribute contains empty value."
if (!StringUtils.hasText(adviceRef)) {
parserContext.getReaderContext().error(
"'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
} else {
//为bean定义设置adviceBeanName属性,值就是advice-ref属性的值封装的一个RuntimeBeanNameReference,将会在运行时解析
advisorDefinition.getPropertyValues().add(
ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef));
}
//如果设置了order属性
if (advisorElement.hasAttribute(ORDER_PROPERTY)) {
//那么为bean定义设置order属性,值就是order属性的值
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY));
}
//返回bean定义
return advisorDefinition;
}
parsePointcutProperty方法用于解析解析当前< aop:advisor/>标签的pointcut或者pointcut-ref属性,返回的切入点可能是一个切入点bean定义或者一个切入点bean定义的id字符串。
pointcut或者pointcut-ref属性只能且必须设置其中一个,如果设置了pointcut属性,那么解析为一个切入点bean定义对象并返回,如果设置了pointcut-ref属性,那么直接返回pointcut-ref属性的值,其他情况抛出异常。
这里的切入点bean定义作为内部bean,不会被注册到容器中。
//--------ConfigBeanDefinitionParser的相关属性---------
private static final String POINTCUT = "pointcut";
private static final String POINTCUT_REF = "pointcut-ref";
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 解析 标签的pointcut或者pointcut-ref属性,即获取Advisor生成器的Pointcut切入点
4. 5. @param parserContext 解析上下文
6. @param element 当前 标签元素
7. @return 如果设置了pointcut属性,那么解析为一个切入点bean定义对象并返回
8. 如果设置了pointcut-ref属性,那么直接返回pointcut-ref属性的值
9. 其他情况抛出异常
*/
@Nullable
private Object parsePointcutProperty(Element element, ParserContext parserContext) {
//如果当前 标签同时具有pointcut和pointcut-ref属性
//那么抛出异常:"Cannot define both 'pointcut' and 'pointcut-ref' on tag."
if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
parserContext.getReaderContext().error(
"Cannot define both 'pointcut' and 'pointcut-ref' on tag." ,
element, this.parseState.snapshot());
return null;
}
//否则,如果具有pointcut属性,那么一定没有pointcut-ref属性
else if (element.hasAttribute(POINTCUT)) {
//获取pointcut属性的值,也就是切入点表达式字符串
String expression = element.getAttribute(POINTCUT);
//创建切入点bean定义对象,bean定义类型为RootBeanDefinition,beanClass类型为AspectJExpressionPointcut
//scope属性设置为prototype,synthetic属性设置为true,设置expression属性的值为切入点表达式字符串
//这个方法我们在解析 标签的时候就见过了
AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
//设置源,属于当前 标签元素
pointcutDefinition.setSource(parserContext.extractSource(element));
//返回新建的切入点bean定义对象
return pointcutDefinition;
}
//否则,如果具有pointcut-ref属性,那么一定没有pointcut属性
else if (element.hasAttribute(POINTCUT_REF)) {
//获取pointcut-ref属性的值,也就是其他地方的 标签的id,表示引入其他外部切入点
String pointcutRef = element.getAttribute(POINTCUT_REF);
//如果是空白字符之类的无意义字符串,那么抛出异常:"'pointcut-ref' attribute contains empty value."
if (!StringUtils.hasText(pointcutRef)) {
parserContext.getReaderContext().error(
"'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
return null;
}
//直接返回pointcut-ref属性的值
return pointcutRef;
}
//否则,表示没有设置这两个属性的任何一个,同样抛出异常:"Must define one of 'pointcut' or 'pointcut-ref' on tag."
else {
parserContext.getReaderContext().error(
"Must define one of 'pointcut' or 'pointcut-ref' on tag." ,
element, this.parseState.snapshot());
return null;
}
}
< aop:aspect/>标签用于配置切面,其内部可以定义具体的应用到哪些切入点的通知,这是一个非常重要的标签,解析的源码也很多。
大概步骤如下:
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String ID = "id";
private static final String REF = "ref";
private static final String POINTCUT = "pointcut";
private static final String DECLARE_PARENTS = "declare-parents";
private static final int METHOD_INDEX = 0;
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 解析 标签,也就是切面标签,一并解析内部的子标签,生成bean定义并注册到容器注册表缓存中
4. 5. @param aspectElement 标签元素
6. @param parserContext 解析上下文
*/
private void parseAspect(Element aspectElement, ParserContext parserContext) {
//获取id属性的值,表示切面的id
String aspectId = aspectElement.getAttribute(ID);
//获取ref属性的值,表示引用一个通知类bean定义,内部具有的通知方法可以通知方法名直接被通知标签引用
String aspectName = aspectElement.getAttribute(REF);
try {
//新建一个AspectEntry点位,存入parseState,压栈
this.parseState.push(new AspectEntry(aspectId, aspectName));
// 标签解析到的bean定义集合
List<BeanDefinition> beanDefinitions = new ArrayList<>();
// 标签解析到的bean定义引用集合
List<BeanReference> beanReferences = new ArrayList<>();
/*
* 1 解析所有 引介增强子标签
*/
//获取全部 子标签元素集合,也就是引介增强标签
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
//从0索引开始遍历declareParents集合
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
//获取每一个 子标签元素
Element declareParentsElement = declareParents.get(i);
/*
* 通过parseDeclareParents解析 子标签元素,新建RootBeanDefinition类型的bean定义
* beanClass类型为DeclareParentsAdvisor,解析各种属性并赋值,default-impl和delegate-ref属性有且只能由其中一个
* 随后将新建的bean定义同样注册到注册表容器中,最后将返回的bean定义加入到beanDefinitions集合中
*/
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
/*
* 2 解析所有advice通知标签,包括 、 、 、
* 、 ,并且设置通知顺序
*/
//获取所有子节点元素,该方法对于标签之间的空白换行符号/n也会算作一个Node节点 -> DeferredTextImpl
//对于标签之间被注释的语句也会算作一个Node节点 -> DeferredCommentImpl
NodeList nodeList = aspectElement.getChildNodes();
//标志位,判断有没有发现任何通知标签,默认false
boolean adviceFoundAlready = false;
//遍历所有子节点元素
for (int i = 0; i < nodeList.getLength(); i++) {
//获取每一个节点
Node node = nodeList.item(i);
//如果是任何一个通知标签节点元素,那么就是true
if (isAdviceNode(node, parserContext)) {
//如果此前还没有解析到任何一个通知节点
if (!adviceFoundAlready) {
//adviceFoundAlready改为true
adviceFoundAlready = true;
//如果 标签的ref属性的没有设置值或者是空白字符等无效值
//那么抛出异常:" tag needs aspect bean reference via 'ref' attribute when declaring advices."
if (!StringUtils.hasText(aspectName)) {
parserContext.getReaderContext().error(
" tag needs aspect bean reference via 'ref' attribute when declaring advices." ,
aspectElement, this.parseState.snapshot());
return;
}
//如果设置了ref属性值,那么包装成为一个RuntimeBeanReference,加入到beanReferences集合中
beanReferences.add(new RuntimeBeanReference(aspectName));
}
/*
* 3 解析该通知标签,获取生成的通知bean定义,该bean定义已被注册到容器中类,beanClass类型为AspectJPointcutAdvisor
*/
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
//加入到beanReferences集合中
beanDefinitions.add(advisorDefinition);
}
}
//创建解析当前 标签的AspectComponentDefinition类型的bean定义
//内部包含了解析出来的全部bean定义和bean引用
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
//存入解析上下文内部的containingComponents集合中,入栈顶
parserContext.pushContainingComponent(aspectComponentDefinition);
/*
* 4 解析所有 切入点子标签
*/
//获取全部 子标签元素集合
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
/*
* 调用parsePointcut方法解析 标签
* 封装成为beanClass类型为AspectJExpressionPointcut类型的bean定义并且注册到IoC容器缓存中
* 该方法此前已经讲过了
*/
parsePointcut(pointcutElement, parserContext);
}
//出栈并注册,并不是注册到注册表中……,可能什么也不做
parserContext.popAndRegisterContainingComponent();
} finally {
//AspectEntry点位,出栈
this.parseState.pop();
}
}
在不修改源代码的前提下,< aop:declare-parents/>引介增强标签可以在运行期为类动态地添加一些额外的方法或属性,又被称为Introduction(引介)。
parseDeclareParents方法用于解析< aop:declare-parents/>子标签元素,解析的bean定义同样会注册到注册表中。
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String IMPLEMENT_INTERFACE = "implement-interface";
private static final String TYPE_PATTERN = "types-matching";
private static final String DEFAULT_IMPL = "default-impl";
private static final String DELEGATE_REF = "delegate-ref";
/**
* ConfigBeanDefinitionParser的方法
*
* 解析 引介增强标签元素,创建beanClass类型为DeclareParentsAdvisor的bean定义并注到容器缓存中
*
* @param declareParentsElement 标签元素
* @param parserContext 解析上下文
*/
private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
//新建RootBeanDefinition类型的bean定义,beanClass类型为DeclareParentsAdvisor
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);
//获取implement-interface和types-matching属性的值,并设置具有索引的bean定义构造器集合的前两位
builder.addConstructorArgValue(declareParentsElement.getAttribute(IMPLEMENT_INTERFACE));
builder.addConstructorArgValue(declareParentsElement.getAttribute(TYPE_PATTERN));
//获取default-impl和delegate-ref属性的值,也就是增强类
String defaultImpl = declareParentsElement.getAttribute(DEFAULT_IMPL);
String delegateRef = declareParentsElement.getAttribute(DELEGATE_REF);
//如果设置了default-impl并且没有设置delegate-ref
if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {
//那么将该属性的值加入具有索引的bean定义构造器集合的第三位
builder.addConstructorArgValue(defaultImpl);
}
//如果设置了delegate-ref并且没有设置default-impl
else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) {
//那么将该属性的值封装成一个RuntimeBeanReference对象,加入具有索引的bean定义构造器集合的第三位
builder.addConstructorArgReference(delegateRef);
}
//如果同时设置或者没设置这两个属性,那么抛出异常
else {
parserContext.getReaderContext().error(
"Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified",
declareParentsElement, this.parseState.snapshot());
}
//获取bean定义
AbstractBeanDefinition definition = builder.getBeanDefinition();
//设置源
definition.setSource(parserContext.extractSource(declareParentsElement));
// 标签没有id或者name属性,通过DefaultBeanNameGenerator生成beanName
//随后同样注册到registry的缓存中,返回生成的beanName
parserContext.getReaderContext().registerWithGeneratedName(definition);
//返回bean定义
return definition;
}
isAdviceNode方法用于判断是不是通知标签节点,如果是任何一个通知标签节点元素,那么就返回true,否则返回false。
通知标签是指< aop:before/>、< aop:after/>、< aop:after-returning/>、< aop:after-throwing/>、< aop:around/>这五个标签中的任何一个。
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String BEFORE = "before";
private static final String AFTER = "after";
private static final String AFTER_RETURNING_ELEMENT = "after-returning";
private static final String AFTER_THROWING_ELEMENT = "after-throwing";
private static final String AROUND = "around";
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 判断是不是通知标签节点
4. 如果是任何一个通知标签节点元素,那么就返回true,否则返回false
*/
private boolean isAdviceNode(Node aNode, ParserContext parserContext) {
//如果不是标签节点,直接返回null
if (!(aNode instanceof Element)) {
return false;
} else {
//获取标签节点的本地名称也就是去除"aop:"之后的名称
String name = parserContext.getDelegate().getLocalName(aNode);
//如果是任何一个通知标签节点元素,那么就返回true,否则返回false
return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||
AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));
}
}
parseAdvice方法用于解析所有advice通知标签,包括< aop:before/>、< aop:after/>、< aop:after-returning/>、< aop:after-throwing/>、< aop:around/>,解析的最终bean定义同样会注入到容器中,该方法比较复杂,bean定义的层级很深。
private static final String ORDER_PROPERTY = "order";
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 解析所有advice通知标签,包括 、 、 、 、
4. ,创建beanClass类型为MethodLocatingFactoryBean的bean定义并注到容器缓存中
5. 6. @param aspectName 外部 标签的ref属性值,也就是引用的通知类bean定义的id
7. @param order 顺序,实际上就是在当前外部 标签中的所有节点的定义顺序由上而下的索引值
8. @param aspectElement 外部 标签元素
9. @param adviceElement advice通知标签元素
10. @param parserContext 解析上下文
11. @param beanDefinitions beanDefinitions集合
12. @param beanReferences beanReferences集合
13. @return 生成的通知bean定义
*/
private AbstractBeanDefinition parseAdvice(
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
try {
//新建一个AdviceEntry点位,存入parseState,压栈
this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
/*
* 1 创建通知方法bean定义,用于获取通知对应的Method对象,在第三步会被用于构造通知bean定义
*/
//新建RootBeanDefinition类型的bean定义,beanClass类型为MethodLocatingFactoryBean
//MethodLocatingFactoryBean实现了FactoryBean接口,是一个方法工厂,专门用于获取通知对应的Method对象
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
//设置bean定义的targetBeanName属性,值就是外部 标签的ref属性值,也就是引用的通知类bean定义的id
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
//设置bean定义的methodName属性,值就是method属性值
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
//设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的bean
methodDefinition.setSynthetic(true);
/*
* 2 创建切面实例类bean定义,用于获取切面实例对象,也就是通知类对象,在第三步会被用于构造通知bean定义
*/
//新建RootBeanDefinition类型的bean定义,beanClass类型为SimpleBeanFactoryAwareAspectInstanceFactory
//实现了AspectInstanceFactory接口,是一个实例工厂,专门用于获取切面实例对象,也就是通知类对象
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
//设置bean定义的aspectBeanName属性,值就是外部 标签的ref属性值,也就是引用的通知类bean定义的id
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
//设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的bean
aspectFactoryDef.setSynthetic(true);
// register the pointcut
/*
* 3 创建advice通知bean定义
*/
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
// configure the advisor
/*
* 4 创建切入点通知器bean定义
*/
//新建RootBeanDefinition类型的bean定义,beanClass类型为AspectJPointcutAdvisor
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
//设置bean定义的构造器参数,值就是上面创建的advice通知bean定义adviceDef
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
//如果外部 标签元素具有order属性
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
//设置bean定义的order属性,值就是外部 标签元素的order属性值
//用来控制切入点方法的优先级
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
/*
* 5 注册通知器bean定义
*/
//通过DefaultBeanNameGenerator生成beanName,随后将最终得到的切入点通知器bean定义同样注册到registry的缓存中
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
//返回通知器bean定义
return advisorDefinition;
} finally {
//AdviceEntry点位,出栈
this.parseState.pop();
}
}
createAdviceDefinition方法解析当前通知标签的各种属性,并且将前两个bean定义作为构造器参数构建通知bean定义。
为什么要设置三个构造器参数?因为所有的通知标签bean定义的beanClass有且只有一个三个参数的构造器。
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String ASPECT_NAME_PROPERTY = "aspectName";
private static final String DECLARATION_ORDER_PROPERTY = "declarationOrder";
private static final String RETURNING = "returning";
private static final String RETURNING_PROPERTY = "returningName";
private static final String THROWING = "throwing";
private static final String THROWING_PROPERTY = "throwingName";
private static final String ARG_NAMES = "arg-names";
private static final String ARG_NAMES_PROPERTY = "argumentNames";
private static final int METHOD_INDEX = 0;
private static final int POINTCUT_INDEX = 1;
private static final int ASPECT_INSTANCE_FACTORY_INDEX = 2;
/**
1. ConfigBeanDefinitionParser的方法
2.
3. 创建一个advice通知bean定义,beanClass类型为该通知标签对应的实现类类型,还会解析内部的切入点,
4. 5. @param adviceElement advice通知标签元素
6. @param parserContext 解析上下文
7. @param aspectName 外部 标签的ref属性值,也就是引用的通知类bean定义的id
8. @param order 顺序,实际上就是在当前外部 标签中的所有节点的定义顺序由上而下的索引值
9. @param methodDef 通知方法bean定义
10. @param aspectFactoryDef 切面通知类bean定义
11. @param beanDefinitions beanDefinitions集合
12. @param beanReferences beanReferences集合
*/
private AbstractBeanDefinition createAdviceDefinition(
Element adviceElement, ParserContext parserContext, String aspectName, int order,
RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
//新建RootBeanDefinition类型的bean定义,beanClass类型为该通知标签对应的实现类类型
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
adviceDefinition.setSource(parserContext.extractSource(adviceElement));
//设置bean定义的aspectName属性,值就是外部 标签的ref属性值,也就是引用的通知类bean定义的id
adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
//设置bean定义的declarationOrder属性,值就是在当前外部 标签中的所有节点的定义顺序由上而下的索引值
adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
//如果具有returning属性,说明是后置通知
if (adviceElement.hasAttribute(RETURNING)) {
//设置bean定义的returningName属性,值就是returning属性的值
adviceDefinition.getPropertyValues().add(
RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
}
//如果具有throwing属性,说明是异常通知
if (adviceElement.hasAttribute(THROWING)) {
//设置bean定义的throwingName属性,值就是throwing属性的值
adviceDefinition.getPropertyValues().add(
THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
}
//如果具有arg-names属性,这表示接收目标方法的参数
if (adviceElement.hasAttribute(ARG_NAMES)) {
//设置bean定义的argumentNames属性,值就是arg-names属性的值
adviceDefinition.getPropertyValues().add(
ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
}
//获取构造器参数
ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
/*
* 设置构造器第一个参数属性,值为methodDef,也就是此前构建的通知方法bean定义
*/
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
//解析当前通知标签的pointcut或者pointcut-ref属性,获取切入点,返回值可能是一个切入点bean定义或者一个切入点bean定义的id
//这个方法我们在前面讲过了
Object pointcut = parsePointcutProperty(adviceElement, parserContext);
//如果是一个切入点bean定义,那么表示设置了pointcut属性,返回的就是根据切入点表达式创建的一个切入点bean定义
if (pointcut instanceof BeanDefinition) {
/*
* 为bean定义设置构造器第二个参数属性,值就是解析后的切入点bean定义
*/
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
//加入beanDefinitions集合
beanDefinitions.add((BeanDefinition) pointcut);
}
//如果是一个字符串,那么表示设置了pointcut-ref属性,返回的就是该属性的值,表示引入的其他切入点bean定义的id
else if (pointcut instanceof String) {
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
/*
* 为bean定义设置构造器第二个参数属性,值就是pointcut-ref属性的值封装的一个RuntimeBeanReference,将会在运行时解析
*/
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
//加入beanReferences集合
beanReferences.add(pointcutRef);
}
/*
* 为bean定义设置构造器第三个参数属性,值就是切面通知类bean定义,也就是此前构建的切面通知类bean定义
*/
cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
//返回bean定义
return adviceDefinition;
}
getAdviceClass方法用于获取给定通知标签对应的通知类的beanClass的类型。
//-------ConfigBeanDefinitionParser的相关属性--------
private static final String BEFORE = "before";
private static final String AFTER = "after";
private static final String AFTER_RETURNING_ELEMENT = "after-returning";
private static final String AFTER_THROWING_ELEMENT = "after-throwing";
private static final String AROUND = "around";
/**
* ConfigBeanDefinitionParser的方法
*
* 获取给定通知元素对应的bean定义的beanClass的实现类类型
*/
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
//获取该通知标签的本地名称
String elementName = parserContext.getDelegate().getLocalName(adviceElement);
//根据不同的通知类型返回不同的Class
if (BEFORE.equals(elementName)) {
//如果是 通知,那么返回AspectJMethodBeforeAdvice.class
return AspectJMethodBeforeAdvice.class;
} else if (AFTER.equals(elementName)) {
//如果是 通知,那么返回AspectJAfterAdvice.class
return AspectJAfterAdvice.class;
} else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
//如果是 通知,那么返回AspectJAfterReturningAdvice.class
return AspectJAfterReturningAdvice.class;
} else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
//如果是 通知,那么返回AspectJAfterThrowingAdvice.class
return AspectJAfterThrowingAdvice.class;
} else if (AROUND.equals(elementName)) {
//如果是 通知,那么返回AspectJAroundAdvice.class
return AspectJAroundAdvice.class;
} else {
//其他情况,抛出异常
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
}
}
com.spring.aop.aopconfig.AspectTarget类用于目标被代理类,com.spring.aop.aopconfig.AspectMethod类用于切面通知方法类:
public class AspectTarget {
/**
* 被代理的方法
*/
public void target(){
System.out.println("----------target---------------");
}
}
public class AspectMethod {
/**
* 进行通知的方法
*/
public void aspect() {
System.out.println("--------aspect--------");
}
}
spring-aop-aopconfig.xml文件的配置如下:
<bean class="com.spring.aop.aopconfig.AspectTarget" id="aspectTarget"/>
<bean class="com.spring.aop.aopconfig.AspectMethod" name="aspectMethod"/>
<aop:config expose-proxy="true">
<aop:pointcut id="pointcut" expression="execution(* *(..))"/>
<aop:aspect id="aspect" ref="aspectMethod">
<aop:before method="aspect" pointcut-ref="pointcut"/>
<aop:after method="aspect" pointcut-ref="pointcut"/>
<aop:after method="aspect" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
测试:
@Test
public void aopconfig() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-aop-aopconfig.xml");
ac.getBean(AspectTarget.class).target();
}
结果如下:
--------aspect--------
----------target---------------
--------aspect--------
--------aspect--------
可以看到,成功的进行了代理。那么问题来了,在解析全部标签之后,容器中一共有多少个AOP相关的bean定义呢?
我们在AbstractApplicationContext的prepareBeanFactory方法调用处打上断点,查看bean定义的缓存,即可知道所有标签解析完毕之后的AOP相关的bean定义一共有7个:
上面我们见识到了< aop:config/>标签的全部解析流程,我们简单的总结一下它的主要流程:
如果我们采用XML配置Spring AOP,那么只需要配置< aop:config/>标签就行了,当前全部标签解析完毕,仅仅是向容器中注册了一些bean定义,我们在后面学习基于注解的AOP配置时,我们会知道,基于注解的AOP配置不会注册这么多bean定义,比如通知器相关的对象,比如切入点对象,通常是new对象。
并且,此时还没有进行真正的创建代理对象操作,创建代理对象的操作实际上是在自动代理创建者AspectJAwareAdvisorAutoProxyCreator内部完成的!下一篇文章,我们将会分析AspectJAwareAdvisorAutoProxyCreator创建代理对象的源码!
相关文章:
https://spring.io/
Spring Framework 5.x 学习
Spring Framework 5.x 源码
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!