您好,登錄后才能下訂單哦!
這篇文章主要介紹“IOC容器啟動和Bean實(shí)例化實(shí)例分析”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“IOC容器啟動和Bean實(shí)例化實(shí)例分析”文章能幫助大家解決問題。
IOC 容器可以分為兩個(gè)階段 : 容器啟動階段和 Bean 實(shí)例化階段。
Spring 的 IoC 容器在實(shí)現(xiàn)的時(shí)候, 充分運(yùn)用了這兩個(gè)階段的不同特點(diǎn), 在每個(gè)階段都加入了相應(yīng)的容器擴(kuò)展點(diǎn), 支持開發(fā)者根據(jù)具體場景的需要, 添加自定義的擴(kuò)展邏輯.
容器啟動, 首先要加載配置元數(shù)據(jù) ( Configuration MetaData ).
容器使用工具類 BeanDefinitionReader 對加載的配置元數(shù)據(jù)進(jìn)行解析和分析, 并將分析后的信息組裝為相應(yīng)的 Bean 定義對象 BeanDefinition, 最后把這些保存了 bean 定義必要信息的 BeanDefinition, 注冊到相應(yīng)的 BeanDefinitionRegistry, 這樣容器啟動工作就完成了. ( 將 XML 信息映射到 BeanDefinition 對象上 )
BeanDefinition 對象中保存的屬性很多,如下 :
BeanDefinitionRegister 接口用來注冊 BeanDefinition. 該接口的默認(rèn)實(shí)現(xiàn)類為 DefaultListableBeanFactory. 在該實(shí)現(xiàn)類中, 有一個(gè)成員屬性定義如下 :
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, bean>(64);
BeanDefinition 就是被保存到這個(gè) map 中的, key 為 beanName, value 為 BeanDefinition 對象.
當(dāng)調(diào)用者通過容器的 getBean() 方法明確地想要獲取某個(gè)對象, 或者因依賴關(guān)系, 容器需要隱式地調(diào)用 getBean() 方法時(shí), 就會觸發(fā)容器的第二階段.
該階段, 容器會首先檢查所請求的對象之前是否已經(jīng)初始化. 如果沒有, 則會根據(jù)注冊的 BeanDefinition 的信息, 實(shí)例化這個(gè)對象 , 并為其注入依賴. 如果該對象實(shí)現(xiàn)了某些回調(diào)接口, 也會根據(jù)回調(diào)接口的要求來裝配它. 當(dāng)該對象裝配完畢之后, 容器會立即將其返回給請求方去使用.
Bean 創(chuàng)建的步驟和普通對象的創(chuàng)建步驟不同, 普通的對象在創(chuàng)建時(shí), 由類加載器找到 xxx.class 文件, 然后創(chuàng)建 xxx 對象即可. Spring 中 bean 的創(chuàng)建多了一步, 先是由 類加載器找到 xxx.class 文件, 然后將其解析組裝為 BeanDefinition 對象, 然后 Spring 根據(jù) BeanDefinition 信息來創(chuàng)建對象.
普通對象 : xxx.class -> object
Bean 對象 : xxx.class -> BeanDefinition -> bean 對象
可以看到, Spring 在實(shí)例化對象時(shí), 脫離了配置元數(shù)據(jù)中的信息, 而是使用的 BeanDefinition 中的信息, 這就意味著我們可以修改 BeanDefinition, 從而改變 Spring 生成的對象的屬性, 甚至是修改最后生成的對象所屬的類.
Spring 提供了一種叫做 BeanFactoryPostProcessor 的容器擴(kuò)展機(jī)制. 該機(jī)制允許開發(fā)者在容器實(shí)例化相應(yīng) Bean 對象之前, 對注冊到容器的 BeanDefinition 的信息做出修改. 即在容器的第一階段的最后進(jìn)行一些自定義操作. 比如修改其中 bean 定義的某些屬性, 為 bean 定義增加其他信息等.
Spring 中自帶的 BeanFactoryPostProcessor 接口的實(shí)現(xiàn):
org.springframework.beans. factory.config.PropertyPlaceholderConfigurer占位符機(jī)制
org.springframework.beans.factory. config.PropertyOverrideConfigurer 重寫屬性值
...
同時(shí)也支持開發(fā)者通過實(shí)現(xiàn) BeanFactoryPostProcessor 接口自定義擴(kuò)展.
PropertyPlaceholderConfigurer 允許開發(fā)者在 XML 配置文件中使用占位符, 并將這些占位符所代表的資源單獨(dú)配置到簡單的 properties 文件中來加載.
比如將數(shù)據(jù)庫的連接信息保存在 properties 中.
<context:property-placeholder location="classpath:dbconfig.properties" /> <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
基本流程 :
當(dāng) BeanFactory 在第一階段加載完成所有配置信息時(shí), BeanFactory 中保存的對象的屬性信息還只是以占位符的形式存在, 如 ${jdbc.url}, ${jdbc.driver}.
當(dāng) PropertyPlaceholderConfigurer 作為 BeanFactoryPostProcessor 被應(yīng)用時(shí), 它會使用 properties 配置文件中的配置信息來替換相應(yīng) BeanDefinition 中占位符所表示的屬性值.
這樣, 當(dāng)進(jìn)入容器實(shí)現(xiàn)的第二階段實(shí)例化 bean 時(shí), bean 定義中的屬性值就是最終替換完成的了.
PropertyOverrideConfigurer 允許你對容器中配置的任何你想處理的 bean 定義的 property 信息進(jìn)行覆蓋替換.
PropertyOverrideConfigurer 的 properties 文件中的配置項(xiàng), 可以覆蓋掉原來 XML 中的 bean 定義的 property 信息.
此案例使用注解的方式來配置元數(shù)據(jù).
現(xiàn)在有兩個(gè)類, 一個(gè)是 User 類, 一個(gè)是 Good 類, User 類歸 Spring 管理, 而 Good 類并不歸 Spring 管理. 類的定義如下 :
@Data @AllArgsConstructor @Accessors(chain = true) @Component //被 Spring 掃描 public class User { private int id; private String name; public User(){ System.out.println("調(diào)用了 user 的無參數(shù)的構(gòu)造方法"); } } @Data @AllArgsConstructor @Accessors(chain = true) public class Good { private int id; private String name; public Good(){ System.out.println("調(diào)用了 good 的無參數(shù)的構(gòu)造方法"); } }
AppConfig 類進(jìn)行注解的掃描.
@ComponentScan(value = {"it.com"}) public class AppConfig { }
Test 類用來測試 :
public static void main(String[] args){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); User user = (User) context.getBean("user"); Good good = (Good) context.getBean("good"); }
運(yùn)行測試類, 結(jié)果如下 :
User 類配置了被 Spring 掃描, 所以可以獲取到 user 對象, 而 Good 類沒有配置被掃描, 所以無法獲取 good 對象, 而報(bào)錯(cuò).
現(xiàn)在我們要做的就是, 讓沒有被 Spring 管理的 Good 類, 也能從 Spring 容器中獲取到它的實(shí)例. 實(shí)現(xiàn)上述目的的思路就是, 對容器啟動的第一階段生成的 User 類的 BeanDefinition 類做修改.
創(chuàng)建自定義的 BeanFactoryPostProcessor 如下 :
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 通過 Spring 中 bean 的名字獲取到指定的 bean GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("user"); // 輸出 beanDefinition 的信息 System.out.println(beanDefinition.getBeanClass()); System.out.println(beanDefinition.getFactoryBeanName()); // 然后進(jìn)行貍貓換太子,將 User 類對應(yīng)的 BeanDefinition 對象的 beanClass 屬性替換掉 beanDefinition.setBeanClass(Good.class); } }
然后再次運(yùn)行測試類, 結(jié)果如下 :
可以看到,雖然表面上是通過 getBean("user") 來獲取 user 對象,但是實(shí)際調(diào)用的確實(shí) Good 對象的構(gòu)造方法,返回的是 good 對象. 但 Good 并沒有讓 Spring 掃描. 這個(gè)例子就展示了如何通過 BeanFactoryPostProcessor 機(jī)制插手容器的啟動
關(guān)于“IOC容器啟動和Bean實(shí)例化實(shí)例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。