Spring中 bean定义的parent属性机制的实现分析

在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 里的设置覆盖掉

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

时间: 2024-08-26 01:55:54

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

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

1.singleton 当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例.换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例.这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例,这里要注意的是singl

Spring中Bean的命名问题及ref和idref之间的区别

一直在用Spring,其实对其了解甚少,刚去了解了一下Spring中Bean的命名问题以及ref和idref之间的区别,略作记录,以备后查. Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: <bean class="com.zyh.spring3.hello.StaticBeanFactory"></bean> 此

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

Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: <bean class="com.zyh.spring3.hello.StaticBeanFactory"></bean> 此时需要通过接口getBean(Class<T> requiredType)来获取Bean: 如果该Bean找不到则抛异常:NoSu

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

Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Singleton模式产生单一实例,对单线程的程序说并不会有什么问题,但对于多线程的程序,就必须注意安全(Thread-safe)的议题,防止多个线程同时存取共享资源所引发的数据不同步问题. 然而在spring中 可以设定每次从BeanFactory或Appl

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

Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: <bean class="com.zyh.spring3.hello.StaticBeanFactory"></bean> 此时需要通过接口getBean(Class<T> requiredType)来获取Bean: 如果该Bean找不到则抛异常:NoSu

Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析

Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析 本文简要介绍了基于 Spring 的 web project 的启动流程,详细分析了 Spring 框架将开发人员基于 XML 定义的 Bean 信息转换为 Spring 框架的 Bean Definition 对象的处理过程,向读者展示了 Spring 框架的奥妙之处,可以加深开发人员对 Spring 框架的理解. 0 评论: 秦 天杰, 软件工程师, IBM China 2013 年 9 月 02 日 内容 在 IBM

spring中Bean的注入参数详解

字面值    一般指可用字符串表示的值,这些值可以通过<value>元素标签进行注入.在默认情况下,基本数据类型及其封装类.String等类型都可以采取字面值注入的方式,Spring容器在内部为字面值提供了编辑器,它可以将以字符串表示的字面值转换为内部变量的相应类型.    配置信息:    <bean id="car" class="com.luxl.domain.Car">        <property name="m

Spring 中bean的scop 阅读随笔(记)

bean的scope scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象. Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另外三种scope类型,即request.session和global session类型.不过这三种类型有所限制,只能在Web应用中使用.也就是说,只有在

spring中Bean的注入类型

1.属性注入    即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式.    属性注入要求Bean提供一个默认的构造参数,并为需要注入的属性提供对应的Setter方法.Spring先调用Bean的默认构造参数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值.    需要指出的是:Spring只会检查Bean中是否有对应的Setter方法,至于Bean中是否有对应的属性变量则不做要