文章目錄
夯實Spring系列|第五章:Spring Bean 定義
1.項目環境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模塊:spring-bean
2.什麼是 BeanDefinition?
什麼是 Spring Bean 的定義,或者說它是用來做什麼?
Spring 應用上下文啓動時,將Xml文件、註解、API等等信息解析成 BeanDefinition 。通過依賴查找 getBean()
等方式獲取 Bean 的對象時,容器獲取到 Bean 定義相關的元信息,通過這些信息來創建對應 Bean 對象。
BeanDefinition 是 Spring FrameWork 中定義 Bean 的配置元信息接口
- Bean 的類名
- Bean 行爲配置元素,如作用域、自動綁定模式、生命週期回調等等
- 其他Bean引用,又可稱作 合作者(Collaborators)或者 依賴(Dependencies)
- 配置設置,比如Bean屬性(Properties)
3.BeanDefinition 元信息
屬性(Property) | 說明 |
---|---|
Class | Bean 全類名,必須是具體類,不能用抽象類或者接口 |
Name | Bean 的名稱或者 ID |
Scope | Bean 的作用域(singleton、prototype等) |
Constructor arguments | Bean 構造器參數(用於依賴注入) |
Properties | Bean 屬性設置(用於依賴注入) |
Autowiring mode | Bean 自動綁定模式(通過名稱 byName) |
Lazy initalization mode | Bean 延遲初始化模式(延遲和非延遲) |
Initalization method | Bean 初始化回調方法名稱 |
Destruction method | Bean 銷燬回調方法名稱 |
4.BeanDefinition 構建
- 通過 BeanDefinitionBuiler
- 通過 AbstractBeanDefinition 以及派生類
示例:
/**
* {@link org.springframework.beans.factory.config.BeanDefinition} 構建示例
*/
public class BeanDefinitionCreationDemo {
public static void main(String[] args) {
//1.通過 BeanDefinitionBuilder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
//通過屬性設置
beanDefinitionBuilder.addPropertyValue("age", 18)
.addPropertyValue("id", 1L)
.addPropertyValue("name", "xwf");
// 獲取 BeanDefinition 對象
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// beanDefinition 並非 Bean 的終態 可以自定義修改
//2.通過 AbstractBeanDefinition 以及派生類
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
//通過屬性設置
MutablePropertyValues propertyValues = new MutablePropertyValues();
// propertyValues.addPropertyValue("age", 18);
// propertyValues.addPropertyValue("id", 1L);
// propertyValues.addPropertyValue("name", "xwf");dependency-lookup-context.xml
propertyValues.add("age", 18)
.add("id", 1L)
.add("name", "xwf");
genericBeanDefinition.setPropertyValues(propertyValues);
}
}
其實通過 API 來定義和 XML 文件配置可以類比
5.Spring Bean 的命名
5.1 Bean 名稱
可以類比 xml配置文件<bean …>的 id、name 屬性。
- 每個 Bean 擁有一個或者多個標識符(identifiers),這些標識符在 Bean 所在的容器必須是唯一的。通常一個 Bean 僅有一個標識符,如果需要額外的,可考慮使用別名(Alias)來擴充
- 在基於 XML 的配置元信息中,開發人員可用 id 或者 name 屬性來規定 Bean 的標識符。通常 Bean 的標識符由字母組成,允許出現特殊字符;如果想要引入 Bean 的別名的話,可以在 name 屬性使用半角逗號(,)或者分好(;)來間隔。
- Bean 的 id 或者 name 屬性並非必須制定,如果留空的話,容器會爲 Bean 自動生成一個唯一的名稱。Bean 的命名儘管沒有限制,不過官方建議採用駝峯的方式,更符合 Java 命名規範。
5.2 Bean名稱生成器
- org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName(since Spring 2.0.3)
- DefaultBeanNameGenerator
- AnnotationBeanNameGenerator
我們可以看下其中一個實現
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
String generatedBeanName = definition.getBeanClassName();
...
String id = generatedBeanName;
if (isInnerBean) {//如果是內嵌的 BeanName +'#'+ hashCode
// Inner bean: generate identity hashcode suffix.
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {//如果是唯一的
// BeanName +'#'+ 數字
return uniqueBeanName(generatedBeanName, registry);
}
return id;
}
6.Spring Bean 的別名
Bean 別名(Alias)的價值
- 複用現有的 BeanDefinition
- 更具有場景化的命名方法
示例
新建一個 bean-definitions-context.xml 文件,import
之前的 xml 文件,爲 user
取一個別名。
<?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">
<import resource="classpath:/META-INF/dependency-lookup-context.xml"/>
<!-- 將 Spring 容器中的 user Bean 關聯/建立別名 -->
<alias name="user" alias="xwf-user"/>
</beans>
BeanAlisaDemo
/**
* Bean 別名示例
*/
public class BeanAlisaDemo {
public static void main(String[] args) {
//配置 xml 配置文件
//啓動 spring 應用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
User xwfUser = (User) beanFactory.getBean("xwf-user");
User user = (User) beanFactory.getBean("user");
System.out.println(xwfUser==user);
}
}
輸出結果爲 true
,說明不管是通過名稱還是別名取到的都是同一個對象,說明 spring 容器底層在初始化的時候對名稱和別名做了相應的映射。
7.參考
- 極客時間-小馬哥《小馬哥講Spring核心編程思想》