在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. 【转】Valid signing identity not found解决办法(原有IDP私钥丢失)及Certificate、App ID、Devices、Provisioning Profiles之间区别--不错

    原文网址:http://blog.csdn.net/mad1989/article/details/8699147 前言: 刚刚把mini换成了macbookair,之前一直在mini上进行开发,到换 ...

  2. EasyUI的增删查改(后台ASP.NET)

    转自:http://www.cnblogs.com/dedeyi/archive/2013/04/22/3035057.html 某某人曾经跟我说,你们做系统不就是增删查改吗. 是啊,很多时候我们就是 ...

  3. (转载)MySQL数据类型中DECIMAL的作用和用法

    (转载)http://database.51cto.com/art/201005/201651.htm 在MySQL数据类型中,例如INT,FLOAT,DOUBLE,CHAR,DECIMAL等,它们都 ...

  4. Flash Vector例子

    var s1:Student = new Student(); var s2:Student = new Student(); var s3:Student = new Student(); s1.n ...

  5. 开源库CImg 数据格式存储

    CImg为开源图像处理库,仅有一个头文件CImg.h便包含了对图像的所有处理函数,函数操作简单,编程方便,但国内使用者较少 其homepage:http://cimg.sourceforge.net/ ...

  6. MFC中添加OpenGL

    WINDOWS下展示OpenGL有多种形式: MFC 或 win32,该如何向MFC中添加OpenGL?下面是介绍最简单OpenGL框架. 1.首先通过VS建立MFC应用程序-MyOpenGL,选择单 ...

  7. bzoj3224 Tyvj 1728 普通平衡树(名次树+处理相同)

    3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5354  Solved: 2196[Submit][Sta ...

  8. XPath在asp.net中查询XML -摘自网络

    .NET Framework 2.0中可以使用System.Xml.XPath命名空间下的类对XML文档进行基于路径的查询,在查询过程中需要构造类似SQL的查询字符串,该字符串遵循XPath语法.它由 ...

  9. nyoj 116 士兵杀敌(二)【线段树单点更新+求和】

    士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5   描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常 ...

  10. oc学习之路-----搞死指针之内存存储int类型

    关于每个数据类型个字节在内存中的存储地址(以int为例) 先上图 如题,为什么说好的*p = &c是1啊,为什么是513呢,一开始,我也觉得挺惊讶的,后面听老师分析了一下才知道怎么回事,但是还 ...