spring mvc 父子容器AOP-建言者(切面,事务)的继承

7 篇文章 1 订阅
5 篇文章 0 订阅

spring 是含有父子容器的机制的,特别是在使用springmvc的情况下,父子容器是非常容易出现的,也是大家常用的。那么父子容器之间的关系到底是怎么样的呢。
之前听说和看别的文章说,springmvc中父容器事务不起效,不要使用父子容器都加载配置信息等,这些会在本文中揭开疑惑。
所以提出了几个疑问,父子容器之间的关系是怎样生成的,父子容器之间的关系是什么样的,父子容器方面有什么特殊实现呢(特别说明建言者方面的,也是本文的重点) 开头先时候总结方便解决问题,文章后半部分会结合源码解读原理和机制。
注:统一一下名词 对于spring来说 切面和事务都是以建言者(Advisor)的方式来实现的
1、父子容器之间的建言者是什么关系呢
子容器想使用父容器的建言需要几个条件
(1)子容器必须含有AbstractAutoProxyCreator的子类实例(它是BeanPostProcessor的实现类)(如事务配置,aspectj切面配置等代理配置)
(2)子容器中的AbstractAutoProxyCreator的子类实例的配置必须不是(InfrastructureAdvisorAutoProxyCreator的实例)也就是spring默认名称为“org.springframework.aop.config.internalAutoProxyCreator“的bean,也就是说如含有的配置文件必须晚于配置文件的加载。或者说子容器只有的配置文件是不行的,必须也要有其他的AbstractAutoProxyCreator的子类实例的配置如并且要早于的配置文件加载。
(3)子容器中建言者的名称不能和父容器的建言者名称相同。
举个例子
说明 – 没有说明import就没有import其他配置文件。
一、
【1】父容器中配置顺序为:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一个切面)
【2】子容器中配置顺序为: servlet.xml,aop-config.xml
那么运行时子容器就会继承父容器的一个切面加上自已有一个(此时子容器中切面会执行两次)。
二、
【1】父容器中配置顺序为:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一个切面)
【2】子容器中配置顺序为: servlet.xml, tx-config.xml
此时子容器中不会继承父容器的切面。
三、
【1】父容器中配置顺序为:applicaitonContext.xml (import aop-config.xml,tx-config.xml),aop-config.xml(含有一个切面)
【2】子容器中配置顺序为: servlet.xml, tx-config.xml,aop-config.xml
此时子容器中不会继承父容器的切面。
四、
【1】父容器中配置顺序为:applicaitonContext.xml (import aop-config.xml,tx-config.xml)
【2】子容器中配置顺序为: servlet.xml, aop-config.xml
此时子容器中会继承父容器的切面。
例子就举到这里,因为场景是无限的,只有原理和机制是一样的,只要了解了原理和机制,我们就不怕各种场景

2、父子容器的关系
前面说了建言者的继承关系,这里说下bean的方面的关系。
spring 当当前容器不存在当前beanName的配置描述信息时,会看父容器是否存在,如果当前容器存在父容器并且父容器存在当前beanName的描述信息,此时会通过父容器的getBean方法获取父容器中当前bean的实例返回。
也就是说一样的bean不需要配置两次,在父子容器都配置,这样只会浪费内存和时间。

3、父子容器是怎么样实现的
这里只针对spring mvc的场景,spring mvc会先加载web.xml中配置的listener 加载的容器这个是父容器,servlet中加载的配置形成的容器这个容器是子容器,生成子容器的时候会把,父容器的引用通过servletconfig中拿出来放入子容器中。

===============源码解析分割线=================
看完解决有人会问为什么,它是怎么实现的,没有源码我不信,下面咱们一起看看源码,看看它是怎么实现的,原理是什么
1、父子容器之间的建言者是什么关系
说到建言者就离不开代理,spring对含有建言者的bean就一定会生成代理对象。
代理的创建机制在这篇文章中有详解:http://blog.csdn.net/songyang19871115/article/details/54345224
代理的创建者AbstractAutoProxyCreator子类的实现取决于spring配置的加载和原理:http://blog.csdn.net/songyang19871115/article/details/54346444

