spring-bean 組件是 IoC 的核心,咱們能夠經過BeanFactory
來獲取所需的對象,對象的實例化、屬性裝配和初始化均可以交給 spring 來管理。html
針對 spring-bean 組件,我計劃分紅兩篇博客來說解。本文會詳細介紹這個組件,包括如下內容。下一篇再具體分析它的源碼。java
正文開始前,先介紹下示例代碼使用的環境等。git
JDK:1.8.0_231github
maven:3.6.1spring
IDE:Spring Tool Suites4 for Eclipse 4.12api
Spring:5.2.6.RELEASEmaven
除了引入 spring,這裏還額外引入了日誌和單元測試。ide
<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 組件的設計中,這三個詞完整、有序地描述了生成一個新對象的整個流程,是很是重要的理論基礎。它們的具體含義以下:post
下面使用一段代碼來簡單演示下這個流程。單元測試
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 是一個由 Spring IoC 容器實例化、組裝和管理的對象。我認爲,這種表述是錯誤的,經過registerSingleton
方式註冊的 bean,它就不是由 Spring IoC 容器實例化、組裝,因此,更準確的表述應該是這樣:
對象的實例,或者它的描述對象被註冊到了 Spring IoC 容器,而且經過 Spring IoC 容器來獲取獲得的對象,就是 bean。
舉個例子,使用了 Spring 的項目中, Controller 對象、Service 對象、DAO 對象等都屬於 bean。
至於什麼是 IoC 容器,在 spring-bean 組件中,我認爲,beanFactory 就屬於 IoC 容器。
從客戶端來看,一個完整的 beanFactory 工廠通常包含如下功能:
AliasRegistry
接口。SingletonBeanRegistry
接口。BeanDefinition
對象。對應下圖的BeanDefinitionRegistry
接口。BeanFactory
接口。在 spring-bean 組件中,DefaultListableBeanFactory
就是一個完整的 beanFactory 工廠,也能夠說是一個 IoC 容器。接下來的例子將直接使用它來做爲 beanFactory。
至於其餘的接口,這裏也補充說明下。HierarchicalBeanFactory
用於提供父子工廠的支持,ConfigurableBeanFactory
用於提供配置 beanFactory 的支持,ListableBeanFactory
用於提供批量獲取 bean 的支持(不包含父工廠的 bean),AutowireCapableBeanFactory
用於提供實例化、屬性裝配、初始化等一系列管理 bean 生命週期的支持。
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 的過程。
@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")); }
beanFactory 除了支持註冊 beanDefinition,還容許直接註冊 bean 實例,以下。
@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 的註冊,多例的就沒辦法了。
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 獲取 bean,這個 beanName 包含如下三種形式:
FactoryBean
,不會返回FactoryBean
的實例,而是會返回FactoryBean.getObject
方法的返回結果。FactoryBean
的實例,形式爲:一個或多個& + factorybeanName。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"));
經過 beanType 的方式獲取 bean,若是存在多個同類型的 bean且沒法肯定最優先的那一個,就會報錯。
@Test public void testPrimary() { // 建立BeanFactory對象 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 建立BeanDefinition對象 BeanDefinition rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); BeanDefinition rootBeanDefinition2 = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 註冊Bean beanFactory.registerBeanDefinition("UserRegisterBeanDefinition", rootBeanDefinition); beanFactory.registerBeanDefinition("UserRegisterBeanDefinition2", rootBeanDefinition2); 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 的異常。
針對上面的這種問題,能夠採起兩種解決方案:
BeanDefinition
對象的 isPrimary = true。這種方式不適用於 registerSingleton 的狀況。其中,1 方案要優先於 2 方案。
@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 BeanDefinition rootBeanDefinition2 = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 註冊Bean beanFactory.registerBeanDefinition("userRegisterBeanDefinition", rootBeanDefinition); beanFactory.registerBeanDefinition("userRegisterBeanDefinition2", rootBeanDefinition2); 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); }
默認狀況下,咱們從 beanFactory 獲取到的 bean 都是單例的,即同一個對象,實際項目中,有時咱們須要獲取到多例的 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); }
當咱們同時使用 beanName + 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)); }
若是我想在UserService
中注入UserDao
,首先,須要在UserService
中添加定義的 setter/getter 方法,以下:
public class UserService implements IUserService { private UserDao userDao; public void save(User user) { System.err.println("Service save user:" + user); userDao.save(user); } public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
在註冊 bean 時須要注意,UserDao
的 bean 也須要註冊,並且須要更改 userServiceBeanDefinition 的 autowireType 爲按 beanType 注入或按 beanName 注入。
@Test public void testPopulate() { // 建立BeanFactory對象 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 建立BeanDefinition對象 AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition(); // userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME); AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition(); // 註冊Bean beanFactory.registerBeanDefinition("userService", userServiceBeanDefinition); beanFactory.registerBeanDefinition("userDao", userDaoBeanDefinition); // 獲取Bean IUserService userService = (IUserService)beanFactory.getBean("userService"); userService.save(null); }
運行以上方法,屬性裝配正常。
前面講到,咱們將 bean 的實例化、屬性裝配和初始化都交給了 spring 處理,然而,有時咱們須要在這些節點對 bean 進行自定義的處理,這時就須要用到 beanPostProcessor。
這裏我簡單演示下如何添加處理器,以及處理器的執行時機,至於處理器的具體實現,我就很少擴展了。
@Test public void testPostProcessor() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 添加實例化處理器 beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if(UserService.class.equals(beanClass)) { System.err.println("實例化以前的處理。。 --> "); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if(UserService.class.isInstance(bean)) { System.err.println("實例化以後的處理。。 --> "); } return true; } }); // 添加屬性裝配處理器 beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { @Override 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); } @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { if(UserService.class.isInstance(bean)) { System.err.println("設置參數前對參數進行檢查依賴關係 --> "); } return InstantiationAwareBeanPostProcessor.super.postProcessPropertyValues(pvs, pds, bean, beanName); } }); // 添加初始化處理器 beanFactory.addBeanPostProcessor(new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(UserService.class.isInstance(bean)) { System.err.println("初始化前,對Bean進行改造。。 --> "); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(UserService.class.isInstance(bean)) { System.err.println("初始化後,對Bean進行改造。。 --> "); } return bean; } }); AbstractBeanDefinition userServiceBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition(); AbstractBeanDefinition userDaoBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserDao.class).getBeanDefinition(); userServiceBeanDefinition.setInitMethodName("init"); userServiceBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); 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
本文爲原創文章,轉載請附上原文出處連接:http://www.javashuo.com/article/p-uegndqxn-br.html