在XML中配置bean元素的时候,我们常常要用到parent属性,这个用起来很方便就可以让一个bean获得parent的所有属性
在spring中,这种机制是如何实现的?
 
 
对于这种情况 transactionProxy01的parent属性是transactionProxy1
此时我们要获取transactionProxy01的实例 spring应该如何处理呢?
    <bean id="transactionProxy01" parent="transactionProxy1">
<property name="target" ref="service01"/>
</bean> <bean id="transactionProxy1" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,timeout_6000,-Exception</prop>
<prop key="error*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_20,readOnly,-Exception</prop> <!-- -Exception表示有Exception抛出时,事务回滚. -代表回滚 +就代表提交 -->
</props>
</property>
</bean>
这个问题还是得从两个过程分析,
  1. 一个是解析xml创建出BeanDefinition对象,
  2. 一个是从beanFactory中 依据BeanDefinition创建出实例
 

1.DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法
此时 xml文档的root元素一句解析出来 ,用于真正解析每个元素的delegate类实例也已经创建好
这两个作为参数传入这个方法
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate); //对xml中的bean元素挨个做解析
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//利用delegate的方法 解析出bean元素 beanDefinition对象存放在一个Holder里面
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
try {
//这个是注册 就是把BeanDefinition保存到BeanFactory的BeanDefinitionMap里面去
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
}
}
 
接下去进入Delegate  BeanDefinitionParserDelegate
   public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE); //获取出bean的id
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//...省略了一些代码
String beanName = id;
//...省略了一些代码 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);//解析xml中的bean元素 创建beanDefinition对象
if (beanDefinition != null) {
//...省略了一些代码
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
    public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));//SAX解析特有的 都要定义一个stack做辅助
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE); //取出了parent属性的值
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);//创建一个BeanDefinition对象 注意把parent属性传进去了 //后面就是解析bean元素的其他属性 然后set到这个BeanDefinition对象里面去
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//...省略了一些代码
return bd;
}
//...省略了一些代码
finally {
this.parseState.pop();
}
return null;
}

进入BeanDefinitionReaderUtils工具类

    public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();//创建一个beanDefinition对象
bd.setParentName(parentName);//把parent属性set进去
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd; //然后返回beanDefinition对象
}

至此 BeanDefinition是创建完了 可以看到,我们定义parent属性,在创建过程中并没有什么特殊的处理,只是把parent作为一个属性,设置到BeanDefinition对象里面去了


 

那么真正的处理逻辑肯定就是在我们getBean的时候了
下面来分析getBean逻辑
问题的关键就在AbstractBeanFactory类里面doGetBean方法
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); //这里是根据beanName获取到BeanDefinition,parent的处理就是在这里发生的
    protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); //一开始获取合并的BeanDefinition 肯定是null 之前没有缓存的
if (mbd != null) {
return mbd;
}
//然后先取出原始的transactionProxy01的beanDefinition 注意 这里取出的beanDefinition中 beanName是transactionProxy01,parent是transactionProxy1,
//但是beanClass是null
//然后执行getMergedBeanDefinition(beanName, getBeanDefinition(beanName)) 准备把parent的beanClass属性拿出来放到子beanBefinition里面去
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
    protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, BeanDefinition containingBd)
throws BeanDefinitionStoreException {
synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;
// Check with full lock now in order to enforce the same merged instance.
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
if (mbd == null) {
if (bd.getParentName() == null) {
//....省略一些代码
}
else {
// Child bean definition: needs to be merged with parent.
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());//取到parent的name
if (!beanName.equals(parentBeanName)) {
//这个方法获取parent对于的beanDefinition,这个方法里面其实又是调用上一个方法的
//也就是还是进一步调用getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); 是递归的! 用于解决parent还有parent 的情况
//所以这个地方要特别注意的 多层级的parent处理就是这里的递归解决的
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
//....省略一些代码
}
}
//....省略一些代码
// Deep copy with overridden values.
//核心的来了 先用parent 的BeanDefinition为参数,创建了一个新的BeanDefinition 想想都知道就是new了一个新的RootBeanDefinition对象
//然后把parent的BeanDefinition的属性一个一个都set到新的RootBeanDefinition对象里面,相当于深拷贝了
mbd = new RootBeanDefinition(pbd);
mbd.overrideFrom(bd);//然后用孩子beanDefinition已有的属性 去覆盖掉parent里继承下来的属性值
}
//....省略一些代码 }
return mbd;
}
}

至此,spring处理的parent的方式已经搞清楚了

    <bean id="transactionProxy01" parent="transactionProxy1">
<property name="target" ref="service01"/>
</bean>
<bean id="transactionProxy1" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,timeout_6000,-Exception</prop>
<prop key="error*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_20,readOnly,-Exception</prop>
</props>
</property>
</bean>
    1.spring在最初解析XML的时候,并没有做特殊处理,只是把transactionProxy01这个beanDefinition对象里的parentName属性给做了赋值,然后照常的存储到了BeanFactory里
 
    2.在我们执行getBean("transactionProxy01")的时候,spring先出去transactionProxy01对于的BeanDefinition对象,检测出了其中有一个parentName属性不为null
 
    3.于是根据parentName,取出了parent对应的BeanDefinition对象,然后创建出一个新的RootBeanDefinition对象,把parent对于的BeanDefinition对象的属性值都拷贝进入,
