在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>
這個問題還是得從兩個過程分析,
- 一個是解析xml創建出BeanDefinition對象,
- 一個是從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 里的設置覆蓋掉
原創博客,轉載請注明出處