基于最新Spring 5.x,对Spring AOP中的<aop:config/>标签的解析源码进行了详细分析,这是Spring AOP源码的入口!

  此前我们已经详细学习了Spring AOP的基本使用,现在我们来学习Spring AOP的源码,学习AOP源码的前提要求是会使用AOP,同时要求我们了解IoC容器初始化的相关源码,否则很多的方法和和名词会让人摸不着头脑,比如bean定义是什么?如何注册bean定义?等等,这些都是IoC源码的核心知识点,我们此前的文章花费了大量时间和文字去讲解Spring IoC容器初始化的源码,在此不做赘述,默认大家都是了解的!

Spring AOP源码 系列文章

Spring AOP源码(1)—<aop:config/>AOP配置标签解析

Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象

Spring AOP源码(3)—invoke代理方法的调用与执行增强

Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator

Spring AOP源码(5)—DefaultAdvisorAutoProxyCreator自动代理创建者

文章目录

  • Spring AOP源码 系列文章
  • 1 Spring AOP源码概述
  • 2 < aop:config/> AOP配置标签解析
    • 2.1 configureAutoProxyCreator配置自动代理创建者
      • 2.1.1 registerAspectJAutoProxyCreatorIfNecessary注册自动代理创建者
        • 2.1.1.1 registerAspectJAutoProxyCreatorIfNecessary注册/升级自动代理创建者
          • 2.1.1.1.1 registerOrEscalateApcAsRequired注册/升级自动代理创建者
            • 2.1.1.1.1.1 findPriorityForClass获取class的优先级
        • 2.1.1.2 useClassProxyingIfNecessary解析proxy-target-class、expose-proxy属性
          • 2.1.1.2.1 forceAutoProxyCreatorToUseClassProxying强制使用CGLIB代理
          • 2.1.1.2.2 forceAutoProxyCreatorToExposeProxy强制暴露代理对象
        • 2.1.1.3 registerComponentIfNecessary注册组件
    • 2.2. parsePointcut解析< aop:pointcut/>切入点标签
      • 2.2.1 createPointcutDefinition创建切入点bean定义
      • 2.2.2 registerWithGeneratedName生成beanName注册bean定义
    • 2.3 parseAdvisor解析通知器标签
      • 2.3.1 createAdvisorBeanDefinition创建通知器bean定义
      • 2.3.2 parsePointcutProperty解析切入点属性
    • 2.4 parseAspect解析< aop:aspect/>切面标签
      • 2.4.1 parseDeclareParents解析< aop:declare-parents/>引介增强标签
      • 2.4.2 isAdviceNode判断通知标签
      • 2.4.3 parseAdvice解析通知标签
        • 2.4.3.1 createAdviceDefinition创建通知bean定义
          • 2.4.3.1.1 getAdviceClass获取通知的beanClass
  • 3 < aop:config/> 案例
  • 4 < aop:config/> 标签解析总结

1 Spring AOP源码概述

  在此前IoC容器初始化(3)的文章中,我们说过,对于扩展标签的解析是在parseCustomElement方法中完成的,不同扩展标签的解析,是根据该标签的本地名称去从NamespaceHandlerSupport的parsers缓存中获取对应的BeanDefinitionParser解析器来完成的。
  对于aop命名空间下的系列的标签的解析器,都是通过AopNamespaceHandler注册到parsers缓存中的,从该类中我们能知道所有aop系列标签及其子标签的解析器:

/*** aop 命名空间处理器*/
public class AopNamespaceHandler extends NamespaceHandlerSupport {/*** 注册一些列的BeanDefinitionParser,用于解析<aop:config/>* <aop:aspectj-autoproxy/>、<aop:scoped-proxy/>、<aop:spring-configured/>标签及其子标签*/@Overridepublic 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的配置终究都要融于一个容器中。

2 < aop:config/> AOP配置标签解析

