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 里的設置覆蓋掉

 

原創博客,轉載請注明出處

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM