Spring Framework Reference Documentation 6.7. Bean definition inheritance
注:本文中bean和definition意思等同
該節詳細介紹了 bean的繼承關系(bean標簽的parent屬性),下面簡單翻譯一下:https://docs.spring.io/spring/docs/4.2.9.RELEASE/spring-framework-reference/htmlsingle/#beans-child-bean-definitions
6.7 Bean definition inheritance
A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values, or add others, as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.
If you work with an ApplicationContext
interface programmatically, child bean definitions are represented by the ChildBeanDefinition
class. Most users do not work with them on this level, instead configuring bean definitions declaratively in something like the ClassPathXmlApplicationContext
. When you use XML-based configuration metadata, you indicate a child bean definition by using the parent
attribute, specifying the parent bean as the value of this attribute.
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/> <!-- the age property value of 1 will be inherited from parent --> </bean>
A child bean definition uses the bean class from the parent definition if none is specified, but can also override it. In the latter case, the child bean class must be compatible with the parent, that is, it must accept the parent’s property values.
A child bean definition inherits scope, constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any scope, initialization method, destroy method, and/or static
factory method settings that you specify will override the corresponding parent settings.
The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, lazy init.
The preceding example explicitly marks the parent bean definition as abstract by using the abstract
attribute. If the parent definition does not specify a class, explicitly marking the parent bean definition as abstract
is required, as follows:
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property name="name" value="override"/> <!-- age will inherit the value of 1 from the parent bean definition--> </bean>
The parent bean cannot be instantiated on its own because it is incomplete, and it is also explicitly marked as abstract
. When a definition is abstract
like this, it is usable only as a pure template bean definition that serves as a parent definition for child definitions. Trying to use such an abstract
parent bean on its own, by referring to it as a ref property of another bean or doing an explicit getBean()
call with the parent bean id, returns an error. Similarly, the container’s internalpreInstantiateSingletons()
method ignores bean definitions that are defined as abstract.
![]() |
|
A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information such as initialization method, static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values, or add others, as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.
我們知道,BeanDefinition是 bean標簽的抽象,BeanDefinition和bean標簽中的屬性是一一對應的。BeanDefinition中存儲大量的配置信息。子BeanDefinition能夠從父BeanDefinition中繼承配置信息。當然子BeanDefinition也可以覆蓋或者新增一些新的配置信息。使用父子BeanDefinition能少打很多字(?),更加高效,這是一種模板方式。
If you work with an ApplicationContext interface programmatically, child bean definitions are represented by the ChildBeanDefinition class. Most users do not work with them on this level, instead configuring bean definitions declaratively in something like the ClassPathXmlApplicationContext. When you use XML-based configuration metadata, you indicate a child bean definition by using the parent attribute, specifying the parent bean as the value of this attribute.
在spring中,子BeanDefinition用ChildBeanDefinition表示。但是大家一般不這么使用,而是直接使用ClassPathXmlApplicationContext來配置BeanDefinition。
當使用XML配置子BeanDefinition時,可以使用bean標簽的parent屬性來表示這個bean是個子bean。parent的屬性值就是父bean。
<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBean" init-method="initialize"> <property name="name" value="override"/> <!-- the age property value of 1 will be inherited from parent --> </bean>
A child bean definition uses the bean class from the parent definition if none is specified, but can also override it. In the latter case, the child bean class must be compatible with the parent, that is, it must accept the parent’s property values.
如果子bean的class沒有指定,則使用使用父bean的class。如果子bean有指定class,則會覆蓋父bean的class。后一種情況(子bean指定了class),子bean的class必須要和父bean的class兼容,也就是說能夠接受父bean的配置。
A child bean definition inherits scope, constructor argument values, property values, and method overrides from the parent, with the option to add new values. Any scope, initialization method, destroy method, and/or static factory method settings that you specify will override the corresponding parent settings.
子BeanDefinition繼承父BeanDefinition的scorp、constructor 、property 、method overrides,並添加新配置。子BeanDefinition會覆蓋父子BeanDefinition的配置有:initialization method、destroy method、static factory method settings。
The remaining settings are always taken from the child definition: depends on, autowire mode, dependency check, singleton, lazy init.
子BeanDefinition有些配置始終不會繼承父BeanDefinition:depends on、autowire mode、dependency check、singleton、lazy init。
The preceding example explicitly marks the parent bean definition as abstract by using the abstract attribute. If the parent definition does not specify a class, explicitly marking the parent bean definition as abstract is required, as follows:
<bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property name="name" value="override"/> <!-- age will inherit the value of 1 from the parent bean definition--> </bean>
前一個例子通過abstract屬性將一個bean定義為抽象的。如果父bean沒有指定class,則需要將其定義為抽象的。
The parent bean cannot be instantiated on its own because it is incomplete, and it is also explicitly marked as abstract. When a definition is abstract like this, it is usable only as a pure template bean definition that serves as a parent definition for child definitions. Trying to use such an abstract parent bean on its own, by referring to it as a ref property of another bean or doing an explicit getBean() call with the parent bean id, returns an error. Similarly, the container’s internal preInstantiateSingletons() method ignores bean definitions that are defined as abstract.
如果bean被定義為抽象,則不能初始化該bean。如果一個bean被指定為抽象,那么也就可以認為該bean就是一個模板,子bean通用配置的抽象。如果通過ref屬性引用抽象bean,或者通過getBean獲取抽象bean,則會返回錯誤。而且,spring的內部方法preInstantiateSingletons會忽略被定義為抽象的bean。
二、獲取RootBeanDefinition
如果緩存中不存在已經加載的單例bean,就需要從頭開始bean的加載過程了。
而加載bean的第一步就是獲取bean的RootBeanDefinition,獲取RootBeanDefinition的過程就是子bean合並父bean配置信息的過程
我們知道bean標簽在spring內部的表現的BeanDefinition。
回憶下,在注冊bean標簽的時候,不論是子bean還是父bean都是用的GenericBeanDefinition表示。子bean為什么不使用ChildBeanDefinition?
進入ChildBeanDefinition源碼,發現這樣一段注釋:
NOTE:Since Spring 2.5, the preferred way to register bean definitions programmatically is the {@link GenericBeanDefinition} class,which allows to dynamically define parent dependencies through the {@link GenericBeanDefinition#setParentName} method. This effectively supersedes the ChildBeanDefinition class for most use cases.
- 1
- 2
- 3
- 4
從spring2.5起,注冊bean的首選是GenericBeanDefinition,GenericBeanDefinition允許動態的定義父bean(通過setParentName方法)。
下面分析代碼:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Quick check on the concurrent map first, with minimal locking. //查看mergedBeanDefinitions集合中是否存在,如果存在直接返回。最小化的同步mergedBeanDefinitions RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null) { return mbd; } return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { return getMergedBeanDefinition(beanName, bd, null); }
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) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } else { // Child bean definition: needs to be merged with parent. BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); if (!beanName.equals(parentBeanName)) { pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // Deep copy with overridden values. mbd = new RootBeanDefinition(pbd); mbd.overrideFrom(bd); } // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. // Let's correct this on the fly here, since this might be the result of // parent-child merging for the outer bean, in which case the original inner bean // definition will not have inherited the merged outer bean's singleton status. if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // Only cache the merged bean definition if we're already about to create an // instance of the bean, or at least have already created an instance before. if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } return mbd; } }
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException { String beanName = transformedBeanName(name); // Efficiently check whether bean definition exists in this factory. if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) { return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName); } // Resolve merged bean definition locally. return getMergedLocalBeanDefinition(beanName); }
一提到父子bean,那么代碼中肯定就是遞歸。不斷的合並父bean的配置。
如果parentName 等於當前beanName,則需要到parentFactory中尋找parentName。如果找不到則拋出NoSuchBeanDefinitionException。
如果beanName不存在當前beanFactory中,則取parentFactory中尋找。