  我们先学习基于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的方法* 解析<aop:config/>标签及其子标签** @param element       <aop:config/>标签元素* @param parserContext 解析上下文* @return 解析结果,固定返回null*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {//新建一个CompositeComponentDefinition类型的bean定义,名称就是标签名 aop:config//内部保存了多个ComponentDefinition,基于XML的source默认为nullCompositeComponentDefinition compositeDef =new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));//存入解析上下文内部的containingComponents集合中,入栈顶parserContext.pushContainingComponent(compositeDef);/** 尝试向容器注入或者升级AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者bean定义,专门用于后续创建AOP代理对象* 这个类还实现了比如BeanClassLoaderAware、BeanFactoryAware、SmartInstantiationAwareBeanPostProcessor、* InstantiationAwareBeanPostProcessor、BeanPostProcessor …… 等一系列的自动回调接口,它们在创建代理对象的过程中非常有用** <aop:config/>标签使用AspectJAwareAdvisorAutoProxyCreator创建代理,实际上还有很多创建者可以用于创建代理对象* 比如<aop:aspectj-autoproxy/>以及@EnableAspectJAutoProxy使用AnnotationAwareAspectJAutoProxyCreator* <tx:annotation-driven/>以及@EnableTransactionManagement使用InfrastructureAdvisorAutoProxyCreator* 不同的标签或者注解使用不同的创建者,但容器最终只会创建一个bean定义,采用优先级最高的自动代理创建者的类型,我们后面会讲到*/configureAutoProxyCreator(parserContext, element);//获取<aop:config/>标签下的所有子标签结点元素List<Element> childElts = DomUtils.getChildElements(element);//遍历解析for (Element elt : childElts) {//获取子标签本地名称,即去除"aop:"之后的名称String localName = parserContext.getDelegate().getLocalName(elt);//如果是 <aop:pointcut/> 标签if (POINTCUT.equals(localName)) {/** 调用parsePointcut方法解析 <aop:pointcut/> 标签*/parsePointcut(elt, parserContext);}//如果是 <aop:advisor/> 标签else if (ADVISOR.equals(localName)) {/** 调用parseAdvisor方法解析 <aop:advisor/> 标签*/parseAdvisor(elt, parserContext);}//如果是 <aop:aspect/> 标签else if (ASPECT.equals(localName)) {/** 调用parseAspect方法解析 <aop:aspect/> 标签*/parseAspect(elt, parserContext);}}//出栈并注册,并不是注册到注册表中……,可能什么也不做parserContext.popAndRegisterContainingComponent();//返回nullreturn 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();
}

2.1 configureAutoProxyCreator配置自动代理创建者

  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. <p>3. 通过<aop:config/>标签的解析触发调用,尝试配置AspectJAwareAdvisorAutoProxyCreator类型的自动代理创建者的bean定义到容器*/
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {//调用AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary方法AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}

  可以看到,该方法直接委托AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary方法继续操作!

2.1.1 registerAspectJAutoProxyCreatorIfNecessary注册自动代理创建者

  分为三步:

  1. 调用AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary,尝试注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator",类型为AspectJAwareAdvisorAutoProxyCreator的自动代理创建者的bean定义。
  2. 调用useClassProxyingIfNecessary,解析proxy-target-class与expose-proxy属性。
  3. 调用registerComponentIfNecessary注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中。

这几个方法都是通用方法,我们在后面的文章中还会见到,到时候不再赘述!

/*** AopNamespaceUtils的方法* <p>* 如有必要,注册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);
}

2.1.1.1 registerAspectJAutoProxyCreatorIfNecessary注册/升级自动代理创建者

  AopConfigUtils的同名方法,内部继续调用registerOrEscalateApcAsRequired方法,由于解析的< aop:config />标签,因此第一个参数是AspectJAwareAdvisorAutoProxyCreator.class类型。

/**1. AopConfigUtils的方法2. <p>3. 如有必要,注册自动代理创建者,类型为AspectJAwareAdvisorAutoProxyCreator.class*/
@Nullable
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {/** 继续调用registerOrEscalateApcAsRequired方法* 由于解析的<aop:config />标签,因此第一个参数是AspectJAwareAdvisorAutoProxyCreator.class*/return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
2.1.1.1.1 registerOrEscalateApcAsRequired注册/升级自动代理创建者

  核心方法终于到了,从源码中就可以看出“升级”或者“新增”的逻辑:

  1. 容器最终只会创建一个自动代理创建者bean定义,采用优先级最高的自动代理创建者的类型。首先尝试获取名为"org.springframework.aop.config.internalAutoProxyCreator"的bean定义,如果已存在,那么比较bean定义中的自动代理创建者的类型和当前参数传递的自动代理创建者的类型的优先级,如果当前参数传递的自动代理创建者的类型的优先级更高,那么那么bean定义的beanClass属性设置为使用当前参数传递的自动代理创建者的类型的className,即升级bean定义,最后返回null。

    1. 设置bean定义的order属性优先级最高,也就是说将会最先被应用并尝试创建代理对象。
  2. 如果没有该名字的bean定义,那么使用当前参数class类型,也就是AspectJAwareAdvisorAutoProxyCreator.class类型作为beanClass,新建一个RootBeanDefinition类型的bean定义,以"org.springframework.aop.config.internalAutoProxyCreator"为beanName,通过registerBeanDefinition注册到容器中(该方法在此前IoC容器初始化(3)的源码文章中已经讲过了)。最后返回新增的bean定义。
/*** AopConfigUtils的属性* <p>* Spring内部管理的自动代理创建者的 beanName*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";/**1. AopConfigUtils的方法2. <p>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;
}
2.1.1.1.1.1 findPriorityForClass获取class的优先级

  该方法用于获取自动类型创建者的优先级。实际上就是从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。

  实际上,在上一个方法中:

  1. 如果是解析< tx:annotation-driven />标签或者@EnableTransactionManagement事物注解,那么cls参数是InfrastructureAdvisorAutoProxyCreator.class;
  2. 如果是解析< aop:config />标签,那么cls参数是AspectJAwareAdvisorAutoProxyCreator.class;
  3. 如果是解析< aop:aspectj-autoproxy />标签或者@EnableAspectJAutoProxy注解,那么cls参数是AnnotationAwareAspectJAutoProxyCreator.class。

  所以说,如果我们设置了< aop:config />和< aop:aspectj-autoproxy />两个标签,那么最终会注册AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者。

//---------AopConfigUtils的相关属性--------/*** 按升序顺序存储的自动代理创建者的类型集合* 可以看到,默认有三种类型,优先级就是比较索引顺序的大小,因此优先级为:* InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator* <p>* 如果是解析<tx:annotation-driven />标签或者@EnableTransactionManagement事物注解,那么cls参数是InfrastructureAdvisorAutoProxyCreator.class* 如果是解析<aop:config />标签,那么cls参数是AspectJAwareAdvisorAutoProxyCreator.class* 如果是解析<aop:aspectj-autoproxy />标签或者@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. <p>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");
}

2.1.1.2 useClassProxyingIfNecessary解析proxy-target-class、expose-proxy属性

  在升级或者注册了自动代理创建者的bean定义之后,这一步用于解析、设置proxy-target-class和expose-proxy属性到这个bean定义的proxyTargetClass和exposeProxy属性中。

  1. proxy-target-class属性用于设置代理模式,默认为false,即优先JDK动态代理,其次CGLIB代理,可以设置为true,表示强制为CGLIB代理。
  2. expose-proxy属性用于暴露代理对象,主要用来解决同一个目标类的方法互相调用时代理不生效的问题。默认值为false表示不开启,设置为true表示开启,可以在代码中通过AopContext.currentProxy()获取当前代理类对象。
//------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的方法* <p>* 解析设置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);}}
}
2.1.1.2.1 forceAutoProxyCreatorToUseClassProxying强制使用CGLIB代理

  如果proxy-target-class属性为true,那么强制使用CGLIB创建代理对象,这里仅仅是设置bean定义的proxyTargetClass属性值为true,后面才会用到。这实际上是顶级父类ProxyConfig的属性。

/*** AopConfigUtils的属性* <p>* Spring内部管理的自动代理创建者的 beanName*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";/*** AopConfigUtils的方法* <p>* 强迫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);}
}
2.1.1.2.2 forceAutoProxyCreatorToExposeProxy强制暴露代理对象

  如果expose-proxy属性为true,那么强制暴露代理对象,这里仅仅是设置bean定义的exposeProxy属性值为true,后面才会用到。这实际上是顶级父类ProxyConfig的属性。

/*** AopConfigUtils的属性* <p>* Spring内部管理的自动代理创建者的 beanName*/
public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";/*** AopConfigUtils的方法* <p>* 强迫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);}
}

2.1.1.3 registerComponentIfNecessary注册组件

  如果bean定义不为null,即新增了bean定义,那么注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中,或者广播事件,而不是注册到注册表中。

/**1. AopNamespaceUtils的方法2. <p>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));}
}

2.2. parsePointcut解析< aop:pointcut/>切入点标签

  parsePointcut方法用于解析< aop:pointcut/>切入点标签,并将一个< aop:pointcut/>标签封装成为一个bean定义并且注册到IoC容器缓存中:

  1. bean定义类型为RootBeanDefinition;beanClass类型为AspectJExpressionPointcut;scope属性设置为prototype,即原型;synthetic属性设置为true,即表示它是一个合成的而不是不是由程序本身定义的bean;最后为bean定义添加一个expression属性,值为XML标签的expression属性,即切入点表达式字符串,该属性是必须的。
  2. 默认采用指定的id属性作为beanName,如果没设置id属性,那么采用DefaultBeanNameGenerator作为beanName生成器来生成beanName,生成的beanName类似于 “org.springframework.aop.aspectj.AspectJExpressionPointcut #0”,具体规则我们在此前IoC容器初始化(3)的文章中已经讲过了。
//-------ConfigBeanDefinitionParser的相关属性--------private static final String EXPRESSION = "expression";
private static final String ID = "id";/*** 用于存储解析的阶段点位* 内部是一个LinkedList集合,可以模拟栈*/
private ParseState parseState = new ParseState();/*** ConfigBeanDefinitionParser的方法* <p>* 解析<aop:pointcut/> 标签,也就是切入点表达式标签,生成bean定义注册到容器中** @param pointcutElement <aop:pointcut/> 标签元素* @param parserContext   解析上下文*/
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {//获取id属性值String id = pointcutElement.getAttribute(ID);//获取expression属性值,也就是切入点表达式字符串String expression = pointcutElement.getAttribute(EXPRESSION);//<aop:pointcut/> 标签对应的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定义的默认名字设置为idString pointcutBeanName = id;//如果设置了id属性if (StringUtils.hasText(pointcutBeanName)) {/** 那么将beanName和BeanDefinition注册到registry的缓存中* 这个方法在"IoC容器初始化(3)"的文章中已经讲过了*/parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);}//如果没有设置id属性else {//那么生成beanName,随后同样注册到registry的缓存中,返回生成的beanNamepointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);}//注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中parserContext.registerComponent(new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));} finally {//PointcutEntry点位出栈this.parseState.pop();}//返回创建的bean定义return pointcutDefinition;
}

2.2.1 createPointcutDefinition创建切入点bean定义

  createPointcutDefinition方法用于创建一个切入点bean定义,用来描述、解析一个< aop:pointcut/>切入点标签。
  注意,它的synthetic属性为true,这表示切入点bean定义是一个合成的而不是不是由程序本身定义的bean,学习Spring源码以来,我们终于见到一个合成的bean定义了。还记得此前IoC源码学习时遇到的isSynthetic方法吗,就是用于判断某个bean定义是不是合成的bean定义。

private static final String EXPRESSION = "expression";/**1. ConfigBeanDefinitionParser的方法2. <p>3. 使用给定的切入点表达式创建AspectJExpressionPointcut类型的bean定义对象*/
protected AbstractBeanDefinition createPointcutDefinition(String expression) {//新建RootBeanDefinition类型的bean定义,beanClass类型为AspectJExpressionPointcutRootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);//设置scope属性为prototypebeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);//设置synthetic属性为true,这表示它是一个合成的而不是不是由程序本身定义的beanbeanDefinition.setSynthetic(true);//添加expression属性值为参数的切入点表达式字符串beanDefinition.getPropertyValues().add(EXPRESSION, expression);//返回创建的bean定义return beanDefinition;
}

2.2.2 registerWithGeneratedName生成beanName注册bean定义

  如果没有设置id,那么生成beanName,采用的生成器默认就是DefaultBeanNameGenerator,规则如下,具体的源码我们在"IoC容器初始化(3)"的文章中已经讲过了,在此不再赘述:

  1. 外部bean的beanName生成规则就是:“类的全路径名#0”、“类的全路径名#1”、“类的全路径名#2”……。
  2. 有n个没有命名的同类型外部bean,那么名字后面就是从[0,n-1]类似于索引递增的进行命名,如果中途遇到同名bean,那么跳过这个索引,使用下一个。

  如果没指定id,那么生成的切入点beean定义的beanName就是"org.springframework.aop.aspectj. AspectJExpressionPointcut#0"、“org.springframework.aop.aspectj. AspectJExpressionPointcut#1”……

/*** XmlReaderContext的方法* <p>* 调用给定的 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);//返回生成的beanNamereturn 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;

2.3 parseAdvisor解析aop:advisor/通知器标签

  < aop:advisor/>通知器标签,它和< aop:aspect >类似,相当于一个小切面,它的advice-ref属性可以指向一个通知类bean,该bean要求实现Advice相关接口,不需要单独配置通知,但接口只有前置通知、后置通知和异常通知的方法。通常,advisor被用来管理事物,它的advice-ref属性配置对一个< tx:advice >的id引用,后面学习事物的时候就知道了。
  parseAdvisor方法用于解析< aop:advisor/>通知器标签,并将一个aop:advisor/标签封装成为一个bean定义并且注册到IoC容器缓存中:

  1. bean定义类型为RootBeanDefinition;beanClass类型为DefaultBeanFactoryPointcutAdvisor;

    1. advice-ref属性是必须的,将会设置给bean定义的adviceBeanName属性,值就是advice-ref属性的值封装的一个RuntimeBeanNameReference,将会在运行时解析;
    2. 如果设置了order属性,那么为bean定义设置order属性,值就是order属性的值,用于通知器的排序。
  2. 通知器bean定义的默认名字设置为id,如果没设置id属性,那么采用DefaultBeanNameGenerator作为beanName生成器来生成beanName,生成的beanName类似于 “org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0”。
  3. 对于< aop:advisor/>标签的pointcut以及pointcut-ref属性,必须且只能设置其中一个:
    1. 如果设置了pointcut属性,那么通过createPointcutDefinition创建一个切入点bean定义,这个的切入点bean定义作为内部bean,不会被注册到容器中。
    2. 如果设置了pointcut-ref属性,那么表示通过id引入外部的切入点。那么将ID字符串封装为一个RuntimeBeanReference,将会在运行时解析。
    3. 最终会将切入点bean定义或者RuntimeBeanReference设置给bean定义的pointcut属性。
//-------ConfigBeanDefinitionParser的相关属性--------private static final String ID = "id";
private static final String POINTCUT = "pointcut";/*** ConfigBeanDefinitionParser的方法* <p>* 解析<aop:advisor/> 标签,也就是通知器标签,一并解析内部的<aop:pointcut/> 标签,生成bean定义并注册到容器注册表缓存中*/
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {//根据<aop:advisor/> 标签创建一个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定义的默认名字设置为idString advisorBeanName = id;if (StringUtils.hasText(advisorBeanName)) {//如果设置了id属性,那么直接将beanName和BeanDefinition注册到registry的缓存中,这个方法在"IoC容器初始化(3)"的文章中已经讲过了parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);} else {//如果没有设置id属性,那么通过DefaultBeanNameGenerator生成beanName,随后同样注册到registry的缓存中,返回生成的beanNameadvisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);}//解析当前<aop:pointcut/>标签的pointcut或者pointcut-ref属性,获取切入点//可能是一个切入点bean定义或者一个切入点bean定义的idObject 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定义的idelse 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();}
}

2.3.1 createAdvisorBeanDefinition创建通知器bean定义

  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的方法* <p>* 创建beanClass类型为DefaultBeanFactoryPointcutAdvisor的bean定义,用于描述<aop:advisor/> 通知器标签*/
private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {//新建RootBeanDefinition类型的bean定义,beanClass类型为DefaultBeanFactoryPointcutAdvisorRootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);//设置源advisorDefinition.setSource(parserContext.extractSource(advisorElement));//获取advice-ref属性的值,advice-ref可以传递一个id指向一个<tx:advice/>标签,用来管理事务//或者可以传递一个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;
}

2.3.2 parsePointcutProperty解析切入点属性

  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. <p>3. 解析<aop:advisor/>标签的pointcut或者pointcut-ref属性,即获取Advisor生成器的Pointcut切入点4.  5. @param parserContext 解析上下文6. @param element       当前<aop:advisor/>标签元素7. @return 如果设置了pointcut属性,那么解析为一个切入点bean定义对象并返回8. 如果设置了pointcut-ref属性,那么直接返回pointcut-ref属性的值9. 其他情况抛出异常*/
@Nullable
private Object parsePointcutProperty(Element element, ParserContext parserContext) {//如果当前<aop:advisor/>标签同时具有pointcut和pointcut-ref属性//那么抛出异常:"Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag."if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {parserContext.getReaderContext().error("Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> 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属性的值为切入点表达式字符串//这个方法我们在解析<aop:pointcut/>标签的时候就见过了AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);//设置源,属于当前<aop:advisor/>标签元素pointcutDefinition.setSource(parserContext.extractSource(element));//返回新建的切入点bean定义对象return pointcutDefinition;}//否则,如果具有pointcut-ref属性,那么一定没有pointcut属性else if (element.hasAttribute(POINTCUT_REF)) {//获取pointcut-ref属性的值,也就是其他地方的<aop:pointcut/> 标签的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 <advisor> tag."else {parserContext.getReaderContext().error("Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",element, this.parseState.snapshot());return null;}
}

2.4 parseAspect解析< aop:aspect/>切面标签

  < aop:aspect/>标签用于配置切面,其内部可以定义具体的应用到哪些切入点的通知,这是一个非常重要的标签,解析的源码也很多。
  大概步骤如下:

  1. 解析所有< aop:declare-parents/>引介增强子标签,主要方法是parseDeclareParents。bean定义的beanClass类型为DeclareParentsAdvisor,会注册到注册表中。
  2. 解析所有advice通知子标签,包括< aop:before/>、< aop:after/>、< aop:after-returning/>、< aop:after-throwing/>、< aop:around/>,并且设置通知顺序,主要方法是parseAdvice。最终bean定义的beanClass类型为AspectJPointcutAdvisor,会注册到注册表中,它内部还有一些内部bean,不会被注册。
  3. 解析所有< aop:pointcut/>切入点子标签,主要方法是parsePointcut这个方法我们前面就讲过了。bean定义的beanClass类型为AspectJExpressionPointcut,会注册到注册表中。
  4. 可以看到,这个< aop:aspect/>标签本身并不会被注册成为一个bean定义,而是对于它内部的子标签分别注册成为bean定义。
//-------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. <p>3. 解析<aop:aspect/>标签,也就是切面标签,一并解析内部的子标签,生成bean定义并注册到容器注册表缓存中4.  5. @param aspectElement <aop:aspect/>标签元素6. @param parserContext 解析上下文*/
private void parseAspect(Element aspectElement, ParserContext parserContext) {//获取id属性的值,表示切面的idString aspectId = aspectElement.getAttribute(ID);//获取ref属性的值,表示引用一个通知类bean定义,内部具有的通知方法可以通知方法名直接被通知标签引用String aspectName = aspectElement.getAttribute(REF);try {//新建一个AspectEntry点位,存入parseState,压栈this.parseState.push(new AspectEntry(aspectId, aspectName));//<aop:aspect/>标签解析到的bean定义集合List<BeanDefinition> beanDefinitions = new ArrayList<>();//<aop:aspect/>标签解析到的bean定义引用集合List<BeanReference> beanReferences = new ArrayList<>();/** 1 解析所有<aop:declare-parents/>引介增强子标签*///获取全部<aop:declare-parents/>子标签元素集合,也就是引介增强标签List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);//从0索引开始遍历declareParents集合for (int i = METHOD_INDEX; i < declareParents.size(); i++) {//获取每一个<aop:declare-parents/>子标签元素Element declareParentsElement = declareParents.get(i);/** 通过parseDeclareParents解析<aop:declare-parents/>子标签元素,新建RootBeanDefinition类型的bean定义* beanClass类型为DeclareParentsAdvisor,解析各种属性并赋值,default-impl和delegate-ref属性有且只能由其中一个* 随后将新建的bean定义同样注册到注册表容器中,最后将返回的bean定义加入到beanDefinitions集合中*/beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));}/** 2 解析所有advice通知标签,包括<aop:before/>、<aop:after/>、<aop:after-returning/>、* <aop:after-throwing/>、<aop:around/>,并且设置通知顺序*///获取所有子节点元素,该方法对于标签之间的空白换行符号/n也会算作一个Node节点 -> DeferredTextImpl//对于标签之间被注释的语句也会算作一个Node节点 -> DeferredCommentImplNodeList nodeList = aspectElement.getChildNodes();//标志位,判断有没有发现任何通知标签,默认falseboolean adviceFoundAlready = false;//遍历所有子节点元素for (int i = 0; i < nodeList.getLength(); i++) {//获取每一个节点Node node = nodeList.item(i);//如果是任何一个通知标签节点元素,那么就是trueif (isAdviceNode(node, parserContext)) {//如果此前还没有解析到任何一个通知节点if (!adviceFoundAlready) {//adviceFoundAlready改为trueadviceFoundAlready = true;//如果<aop:aspect/>标签的ref属性的没有设置值或者是空白字符等无效值//那么抛出异常:"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices."if (!StringUtils.hasText(aspectName)) {parserContext.getReaderContext().error("<aspect> 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);}}//创建解析当前<aop:aspect/>标签的AspectComponentDefinition类型的bean定义//内部包含了解析出来的全部bean定义和bean引用AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);//存入解析上下文内部的containingComponents集合中,入栈顶parserContext.pushContainingComponent(aspectComponentDefinition);/** 4 解析所有<aop:pointcut/>切入点子标签*///获取全部<aop:pointcut/>子标签元素集合List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);for (Element pointcutElement : pointcuts) {/** 调用parsePointcut方法解析 <aop:pointcut/> 标签* 封装成为beanClass类型为AspectJExpressionPointcut类型的bean定义并且注册到IoC容器缓存中* 该方法此前已经讲过了*/parsePointcut(pointcutElement, parserContext);}//出栈并注册,并不是注册到注册表中……,可能什么也不做parserContext.popAndRegisterContainingComponent();} finally {//AspectEntry点位,出栈this.parseState.pop();}
}

2.4.1 parseDeclareParents解析< aop:declare-parents/>引介增强标签

  在不修改源代码的前提下,< aop:declare-parents/>引介增强标签可以在运行期为类动态地添加一些额外的方法或属性,又被称为Introduction(引介)。
  parseDeclareParents方法用于解析< aop:declare-parents/>子标签元素,解析的bean定义同样会注册到注册表中。

  1. 新建RootBeanDefinition类型的bean定义,beanClass类型为DeclareParentsAdvisor,解析各种属性并赋值,其中default-impl和delegate-ref属性有且只能由其中一个。
  2. < aop:declare-parents/>标签没有id或者name属性,直接采用DefaultBeanNameGenerator作为beanName生成器来生成beanName,生成的beanName类似于 “org.springframework.aop.aspectj.DeclareParentsAdvisor#0”。
//-------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的方法* <p>* 解析<aop:declare-parents/>引介增强标签元素,创建beanClass类型为DeclareParentsAdvisor的bean定义并注到容器缓存中** @param declareParentsElement <aop:declare-parents/>标签元素* @param parserContext         解析上下文*/
private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {//新建RootBeanDefinition类型的bean定义,beanClass类型为DeclareParentsAdvisorBeanDefinitionBuilder 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-refif (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {//那么将该属性的值加入具有索引的bean定义构造器集合的第三位builder.addConstructorArgValue(defaultImpl);}//如果设置了delegate-ref并且没有设置default-implelse 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));//<aop:declare-parents/>标签没有id或者name属性,通过DefaultBeanNameGenerator生成beanName//随后同样注册到registry的缓存中,返回生成的beanNameparserContext.getReaderContext().registerWithGeneratedName(definition);//返回bean定义return definition;
}

2.4.2 isAdviceNode判断通知标签

  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. <p>3. 判断是不是通知标签节点4. 如果是任何一个通知标签节点元素,那么就返回true,否则返回false*/
private boolean isAdviceNode(Node aNode, ParserContext parserContext) {//如果不是标签节点,直接返回nullif (!(aNode instanceof Element)) {return false;} else {//获取标签节点的本地名称也就是去除"aop:"之后的名称String name = parserContext.getDelegate().getLocalName(aNode);//如果是任何一个通知标签节点元素,那么就返回true,否则返回falsereturn (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));}
}

2.4.3 parseAdvice解析通知标签

  parseAdvice方法用于解析所有advice通知标签,包括< aop:before/>、< aop:after/>、< aop:after-returning/>、< aop:after-throwing/>、< aop:around/>,解析的最终bean定义同样会注入到容器中,该方法比较复杂,bean定义的层级很深。

  1. 首先创建RootBeanDefinition类型的通知方法bean定义,beanClass类型为MethodLocatingFactoryBean,它实现了FactoryBean接口,是一个方法工厂,专门用于获取通知对应的Method对象。在第三步会被用于构造通知bean定义。

    1. 设置bean定义的targetBeanName属性,值就是外部< aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的id。
    2. 设置bean定义的methodName属性,值就是method属性值。
    3. 设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的bean。
    4. 这个bean定义不会被注册到容器中,也就是内部bean。
  2. 创建RootBeanDefinition类型的切面实例类bean定义,beanClass类型为SimpleBeanFactoryAwareAspectInstanceFactory,它实现了AspectInstanceFactory接口,是一个实例工厂,专门用于获取切面实例对象,也就是通知类对象。在第三步会被用于构造通知bean定义。
    1. 设置bean定义的aspectBeanName属性,值就是外部< aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的id。
    2. 设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的bean。
    3. 这个bean定义不会被注册到容器中,也就是内部bean。
  3. 调用createAdviceDefinition方法,根据前两个bean定义构建advice通知bean定义 - adviceDef,并解析通知标签的各种属性。通知bean定义也不会被注册到容器中,也就是内部bean。
  4. 创建RootBeanDefinition类型的切入点通知器bean定义 - advisorDefinition,beanClass类型为AspectJPointcutAdvisor,这个bean定义才是该通知标签最终的bean定义
    1. 设置bean定义的构造器参数,值就是上面创建的advice通知bean定义 - adviceDef。
    2. 如果外部< aop:aspect/>标签元素具有order属性,那么设置bean定义的order属性值,这用来控制切入点方法的执行优先级。
  5. 向容器注册最终的切入点通知器bean定义 - advisorDefinition,通过DefaultBeanNameGenerator生成beanName。最后返回advisorDefinition。
private static final String ORDER_PROPERTY = "order";/**1. ConfigBeanDefinitionParser的方法2. <p>3. 解析所有advice通知标签,包括<aop:before/>、<aop:after/>、<aop:after-returning/>、<aop:after-throwing/>、4. <aop:around/>,创建beanClass类型为MethodLocatingFactoryBean的bean定义并注到容器缓存中5.  6. @param aspectName      外部<aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的id7. @param order           顺序,实际上就是在当前外部<aop:aspect/>标签中的所有节点的定义顺序由上而下的索引值8. @param aspectElement   外部<aop:aspect/>标签元素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属性,值就是外部<aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的idmethodDefinition.getPropertyValues().add("targetBeanName", aspectName);//设置bean定义的methodName属性,值就是method属性值methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));//设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的beanmethodDefinition.setSynthetic(true);/** 2 创建切面实例类bean定义,用于获取切面实例对象,也就是通知类对象,在第三步会被用于构造通知bean定义*///新建RootBeanDefinition类型的bean定义,beanClass类型为SimpleBeanFactoryAwareAspectInstanceFactory//实现了AspectInstanceFactory接口,是一个实例工厂,专门用于获取切面实例对象,也就是通知类对象RootBeanDefinition aspectFactoryDef =new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);//设置bean定义的aspectBeanName属性,值就是外部<aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的idaspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);//设置bean定义的synthetic,值为true,这表示它是一个合成的而不是不是由程序本身定义的beanaspectFactoryDef.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类型为AspectJPointcutAdvisorRootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);advisorDefinition.setSource(parserContext.extractSource(adviceElement));//设置bean定义的构造器参数,值就是上面创建的advice通知bean定义adviceDefadvisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);//如果外部<aop:aspect/>标签元素具有order属性if (aspectElement.hasAttribute(ORDER_PROPERTY)) {//设置bean定义的order属性,值就是外部<aop:aspect/>标签元素的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();}
}

2.4.3.1 createAdviceDefinition创建通知bean定义