作为parent对于的BeanDefinition的一个深拷贝(称为mbd)。
 
    4.然后用孩子bean,也就是transactionProxy01对应的BeanDefinition对象里的有的属性值去覆盖这个mbd里的属性值,也就是例子中<property name="target" ref="service01"/>这个属性
 
  如此一来,
  •   我们得到的mbd 就是这样一个BeanDefinition
  •   它的beanName是transactionProxy01
  •   它的target是service01
  •   它的beanClass,以及其他的属性,都和parent的BeanDefinition是相同的
 
 
 
最后把这个新的BeanDefinition返回
接下去就是利用这个心的BeanDefinition来创建Bean实例对象的过程了。
 
  
其中孩子bean和parent合并出来的这个BeanDefinition也会做缓存的,存储在DefaultListableBeanFactory里的mergedBeanDefinitions这个线程安全Map里面
下次就不用合并了,直接可以取出来用
    private final Map<String, RootBeanDefinition> mergedBeanDefinitions =
new ConcurrentHashMap<String, RootBeanDefinition>(64);

这种下一层级覆盖上一层级的做法很普遍,比如struts就是先加载底层的struts-default.xml 然后再加载我们用户自己的struts.xml
这个时候我们用户自己的属性设置,就会把初始的struts-default.xml 里的设置覆盖掉

原创博客,转载请注明出处

 
 
 
 
 
 
 
 
 
 
 
 
 
 

Spring中 bean定义的parent属性机制的实现分析的更多相关文章

  1. spring中bean的配置详解--定义parent

    在工作中碰到了好多的配置文件,具体来说是spring 中bean配置的parent的配置,搞的我一头雾水,仔细看一下spring中有关bean的配置,剖析一下,具体什么含义! 一.Spring IoC ...

  2. spring中bean的作用域属性singleton与prototype的区别

    1.singleton 当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会 ...

  3. Spring中bean标签的属性和值:

    Spring中bean标签的属性和值: <bean name="user" class="com.pojo.User" init-method=" ...

  4. Spring 中bean的作用、定义

    Spring 中bean的作用.定义: 创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的"配方(recipe)".把bean定义看成一个配方很有意义,它与cl ...

  5. 一次性讲清楚spring中bean的生命周期之三:bean是如何实例化的

    在前面的两篇博文<一次性讲清楚spring中bean的生命周期之一:getSingleton方法>和<一次性讲清楚spring中bean的生命周期之二:FactoryBean的前世今 ...

  6. Spring中Bean的命名问题(id和name区别)及ref和idref之间的区别

    Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...

  7. Spring中bean的配置

    先从IOC说起,这个概念其实是从我们平常new一个对象的对立面来说的,我们平常使用对象的时候,一般都是直接使用关键字类new一个对象,那这样有什么坏处呢?其实很显然的,使用new那么就表示当前模块已经 ...

  8. (转)Spring中Bean的命名问题(id和name区别)及ref和idref之间的区别

    Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...

  9. JAVA面试题:Spring中bean的生命周期

    Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一 ...

随机推荐

  1. 【HDOJ】1983 Kaitou Kid - The Phantom Thief (2)

    不仅仅是DFS,还需要考虑可以走到终点.同时,需要进行预处理.至多封闭点数为起点和终点的非墙壁点的最小值. #include <iostream> #include <cstdio& ...

  2. BZOJ2661: [BeiJing wc2012]连连看

    2661: [BeiJing wc2012]连连看 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 483  Solved: 200[Submit][S ...

  3. [pod install] error: cannot open .git/FETCH_HEAD: Permission denied

    pod installAnalyzing dependencies[!] Pod::Executable pull error: cannot open .git/FETCH_HEAD: Permis ...

  4. ArcServer,ArcSDE,ArcIMS,ArcEngine

    ArcServer,ArcSDE,ArcIMS,ArcEngine是ESRI的四种产品ArcGIS Server 与 ArcIMS功能相似,是将地图发布成服务供调用的ArcSDE 是空间数据引擎,是将 ...

  5. Count Color POJ--2777

    Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 32217   Accepted: 9681 Desc ...

  6. HDU 1280 前m大的数

    http://acm.hdu.edu.cn/showproblem.php?pid=1280 前m大的数 Time Limit: 2000/1000 MS (Java/Others) Memory L ...

  7. Interleaving String——Leetcode

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...

  8. JavaScript高级程序设计24.pdf

    Element类型 Element类型用于表现XML或HTML元素,提供对元素标签名.子节点及特性的访问,它具有以下特征 nodeType的值为1: nodeName的值为元素的标签名: nodeVa ...

  9. bayer图像格式

    Bayer数据,其一般格式为:奇数扫描行输出 RGRG……偶数扫描行输出 GBGB…… 根据人眼对彩色的响应带宽不高的大面积着色特点,每个像素没有必要同时输出3种颜色.因此,数据采样时, 奇数扫描行的 ...

  10. latin1字符集在navicat下显示乱码(mysql)

    用navicat查看一个表的内容时显示如下