Spring源碼系列(一)–詳細介紹bean組件
簡介
spring-bean 組件是 Spring IoC 的核心,我們可以使用它的 beanFactory 來獲取所需的對象,對象的實例化、屬性裝配和初始化等都可以交給 spring 來管理。
針對 spring-bean 組件,我計劃分成兩篇博客來講解。本文會詳細介紹這個組件,包括以下內容。下一篇再具體分析它的源碼。
- spring-bean 組件的相關概念:實例化、屬性裝配、初始化、bean、beanDefinition、beanFactory。
- bean 組件的使用:註冊bean、獲取bean、屬性裝配、處理器等。
項目環境說明
正文開始前,先介紹下示例代碼使用的環境等。
工程環境
JDK:1.8.0_231
maven:3.6.1
IDE:Spring Tool Suites4 for Eclipse 4.12
Spring:5.2.6.RELEASE
依賴引入
除了引入 spring,這裏還額外引入了日誌和單元測試。
<properties>
<spring.version>5.2.6.RELEASE</spring.version>
</properties>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<type>jar</type>
</dependency>
</dependencies>
幾個重要概念
實例化、屬性裝配和初始化
在 spring-bean 組件的設計中,這三個詞完整、有序地描述了生成一個新對象的整個流程,是非常重要的理論基礎。它們的具體含義如下:
- 實例化:創建出一個新對象。
- 屬性裝配:給對象的成員屬性賦值。
- 初始化:調用對象的初始化方法。
下面使用一段代碼來簡單演示下這個流程。
public class UserService implements IUserService {
private UserDao userDao;
public UserService() {
super();
System.err.println("UserService構造方法被調用");
System.err.println(" ||");
System.err.println(" \\/");
}
public void init() {
System.err.println("UserService的init方法被調用");
System.err.println(" ||");
System.err.println(" \\/");
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
System.err.println("UserService的屬性裝配中");
System.err.println(" ||");
System.err.println(" \\/");
this.userDao = userDao;
}
}
如果我們將這個 bean 交給 spring 管理,獲取 bean 時會在控制台打印以下內容:
什麼是bean
按照官方的說法, bean 是一個由 Spring IoC 容器實例化、組裝和管理的對象。我認為,這種表述是錯誤的,通過registerSingleton
方式註冊的 bean,它就不是由 Spring IoC 容器實例化、組裝,所以,更準確的表述應該是這樣:
某個類的對象、FactoryBean 對象、描述對象或 FactoryBean 描述對象,被註冊到了 Spring IoC 容器,這時通過 Spring IoC 容器獲取的這個類的對象就是 bean。
舉個例子,使用了 Spring 的項目中, Controller 對象、Service 對象、DAO 對象等都屬於 bean。
至於什麼是 IoC 容器,在 spring-bean 組件中,我認為,beanFactory 就屬於 IoC 容器。
什麼是beanFactory
從客戶端來看,一個完整的 beanFactory 工廠包含以下基本功能:
- 註冊別名。對應下圖的
AliasRegistry
接口。 - 註冊單例對象。對應下圖的
SingletonBeanRegistry
接口。 - 註冊
BeanDefinition
對象。對應下圖的BeanDefinitionRegistry
接口。 - 獲取 bean。對應下圖的
BeanFactory
接口。
在 spring-bean 組件中,DefaultListableBeanFactory
就是一個完整的 beanFactory 工廠,也可以說是一個 IoC 容器。接下來的例子將直接使用它來作為 beanFactory。
至於其他的接口,這裏也補充說明下。HierarchicalBeanFactory
用於提供父子工廠的支持,ConfigurableBeanFactory
用於提供配置 beanFactory 的支持,ListableBeanFactory
用於提供批量獲取 bean 的支持(不包含父工廠的 bean),AutowireCapableBeanFactory
用於提供實例化、屬性裝配、初始化等一系列管理 bean 生命周期的支持。
什麼是beanDefinition
beanDefinaition 是一個描述對象,用來描述 bean 的實例化、初始化等信息。
在 spring-bean 組件中,beanDefinaition主要包含以下四種:
RootBeanDefinition
:beanFactory 中最終用於 createBean 的 beanDefinaition,不允許添加 parentName。在 BeanFactory 中以下三種實現類都會被包裝成RootBeanDefinition
用於 createBean。ChildBeanDefinition
:必須設置 parentName 的 beanDefinaition。當某個 Bean 的描述對象和另外一個的差不多時,我們可以直接定義一個ChildBeanDefinition
,並設置它的 parentName 為另外一個的 beanName,這樣就不用重新設置一份。GenericBeanDefinition
:通用的 beanDefinaition,可以設置 parentName,也可以不用設置。AnnotatedGenericBeanDefinition
:在GenericBeanDefinition
基礎上增加暴露註解數據的方法。
spring-bean 組件提供了BeanDefinitionBuilder
用於創建 beanDefinaition,下面的例子會頻繁使用到。
使用例子
入門–簡單地註冊和獲取bean
下面通過一個入門例子來介紹註冊和獲取 bean 的過程。
@Test
public void testBase() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 創建BeanDefinition對象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
// 註冊Bean
beanFactory.registerBeanDefinition("userService", rootBeanDefinition);
// 獲取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
}
兩種註冊bean的方式
beanFactory 除了支持註冊 beanDefinition,還允許直接註冊 bean 實例,如下。和前者相比,後者的實例化、屬性裝配和初始化都沒有交給 spring 管理。
@Test
public void testRegisterWays() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 註冊Bean-- BeanDefinition方式
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
beanFactory.registerBeanDefinition("userService", rootBeanDefinition);
// 註冊Bean-- Bean實例方式
beanFactory.registerSingleton("userService2", new UserService());
// 獲取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
IUserService userService2 = (IUserService)beanFactory.getBean("userService2");
System.err.println(userService2.get("userId"));
}
當然,這種方式僅支持單例 bean 的註冊,多例的就沒辦法了。
註冊多例bean
默認情況下,我們從 beanFactory 獲取到的 bean 都是單例的,即每次 getBean 獲取到的都是同一個對象,實際項目中,有時我們需要獲取到多例的 bean,這個時候就可以通過設置 beanDefinition 的 scope 來處理。如下:
@Test
public void testScope() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 註冊Bean-- BeanDefinition方式
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
rootBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
beanFactory.registerBeanDefinition("userService", rootBeanDefinition);
// 獲取Bean--通過BeanType
IUserService userService1 = beanFactory.getBean(IUserService.class);
IUserService userService2 = beanFactory.getBean(IUserService.class);
assertNotEquals(userService1, userService2);
}
多種獲取bean的方式
beanFactory 提供了多種方式來獲取 bean 實例,如下。如果同時使用 beanName 和 beanType,獲取到指定 beanName 的 bean 後會進行類型檢查和類型類型,如果都不通過,將會報錯。
@Test
public void testGetBeanWays() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 創建BeanDefinition對象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
// 註冊Bean
beanFactory.registerBeanDefinition("userService", rootBeanDefinition);
// 獲取Bean--通過BeanName
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
// 獲取Bean--通過BeanType
IUserService userService2 = beanFactory.getBean(IUserService.class);
System.err.println(userService2.get("userId"));
// 獲取Bean--通過BeanName+BeanType的方式
IUserService userService3 = beanFactory.getBean("userService", IUserService.class);
System.err.println(userService3.get("userId"));
}
不同形式的beanName
通過 name 獲取 bean,這個 name 包含以下三種形式:
- beanName,即註冊 bean 時用的 beanName。這是使用最多的形式,需要注意一點,如果 beanName 對應的 bean 是
FactoryBean
,並不會返回FactoryBean
的實例,而是會返回FactoryBean.getObject
方法的返回結果。 - alias,即我們通過
SimpleAliasRegistry.registerAlias(name, alias)
方法註冊到 beanFactory 的別名。這時,需要將 name 解析為 alias 對應的 beanName 來獲取 bean。 - ‘&’ + factorybeanName,這時為了獲取
FactoryBean
的一種特殊格式。
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 註冊Bean--註冊的是一個 FactoryBean
UserServiceFactoryBean userServiceFactoryBean = new UserServiceFactoryBean();
beanFactory.registerSingleton("userServiceFactoryBean", userServiceFactoryBean);
// 註冊BeanName的別名
beanFactory.registerAlias("userServiceFactoryBean", "userServiceAlias01");
// 通過BeanName獲取
assertEquals(userServiceFactoryBean.getObject(), beanFactory.getBean("userServiceFactoryBean"));
// 通過別名獲取
assertEquals(userServiceFactoryBean.getObject(), beanFactory.getBean("userServiceAlias01"));
// 通過&+FactoryBeanName的方式
assertEquals(userServiceFactoryBean, beanFactory.getBean("&userServiceFactoryBean"));
bean衝突的處理
通過 beanType 的方式獲取 bean,如果存在多個同類型的 bean且無法確定最優先的那一個,就會報錯。
@Test
public void testPrimary() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 創建BeanDefinition對象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
// 註冊Bean
beanFactory.registerBeanDefinition("UserRegisterBeanDefinition", rootBeanDefinition);
beanFactory.registerSingleton("UserRegisterSingleton", new User("zzs002", 19));
beanFactory.registerSingleton("UserRegisterSingleton2", new User("zzs002", 18));
// 獲取Bean--通過BeanType
User user = beanFactory.getBean(User.class);
System.err.println(user);
}
運行以上方法,將出現 NoUniqueBeanDefinitionException 的異常。
針對上面的這種問題,spring 的處理方法如下:
- 檢查是否存在唯一一個通過
registerBeanDefinition
且isPrimary = true
的(存在多個會報錯),存在的話將它作為匹配到的唯一 beanName; - 通過我們註冊的
OrderComparator
來確定優先值最小的作為唯一 beanName。注意,通過registerSingleton
註冊的和通過registerBeanDefinition
註冊的,比較的對象是不一樣的,前者比較的對象是 bean 實例,後者比較的對象是 bean 類型,另外,這種方法最好不要存在相同優先級的 bean。
所以,為了解決這種衝突,可以設置BeanDefinition
對象的 isPrimary = true,或者為 beanFactory 設置OrderComparator
,代碼如下:
@Test
public void testPrimary() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 為BeanFactory設置比較器
beanFactory.setDependencyComparator(new OrderComparator() {
@Override
public Integer getPriority(Object obj) {
return obj.hashCode();
}
});
// 創建BeanDefinition對象
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
// rootBeanDefinition.setPrimary(true); // 設置BeanDefinition對象為isPrimary
// 註冊Bean
beanFactory.registerBeanDefinition("userRegisterBeanDefinition", rootBeanDefinition);
beanFactory.registerSingleton("userRegisterSingleton", new User("zzs002", 19));
beanFactory.registerSingleton("userRegisterSingleton2", new User("zzs003", 18));
// 獲取Bean--通過BeanType
User user = beanFactory.getBean(User.class);
System.err.println(user);
}
使用TypeConverter獲取自定義類型的對象
當我們使用 beanType 來獲取 bean 時,如果獲取到的 bean 不是指定的類型,這時,不會立即報錯,beanFactory 會嘗試使用我們註冊的TypeConverter
來強制轉換。而這個類型轉換器我們可以自定義設置,如下。
@Test
public void testTypeConverter() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 註冊類型轉換器
beanFactory.setTypeConverter(new TypeConverterSupport() {
@SuppressWarnings("unchecked")
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
// 將User轉換為UserVO
if(UserVO.class.equals(requiredType) && User.class.isInstance(value)) {
User user = (User)value;
return (T)new UserVO(user);
}
return null;
}
});
BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
beanFactory.registerBeanDefinition("User", rootBeanDefinition);
UserVO bean = beanFactory.getBean("User", UserVO.class);
Assert.assertTrue(UserVO.class.isInstance(bean));
}
屬性裝配
beanFactory 在進行屬性裝配時,會讀取 beanDefinition 對象中的PropertyValues
中的propertyName=propertyValue,所以,我們想要對 bean 注入什麼參數,只要在定義 beanDefinition 時指定就行。
@Test
public void testPopulate() {
// 創建BeanFactory對象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 定義userService的beanDefinition
AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
// 定義userDao的beanDefinition
AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition();
// 給userService設置裝配屬性
userServiceBeanDefinition.getPropertyValues().add("userDao", userDaoBeanDefinition);
// userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
// 註冊Bean
beanFactory.registerBeanDefinition("userService", userServiceBeanDefinition);
beanFactory.registerBeanDefinition("userDao", userDaoBeanDefinition);
// 獲取Bean
IUserService userService = (IUserService)beanFactory.getBean("userService");
userService.save(null);
}
運行以上方法,發現 userDao 對象被成功注入到了 userService 對象中!
這裏補充一點,beanFactory 除了通過 beanDefinition 中的PropertyValues
獲取 propertyName=propertyValue,還可以讀取 bean 中的屬性來自動定義 propertyName=propertyValue,只要設置 beanDefinition 的 autowireMode 就可以了。
bean 實例化、屬性裝配和初始化的處理器
前面講到,我們將 bean 的實例化、屬性裝配和初始化都交給了 spring 處理,然而,有時我們需要在這些節點對 bean 進行自定義的處理,這時就需要用到 beanPostProcessor。
這裏我簡單演示下如何添加處理器,以及處理器的執行時機,至於處理器的具體實現,我就不多擴展了。
@Test
public void testPostProcessor() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 添加實例化處理器
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
// 如果這裏我們返回了對象,則beanFactory會將它作為bean直接返回,不再進行bean的實例化、屬性裝配和初始化等操作
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if(UserService.class.equals(beanClass)) {
System.err.println("bean實例化之前的處理。。 --> ");
}
return null;
}
// 這裏通過返回的布爾值判斷是否需要繼續對bean進行屬性裝配和初始化等操作
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("bean實例化之後的處理。。 --> ");
}
return true;
}
});
// 添加裝配處理器
beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
// 這裏可以在屬性裝配前對參數列表進行調整
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("屬性裝配前對參數列表進行調整 --> ");
}
return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
}
});
// 添加初始化處理器
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
// 初始化前對bean進行改造
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("初始化前,對Bean進行改造。。 --> ");
}
return bean;
}
// 初始化后對bean進行改造
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(UserService.class.isInstance(bean)) {
System.err.println("初始化后,對Bean進行改造。。 --> ");
}
return bean;
}
});
// 定義userService的beanDefinition
AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();
// 定義userDao的beanDefinition
AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition();
// 給userService添加裝配屬性
userServiceBeanDefinition.getPropertyValues().add("userDao", userDaoBeanDefinition);
// 給userService設置初始化方法
userServiceBeanDefinition.setInitMethodName("init");
// 註冊bean
beanFactory.registerBeanDefinition("userService", userServiceBeanDefinition);
beanFactory.registerBeanDefinition("userDao", userDaoBeanDefinition);
IUserService userService = (IUserService)beanFactory.getBean("userService");
System.err.println(userService.get("userId"));
}
運行以上方法,控制台打印出了整個處理流程。實際開發中,我們可以通過設置處理器來改變改造生成的 bean 。
以上,基本介紹完 spring-bean 組件的使用,下篇博客再分析源碼,如果在分析過程中發現有其他特性,也會在這篇博客的基礎上擴展。
相關源碼請移步: spring-beans
本文為原創文章,轉載請附上原文出處鏈接:https://www.cnblogs.com/ZhangZiSheng001/p/13126053.html
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※超省錢租車方案
※別再煩惱如何寫文案,掌握八大原則!
※回頭車貨運收費標準
※教你寫出一流的銷售文案?
※產品缺大量曝光嗎?你需要的是一流包裝設計!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益