  createAdviceDefinition方法解析当前通知标签的各种属性,并且将前两个bean定义作为构造器参数构建通知bean定义。

  1. 新建RootBeanDefinition类型的bean定义 - adviceDefinition,beanClass类型为该通知标签对应的实现类类型。
  2. 设置bean定义的aspectName属性,值就是外部< aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的id。
  3. 设置bean定义的declarationOrder属性,也就是“声明顺序”,值就是当前的通知的标签在外部< aop:aspect/>标签中的所有节点的定义顺序由上而下的索引值,后面用于拦截器排序。
  4. 如果具有returning属性,说明是后置通知。设置bean定义的returningName属性,值就是returning属性的值。
  5. 如果具有throwing属性,说明是异常通知。设置bean定义的throwingName属性,值就是throwing属性的值。
  6. 如果具有arg-names属性,这表示接收目标方法的参数。设置bean定义的argumentNames属性,值就是arg-names属性的值。
  7. 为adviceDefinition设置构造器第一个参数属性,值为methodDef,也就是此前构建的通知方法bean定义。
  8. 调用parsePointcutProperty方法,解析当前通知标签的pointcut或者pointcut-ref属性,获取切入点,返回值可能是一个切入点bean定义或者一个切入点bean定义的id,这个方法我们在前面讲过了。这个的切入点bean定义作为内部bean,不会被注册到容器中。
  9. 为adviceDefinition设置构造器第二个参数属性,值就是上一步解析返回的切入点bean定义,或者一个切入点bean定义的id封装的一个RuntimeBeanReference。
  10. 为adviceDefinition设置构造器第三个参数属性,值就是切面通知类bean定义,也就是此前构建的切面通知类bean定义.
  11. 返回构建好的通知bean定义adviceDefinition。

