Spring的Bean生命周期之BeanDefinition詳解
1 BeanDefinition
在spring bean創(chuàng)建過程 依賴 BeanDefinition 中的信息處理bean的生產(chǎn)。
BeanDefinition 是 Spring Framework 中定義 Bean 的配置元信息接口

在處理配置文件生成BeanDefinition主要經(jīng)過:Spring Bean 讀取解析配置信息、spring bean 注冊階段、Spring BeanDefinition 合并階段
1.1 Spring Bean 讀取解析配置信息
spring bean的配置信息分為:
- 面向資源
- XML配置的處理主要使用 :XmlBeanDefinitionReader
- Properties 資源配置:PropertiesBeanDefinitionReader
- 面向注解
- 面向注解 BeanDefinition 解析: AnnotatedBeanDefinitionReader
1.1.1 XML 配置的處理主要使用的事例
這里主要基于xml中的構(gòu)造方法處理:
public class UserHolder {
private User user;
public UserHolder() {
}
public UserHolder(User user) {
this.user = user;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "UserHolder{" +
"user=" + user +
'}';
}
}/**
* 基于 XML 資源的依賴 Constructor 注入示例
*/
public class XmlDependencyConstructorInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String xmlResourcePath = "classpath:/META-INF/dependency-constructor-injection.xml";
// 加載 XML 資源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 依賴查找并且創(chuàng)建 Bean
UserHolder userHolder = beanFactory.getBean(UserHolder.class);
System.out.println(userHolder);
}
}xml的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
<property name="name" value="測試"/>
<property name="city" value="test"/>
<property name="workCities" value="BEIJING,HANGZHOU"/>
<property name="lifeCities">
<list>
<value>BEIJING</value>
<value>SHANGHAI</value>
</list>
</property>
<property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
</bean>
<bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
primary="true">
<property name="address" value="杭州"/>
</bean>
<bean class="org.geekbang.thinking.in.spring.ioc.dependency.injection.UserHolder">
<constructor-arg name="user" ref="superUser" />
</bean>
</beans>1.1.2 注解 BeanDefinition 解析示例
使用AnnotatedBeanDefinitionReader 來處理注解 的類生成BeanDefinition 并注入到容器中
public class AnnotatedBeanDefinitionParsingDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 基于 Java 注解的 AnnotatedBeanDefinitionReader 的實現(xiàn)
AnnotatedBeanDefinitionReader beanDefinitionReader = new AnnotatedBeanDefinitionReader(beanFactory);
int beanDefinitionCountBefore = beanFactory.getBeanDefinitionCount();
// 注冊當前類(非 @Component class)
beanDefinitionReader.register(AnnotatedBeanDefinitionParsingDemo.class);
int beanDefinitionCountAfter = beanFactory.getBeanDefinitionCount();
int beanDefinitionCount = beanDefinitionCountAfter - beanDefinitionCountBefore;
System.out.println("已增加加載 BeanDefinition 數(shù)量:" + beanDefinitionCount);
// 普通的 Class 作為 Component 注冊到 Spring IoC 容器后,通常 Bean 名稱為 annotatedBeanDefinitionParsingDemo
// Bean 名稱生成來自于 BeanNameGenerator,注解實現(xiàn) AnnotationBeanNameGenerator
AnnotatedBeanDefinitionParsingDemo demo = beanFactory.getBean("annotatedBeanDefinitionParsingDemo",
AnnotatedBeanDefinitionParsingDemo.class);
System.out.println(demo);
}
}1.2 spring bean 注冊階段
在步驟一中生成的 BeanDefinition 需要注入到容器中 BeanDefinition 注冊接口:BeanDefinitionRegistry 注冊BeanDefinition其實就是把 BeanDefinition信息放入map中
注冊到一個map中
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
1.3 Spring BeanDefinition 合并階段
在合并階段主要解決Bean繼承時子類合并父類公共屬性問題:
可以把 父BeanDefinition的信息合并到子BeanDefinition中;合并中的BeanDefinition主要依靠下面三個子類實現(xiàn)的
- ChildBeanDefinition:要指定父類。實例化的時候一定要是 父BeanDefinition,永遠只能作為一個子BeanDefinition。
- RootBeanDefinition: spring 2.5的首選 一般表示。 作為父beanDefinition出現(xiàn)也可以作為普遍的bd 但是不能設(shè)置父beanDefinition即 不能作為子bd。
- GenericBeanDefinition:spring 2.5 以后出現(xiàn)的 常用的 可以 替換 ChildBeanDefinition 但是不能替換 RootBeanDefinition??梢酝瓿蒖ootBeanDefinition和ChildBeanDefinition 兩種的功能。
合并過程一般是把 GenericBeanDefinition的處理合并成 RootBeanDefinition。
1.3.1 BeanDefinition 合并示例
public class MergedBeanDefinitionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 基于 XML 資源 BeanDefinitionReader 實現(xiàn)
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String location = "META-INF/dependency-lookup-context.xml";
// 基于 ClassPath 加載 XML 資源
Resource resource = new ClassPathResource(location);
// 指定字符編碼 UTF-8
EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");
int beanNumbers = beanDefinitionReader.loadBeanDefinitions(encodedResource);
System.out.println("已加載 BeanDefinition 數(shù)量:" + beanNumbers);
// 通過 Bean Id 和類型進行依賴查找
User user = beanFactory.getBean("user", User.class);
System.out.println(user);
User superUser = beanFactory.getBean("superUser", User.class);
System.out.println(superUser);
}
}<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- <context:annotation-config/>-->
<!-- <context:component-scan base-package="org.acme" />-->
<!-- Root BeanDefinition 不需要合并,不存在 parent -->
<!-- 普通 beanDefinition GenericBeanDefinition -->
<!-- 經(jīng)過合并后 GenericBeanDefinition 變成 RootBeanDefinition -->
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
<property name="name" value="小馬哥"/>
<property name="city" value="HANGZHOU"/>
<property name="workCities" value="BEIJING,HANGZHOU"/>
<property name="lifeCities">
<list>
<value>BEIJING</value>
<value>SHANGHAI</value>
</list>
</property>
<property name="configFileLocation" value="classpath:/META-INF/user-config.properties"/>
</bean>
<!-- 普通 beanDefinition GenericBeanDefinition -->
<!-- 合并后 GenericBeanDefinition 變成 RootBeanDefinition,并且覆蓋 parent 相關(guān)配置-->
<!-- primary = true , 增加了一個 address 屬性 -->
<bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
primary="true">
<property name="address" value="杭州"/>
</bean>
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
</beans>執(zhí)行結(jié)果。
我們可以看到 SuperUser中包括的 其父類 User的相關(guān)屬性
已加載 BeanDefinition 數(shù)量:3
User{id=1, name='小馬哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='user'}
SuperUser{address='杭州'} User{id=1, name='小馬哥', city=HANGZHOU, workCities=[BEIJING, HANGZHOU], lifeCities=[BEIJING, SHANGHAI], configFileLocation=class path resource [META-INF/user-config.properties], company=null, context=null, contextAsText='null', beanName='superUser'}
1.3.2 源碼分析
合并 BeanDefinition 操作在 AbstractBeanFactory中發(fā)doGetBean()方法中的 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable 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)) {
//TODO 這里這次在去合并父類的 父類 這里使用遞歸的處理
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());
}
// Cache the merged bean definition for the time being
// (it might still get re-merged later on in order to pick up metadata changes)
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
return mbd;
}
}這一節(jié)我們簡單的介紹了 spring BeanDefinition的處理過程。
到此這篇關(guān)于Spring的Bean生命周期之BeanDefinition詳解的文章就介紹到這了,更多相關(guān)Bean生命周期之BeanDefinition內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot基礎(chǔ)學習之初識SpringBoot
今天帶大家學習Springboot基礎(chǔ)知識,文中有非常詳細的圖文解說及代碼示例,對正在學習java基礎(chǔ)的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
Spring Security 自定義資源服務(wù)器實踐過程
這篇文章主要介紹了Spring Security 自定義資源服務(wù)器實踐,我們通過自己搭建的授權(quán)服務(wù)器和資源服務(wù)器,完整體驗了OAuth2流程,需要的朋友可以參考下2022-08-08
詳解Java的JDBC中Statement與PreparedStatement對象
這篇文章主要介紹了詳解Java的JDBC中Statement與PreparedStatement對象,PreparedStatement一般來說比使用Statement效率更高,需要的朋友可以參考下2015-12-12
IntelliJ-Idea導出可執(zhí)行Jar流程解析
這篇文章主要介紹了IntelliJ-Idea導出可執(zhí)行Jar流程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12