spring的代理的创建取决于是否含有目标源(targetSource)和建言者。
这里写图片描述
(图1.1)
这里写图片描述
(图1.2)
从上图1.1,1.2 源码看出 spring在生成代理的时候一定会获取建言者信息。
findCandidateAdvisors 调用findAdvisorBeans方法下面会详解。
findAdvisorsThatCanApply 过滤掉当前bean不匹配的建言者信息extendAdvisors 处理如果包含aspectj 建言者,并且没有默认建言者则添加一个默认建言者到建言者链的头部。
而且会针对所有建言者进行排序。

这里写图片描述
(图1.3)
spring获取建言者名称,并且匹配出适合的建言者。
这里写图片描述
(图1.4)
如图可以看出,spring获取建言者的时候会把当前容器的和父容器的建言者获取出来,然后匹配当前容器是否含有当前建言者(按名称),如果不包含则放在集合里去。也就是会继承父容器的建言者只要名称不一致。

这里写图片描述
(图1.5)
这里写图片描述
(图1.6)
从图1.6中看出InfrastructureAdvisorAutoProxyCreator 只有这个建言者存在于当前beanFactory中才会是适和当前bean的建言者。

这里写图片描述
(图1.7)
图1.7为DefaultAdvisorAutoProxyCreator中判断建言者是否适合当前bean,它是通过前缀来匹配的。(但是spring没有使用这个代理创建者,如果二次开发或拓展spring的时候请注意这部分)

而AbstractAdvisorAutoProxyCreator的其他子类:AnnotationAwareAspectJAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator对判断是否建言者是否适合当前bean都是使用默认提供的方法。如下图1.8
这里写图片描述
至此说明这两种creator都是可以继承父容器和子容器不同名建言者的。
而使用哪种代理creator取决于配置的加载原理。详见http://blog.csdn.net/songyang19871115/article/details/54346444

1、1前面说到了名称不能重名,但是建言者的bean名称是怎么生成的呢
这个在前面我的一篇配置加载原理的文章中引出了http://blog.csdn.net/songyang19871115/article/details/54346444

spring提供了一种bean名称自动生成的实现 如下图1.1.1
这里写图片描述
(图1.1.1)
xmlReaderContext是ReaderContext的唯一实现
bean名称的生成spring提供了两种一种是AnnotationBeanNameGenerator注解bean的生成规则,一种是DefaultBeanNameGenerator,
在没有指定名称的时候他们会使用对应的规则生成名称。
这里写图片描述
(图1.1.2)
这里写图片描述
(图1.1.3)
这里写图片描述
(图1.1.4)
从图1.1.2,1.1.3,1.1.4中可以看出注解的名称生成规则是通过类名生成的,并且驼峰式命名,如果前两个字母大写了就全是大写。

除了注解以外名称的命名都是使用DefaultBeanNameGenerator
这里写图片描述
(图1.1.5)
这里写图片描述
(图1.1.6)
图1.1.5,图1.1.6源码中显示默认的名称实现为类名 + 后缀 + “#” 内部类使用beanDefinition hash code的hex 字符串。
如果没有内部类则使用第几个编号。从0开始。而且从源码中看出判断是否存在当前bean描述信息时,只使用了当前容器,那么使用的一样的规则(同样配置方法)就会产生重名。进而影响继承父容器的建言者。
总结:鉴于上面提到的建言者重名导致的不能被子容器继承的关系。举个例子说明:
如果父容器实例了两个aop建言(配置文件方式),那么按照名称生成规则两个的名称为org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,和org.springframework.aop.aspectj.AspectJPointcutAdvisor#1,
如果子容器也实例了肯定也是使用了这套规则,那么如果子容器配置了一个就会叫做org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,如果只考虑名称继承规则,那么子容器就会继承org.springframework.aop.aspectj.AspectJPointcutAdvisor#1这个建言者。

2、spring子容器使用父容器bean
这里写图片描述
(图2.1)
如上源码:spring在发现当前容器中没有当前bean的时候,不会去先创建而是去父容器获得,此处使用的是递归算法,也就是当前容器不存在只要任何祖先容器存在都会获取到。离他越近的祖先容器中有就会使用到。

注:解决任何一种问题都是因为良好的基础和原理知识,这样才能举一反三,解决同类问题,此文章中很多的解决都依赖了很多spring的原理基础。
如:
spring容器及bean加载机制:
http://blog.csdn.net/songyang19871115/article/details/54342242
spring代理的生成和运行机制:
http://blog.csdn.net/songyang19871115/article/details/54345224
spring配置加载的原理:
http://blog.csdn.net/songyang19871115/article/details/54346444

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值