  为什么要设置三个构造器参数?因为所有的通知标签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. <p>3. 创建一个advice通知bean定义,beanClass类型为该通知标签对应的实现类类型,还会解析内部的切入点,4.  5. @param adviceElement    advice通知标签元素6. @param parserContext    解析上下文7. @param aspectName       外部<aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的id8. @param order            顺序,实际上就是在当前外部<aop:aspect/>标签中的所有节点的定义顺序由上而下的索引值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属性,值就是外部<aop:aspect/>标签的ref属性值,也就是引用的通知类bean定义的idadviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);//设置bean定义的declarationOrder属性,值就是在当前外部<aop:aspect/>标签中的所有节点的定义顺序由上而下的索引值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定义的idelse 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;
}
2.4.3.1.1 getAdviceClass获取通知的beanClass

  getAdviceClass方法用于获取给定通知标签对应的通知类的beanClass的类型。

  1. 如果是< aop:before/>通知,那么返回AspectJMethodBeforeAdvice.class;
  2. 如果是< aop:after/>通知,那么返回AspectJAfterAdvice.class;
  3. 如果是< aop:after-returning/>通知,那么返回AspectJAfterReturningAdvice.class;
  4. 如果是< aop:after-throwing/>通知,那么返回AspectJAfterThrowingAdvice.class;
  5. 如果是< aop:around/>通知,那么返回AspectJAroundAdvice.class。
//-------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的方法* <p>* 获取给定通知元素对应的bean定义的beanClass的实现类类型*/
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {//获取该通知标签的本地名称String elementName = parserContext.getDelegate().getLocalName(adviceElement);//根据不同的通知类型返回不同的Classif (BEFORE.equals(elementName)) {//如果是<aop:before/>通知,那么返回AspectJMethodBeforeAdvice.classreturn AspectJMethodBeforeAdvice.class;} else if (AFTER.equals(elementName)) {//如果是<aop:after/>通知,那么返回AspectJAfterAdvice.classreturn AspectJAfterAdvice.class;} else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {//如果是<aop:after-returning/>通知,那么返回AspectJAfterReturningAdvice.classreturn AspectJAfterReturningAdvice.class;} else if (AFTER_THROWING_ELEMENT.equals(elementName)) {//如果是<aop:after-throwing/>通知,那么返回AspectJAfterThrowingAdvice.classreturn AspectJAfterThrowingAdvice.class;} else if (AROUND.equals(elementName)) {//如果是<aop:around/>通知,那么返回AspectJAroundAdvice.classreturn AspectJAroundAdvice.class;} else {//其他情况,抛出异常throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");}
}

3 < aop:config/> 案例

  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,内部仅仅定义了通知方法-->
<bean class="com.spring.aop.aopconfig.AspectMethod" name="aspectMethod"/><!--AOP配置-->
<aop:config expose-proxy="true"><!--切入点--><aop:pointcut id="pointcut" expression="execution(* *(..))"/><!--切面,ref引用切面通知类aspectMethod,方便调用其方法--><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个:

4 < aop:config/> 标签解析总结

  上面我们见识到了< aop:config/>标签的全部解析流程,我们简单的总结一下它的主要流程:

  1. 首先通过configureAutoProxyCreator方法尝试创建或者升级一个自动代理创建者的bean定义,这个bean定义专门用于后续创建AOP代理对象。

    1. 如果容器中不存在beanName为“org.springframework.aop.config.internalAutoProxyCreator”的bean定义,那么新建一个RootBeanDefinition,beanClass为AspectJAwareAdvisorAutoProxyCreator.class,并以“org.springframework.aop.config.internalAutoProxyCreator”为beanName注册到容器中。
    2. 如果容器中存在beanName为“org.springframework.aop.config.internalAutoProxyCreator”的bean定义,那么升级beanClass属性,即beanClass属性替换为AspectJAwareAdvisorAutoProxyCreator.class类型,到创建bean实例的时候,就会创建对应的beanClass类型的实例。
    3. AspectJAwareAdvisorAutoProxyCreator实现了比如BeanClassLoaderAware、BeanFactoryAware、SmartInstantiationAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanPostProcessor …… 等一系列的自动回调接口,它们在创建代理对象的过程中非常有用。
    4. 上面的“替换”是通过比较自动代理创建者类型的优先级来确定的,优先级更高的class将替换优先级更低的class,不同的标签或者注解使用不同的创建者:
      1. < aop:config/>标签使用AspectJAwareAdvisorAutoProxyCreator自动代理创建者;
      2. 比如< aop:aspectj-autoproxy/>以及@EnableAspectJAutoProxy使用AnnotationAwareAspectJAutoProxyCreator自动代理创建者;
      3. < tx:annotation-driven/>以及@EnableTransactionManagement使用InfrastructureAdvisorAutoProxyCreator自动代理创建者;
      4. 容器最终只会创建和保留一个自动代理创建者的bean定义,并采用优先级最高的自动代理创建者的类型,优先级从低到高为:InfrastructureAdvisorAutoProxyCreator < AspectJAwareAdvisorAutoProxyCreator < AnnotationAwareAspectJAutoProxyCreator。所以说,如果存在< aop:aspectj-autoproxy/>标签或者@EnableAspectJAutoProxy注解,那么自动代理创建者的类型一定是AnnotationAwareAspectJAutoProxyCreator。
  2. 通过parsePointcut方法解析< aop:pointcut/>切入点标签:
    1. 内部主要是通过createPointcutDefinition方法创建一个RootBeanDefinition,beanClass类型为AspectJExpressionPointcut。随后以id作为beanName,最后注册到容器中。
    2. 在< aop:aspect/>标签内部也可以定义切入点标签,它们都会被解析成AspectJExpressionPointcut类型的切入点bean定义,以id作为beanName,最后同样会注册到容器中。所以说,这个< aop:aspect/>标签无论定义在哪里都是通用的。
    3. < aop:advisor/>和全部通知标签中都可以通过pointcut属性自定义切入点表达式,它们同样都会被解析成AspectJExpressionPointcut类型的切入点bean定义,会自动生成beanName,并且这些切入点bean定义作为内部bean,不会被注册到容器中,其他地方无法引用。
  3. 通过parseAdvisor方法解析< aop:advisor/>通知器标签:
    1. 内部主要通过createAdvisorBeanDefinition方法用于创建一个RootBeanDefinition,beanClass类型为DefaultBeanFactoryPointcutAdvisor。以id作为beanName或者自动生成beanName,最后注册到容器中。
  4. 通过parseAspect方法解析< aop:aspect/>切面标签
    1. 通过parseDeclareParents方法解析< aop:declare-parents/>引介增强子标签。创建RootBeanDefinition类型的bean定义,beanClass类型为DeclareParentsAdvisor,自动生成beanName,最后注册到容器中。
    2. 通过parseAdvice方法解析所有类型的advice通知增强子标签:
      1. 在创建过程中,首先会创建beanClass类型为MethodLocatingFactoryBean的通知方法bean定义和SimpleBeanFactoryAwareAspectInstanceFactory的切面实例bean定义,它们作为构造器参数用于构建通知bean定义。这两个bean定义作为内部bean,不会注册到容器中。
      2. 随后解析标签属性,并将上面两个bean定义作为构造器参数创建通知bean定义,beanClass类型为该通知标签对应的实现类类型,这个bean定义同样作为内部bean,不会注册到容器中:
        1. 如果是< aop:before/>通知,那么通知类型为AspectJMethodBeforeAdvice.class;
        2. 如果是< aop:after/>通知,那么通知类型为AspectJAfterAdvice.class;
        3. 如果是< aop:after-returning/>通知,那么通知类型为AspectJAfterReturningAdvice.class;
        4. 如果是< aop:after-throwing/>通知,那么通知类型为AspectJAfterThrowingAdvice.class;
        5. 如果是< aop:around/>通知,那么通知类型为AspectJAroundAdvice.class。
      3. 将创建的通知bean定义又作为构造器参数创建一个切入点通知器bean定义来,用于这个通知bean定义,它的beanClass类型为AspectJPointcutAdvisor,自动生成beanName,最后该切入点通知器bean定义注册到容器中,只会注册这一个bean定义。
    3. 通过parsePointcut方法解析< aop:pointcut/>切入点子标签,解析的beean定义会注册到容器中。

  如果我们采用XML配置Spring AOP,那么只需要配置< aop:config/>标签就行了,当前全部标签解析完毕,仅仅是向容器中注册了一些bean定义,我们在后面学习基于注解的AOP配置时,我们会知道,基于注解的AOP配置不会注册这么多bean定义,比如通知器相关的对象,比如切入点对象,通常是new对象。
  并且,此时还没有进行真正的创建代理对象操作,创建代理对象的操作实际上是在自动代理创建者AspectJAwareAdvisorAutoProxyCreator内部完成的!下一篇文章,我们将会分析AspectJAwareAdvisorAutoProxyCreator创建代理对象的源码!

相关文章:
  https://spring.io/
  Spring Framework 5.x 学习
  Spring Framework 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

Spring AOP源码(1)—<aop:config/>AOP配置标签解析【一万字】相关推荐

  1. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  2. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  3. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  4. Spring AOP 源码系列(一)解析 AOP 配置信息

    在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读. 关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐 ...

  5. Spring AOP源码解析——AOP动态代理原理和实现方式

    2019独角兽企业重金招聘Python工程师标准>>> Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和 ...

  6. spring AOP源码分析(一)

    spring AOP源码分析(一) 对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目 ...

  7. Spring中AOP源码剖析

    Spring中AOP源码剖析 关键词 aop的增强发生在后置处理器中(没有循环依赖) 最终增强是通过 递归调用 ,层层增强 一.环境准备 1.1 bean和接口 public class AopBea ...

  8. 一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  9. Spring AOP源码解析-拦截器链的执行过程

    一.简介 在前面的两篇文章中,分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在得到了 bean 的代理对象,且通知也以合适的方式插在了目标方 ...

最新文章

  1. 王者又连跪了?快让 AI 帮你上分!
  2. Ubuntu12安装RobotFramework
  3. jQuery图片提示和文字提示
  4. TOTP 介绍及基于 C# 的简单实现
  5. 华为trt一al00计算机在哪,华为trt一al00a属于什么系列
  6. g2o中 EdgeSE3Expmap类型Jacobian的计算
  7. AsyncTask 异步任务基本使用-下载视频
  8. windows下升级nodenpm
  9. mysql union 放弃索引_MySQL索引优化和in or替换为union all
  10. java 调用oracle函数_java – 用Spring的SimpleJdbcCall来调用Oracle函数
  11. linux mate桌面主题下载_5个适用于Linux的最佳图标主题
  12. 从CVPR2019看计算机视觉的最新趋势
  13. zz 教你如何用proxyhunter找大学代理
  14. 全志A33_Vstar
  15. Java Swing实现高仿电脑版微信
  16. 2022年嵌入式系统设计师考试大纲
  17. 用Excel制作不一样的分割图表
  18. Ajax请求前显示的等待图标
  19. 在线apt-get安装mysql_apt-get安装mysql
  20. (2020年最新版)App Store 审核指南

热门文章

  1. ubuntu 修改系统默认语言为英文!
  2. Spring Security CSRF防御源码分析
  3. Python 解决报错NameError: name ‘LEFT‘ is not defined
  4. 蓝蓝算法09-进制转换
  5. stm32 mbed 入门教程(一)---前期准备
  6. 路由器什么牌子好?消灭卡顿畅快吃鸡
  7. 阿里云是干什么用的?
  8. 史上最简单的图片二维码识别
  9. 徐辉 北大计算机,徐辉的痛苦回忆_徐辉经历的那一段痛苦回忆
  10. 3-综合案例:月福首页-多媒体-图片热点-框架