新聞中心
1.DefaultListableBeanFactory

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿(mǎn)足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的懷安網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
要說(shuō) XmlBeanFactory 就不得不先說(shuō)它的父類(lèi) DefaultListableBeanFactory,因?yàn)?XmlBeanFactory 中的大部分功能實(shí)際上在 DefaultListableBeanFactory 中就已經(jīng)提供好了,XmlBeanFactory 只是對(duì) IO 流的讀取做了一些定制而已。
DefaultListableBeanFactory 是一個(gè)完整的、功能成熟的 IoC 容器,如果你的需求很簡(jiǎn)單,甚至可以直接使用 DefaultListableBeanFactory,如果你的需求比較復(fù)雜,那么通過(guò)擴(kuò)展 DefaultListableBeanFactory 的功能也可以達(dá)到,可以說(shuō) DefaultListableBeanFactory 是整個(gè) Spring IoC 容器的始祖。
我們先來(lái)看一下 DefaultListableBeanFactory 的繼承關(guān)系:
從這張類(lèi)的關(guān)系圖中可以看出,DefaultListableBeanFactory 實(shí)際上也是一個(gè)集大成者。在 Spring 中,針對(duì) Bean 的不同操作都有不同的接口進(jìn)行規(guī)范,每個(gè)接口都有自己對(duì)應(yīng)的實(shí)現(xiàn),最終在 DefaultListableBeanFactory 中將所有的實(shí)現(xiàn)匯聚到一起。從這張類(lèi)的繼承關(guān)系圖中我們大概就能感受到 Spring 中關(guān)于類(lèi)的設(shè)計(jì)是多么厲害,代碼耦合度非常低。
這些類(lèi),在本系列后面的介紹中,大部分都會(huì)涉及到,現(xiàn)在我先大概介紹一下每個(gè)類(lèi)的作用,大家先混個(gè)臉熟:
- BeanFactory:這個(gè)接口看名字就知道是一個(gè) Bean 的工廠(chǎng),BeanFactory 接口定義了各種獲取 Bean 的方法、判斷 Bean 是否存在、判斷 Bean 是否單例等針對(duì) Bean 的基礎(chǔ)方法。
- ListableBeanFactory:這個(gè)接口繼承自 BeanFactory,在 BeanFactory 的基礎(chǔ)上,擴(kuò)展了 Bean 的查詢(xún)方法,例如根據(jù)類(lèi)型獲取 BeanNames、根據(jù)注解獲取 BeanNames、根據(jù) Bean 獲取注解等。
- AutowireCapableBeanFactory:該接口繼承自 BeanFactory,在 BeanFactory 的基礎(chǔ)上,提供了 Bean 的創(chuàng)建、配置、注入、銷(xiāo)毀等操作。有時(shí)候我們需要自己手動(dòng)注入 Bean 的時(shí)候,可以考慮通過(guò)實(shí)現(xiàn)該接口來(lái)完成。AutowireCapableBeanFactory 在 Spring Security 中有一個(gè)重要的應(yīng)用就是 ObjectPostProcessor,這個(gè)松哥將在 ??Spring Security 系列中和大家詳細(xì)介紹。
- HierarchicalBeanFactory:該接口繼承自 BeanFactory,并在 BeanFactory 基礎(chǔ)上添加了獲取 parent beanfactory 的方法。
- SingletonBeanRegistry:這個(gè)接口定義了對(duì)單例 Bean 的定義以及獲取方法。
- ConfigurableBeanFactory:這個(gè)接口主要定了針對(duì) BeanFactory 的各種配置以及銷(xiāo)毀的方法。
- ConfigurableListableBeanFactory:這是 BeanFactory 的配置清單,這里定義了忽略的類(lèi)型、接口,通過(guò) Bean 的名稱(chēng)獲取 BeanDefinition 、凍結(jié) BeanDefinition 等。
- AliasRegistry:這個(gè)接口定義了對(duì) alias 的注冊(cè)、移除、判斷以及查詢(xún)操作。
- SimpleAliasRegistry:這個(gè)類(lèi)實(shí)現(xiàn)了 AliasRegistry 接口并實(shí)現(xiàn)了它里邊的方法,SimpleAliasRegistry 使用 ConcurrentHashMap 做載體,實(shí)現(xiàn)了對(duì) alias 的注冊(cè)、移除判斷以及查詢(xún)操作。
- DefaultSingletonBeanRegistry:這個(gè)類(lèi)基于 Java 中的集合,對(duì) SingletonBeanRegistry 接口進(jìn)行了實(shí)現(xiàn)。
- FactoryBeanRegistrySupport:該類(lèi)繼承自 DefaultSingletonBeanRegistry,并在 DefaultSingletonBeanRegistry 的基礎(chǔ)上,增加了獲取 FactoryBean 類(lèi)型、移除 FactoryBean 緩存的方法等等操作。
- AbstractBeanFactory:實(shí)現(xiàn)了 ConfigurableBeanFactory 接口并繼承自 FactoryBeanRegistrySupport,在 AbstractBeanFactory 中對(duì) ConfigurableBeanFactory 中定義的方法進(jìn)行了實(shí)現(xiàn)。
- AbstractAutowireCapableBeanFactory:該類(lèi)繼承自 AbstractBeanFactory 并對(duì) AutowireCapableBeanFactory 接口中定義的方法進(jìn)行了落地實(shí)現(xiàn)。
- BeanDefinitionRegistry:這個(gè)接口繼承自 AliasRegistry 接口,并增加了一系列針對(duì) BeanDefinition 的注冊(cè)、移除、查詢(xún)、判斷等方法。
- 最后的 DefaultListableBeanFactory 自然就具備了上面所有的功能。
上面的內(nèi)容可能看的大家眼花繚亂,松哥這里通過(guò)幾個(gè)簡(jiǎn)單實(shí)際的例子,來(lái)帶大家使用一下 DefaultListableBeanFactory 的功能,可能大家的理解就比較清晰了。
DefaultListableBeanFactory 作為一個(gè)集大成者,提供了非常多的功能,我們一個(gè)一個(gè)來(lái)看。
2.代碼改造
首先文章中一開(kāi)始的三行代碼我們可以對(duì)其略加改造,因?yàn)槲覀円呀?jīng)說(shuō)了 XmlBeanFactory 中的大部分功能實(shí)際上在 DefaultListableBeanFactory 中就已經(jīng)提供好了,XmlBeanFactory 只是對(duì) IO 流的讀取做了一些定制而已,文件的讀取主要是通過(guò) XmlBeanDefinitionReader 來(lái)完成的(本系列前面文章已經(jīng)講過(guò)),我們可以對(duì)文章一開(kāi)始的三行代碼進(jìn)行改造,以便更好的體現(xiàn)“XmlBeanFactory 中的大部分功能實(shí)際上在 DefaultListableBeanFactory 中就已經(jīng)提供好了”:
- ClassPathResource res=new ClassPathResource("beans.xml");
- DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
- XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
- reader.loadBeanDefinitions(res);
- User user = factory.getBean(User.class);
- System.out.println("user = " + user);
使用前四行代碼代替 XmlBeanFactory,這樣 XmlBeanFactory 的功能是不是就很明確了?就是前四行代碼的功能。
3.動(dòng)態(tài)注冊(cè) Bean
動(dòng)態(tài)注冊(cè) Bean,這是 DefaultListableBeanFactory 的功能之一,不過(guò)準(zhǔn)確來(lái)說(shuō)應(yīng)該是動(dòng)態(tài)注冊(cè) BeanDefinition 。
我們先來(lái)看一個(gè)簡(jiǎn)單的例子:
- DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
- GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
- MutablePropertyValues pvs = new MutablePropertyValues();
- pvs.add("username", "javaboy");
- pvs.add("address", "www.javaboy.org");
- userBeanDefinition.setPropertyValues(pvs);
- userBeanDefinition.setBeanClass(User.class);
- defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
- User user = defaultListableBeanFactory.getBean(User.class);
- System.out.println("user = " + user);
首先我們自己手動(dòng)構(gòu)建一個(gè) DefaultListableBeanFactory 對(duì)象。當(dāng)然也可以使用前面的 XmlBeanFactory。
然后再手動(dòng)構(gòu)建一個(gè) GenericBeanDefinition。在前面的文章中,松哥和大家講過(guò),現(xiàn)在默認(rèn)使用的 BeanDefinition 就是 GenericBeanDefinition,所以這里我們自己也手動(dòng)構(gòu)建一個(gè) GenericBeanDefinition。有了 GenericBeanDefinition 之后,我們?cè)O(shè)置相關(guān)的類(lèi)和屬性。
接下來(lái)再將 userBeanDefinition 注冊(cè)到 defaultListableBeanFactory。注冊(cè)完成之后,我們就可以從 defaultListableBeanFactory 中獲取相應(yīng)的 Bean 了。
這里說(shuō)一句題外話(huà),希望大家在閱讀本系列每一篇文章的時(shí)候,能夠?qū)⒈鞠盗星昂笪恼侣?lián)系起來(lái)一起理解,這樣會(huì)有很多意料之外的收獲。例如上面的,我們既可以聲明一個(gè) DefaultListableBeanFactory,也可以聲明一個(gè) XmlBeanFactory,那你大概就能據(jù)此推斷出 XmlBeanFactory 的主要目的可能就是對(duì)資源文件進(jìn)行讀取和注冊(cè)。
那么到底是怎么注冊(cè)的呢?我們來(lái)看一下 defaultListableBeanFactory.registerBeanDefinition 方法的定義:
- @Override
- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
- throws BeanDefinitionStoreException {
- Assert.hasText(beanName, "Bean name must not be empty");
- Assert.notNull(beanDefinition, "BeanDefinition must not be null");
- if (beanDefinition instanceof AbstractBeanDefinition) {
- try {
- ((AbstractBeanDefinition) beanDefinition).validate();
- }
- catch (BeanDefinitionValidationException ex) {
- throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
- "Validation of bean definition failed", ex);
- }
- }
- BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
- if (existingDefinition != null) {
- if (!isAllowBeanDefinitionOverriding()) {
- throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
- }
- else if (existingDefinition.getRole() < beanDefinition.getRole()) {
- // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
- if (logger.isInfoEnabled()) {
- logger.info("Overriding user-defined bean definition for bean '" + beanName +
- "' with a framework-generated bean definition: replacing [" +
- existingDefinition + "] with [" + beanDefinition + "]");
- }
- }
- else if (!beanDefinition.equals(existingDefinition)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Overriding bean definition for bean '" + beanName +
- "' with a different definition: replacing [" + existingDefinition +
- "] with [" + beanDefinition + "]");
- }
- }
- else {
- if (logger.isTraceEnabled()) {
- logger.trace("Overriding bean definition for bean '" + beanName +
- "' with an equivalent definition: replacing [" + existingDefinition +
- "] with [" + beanDefinition + "]");
- }
- }
- this.beanDefinitionMap.put(beanName, beanDefinition);
- }
- else {
- if (hasBeanCreationStarted()) {
- // Cannot modify startup-time collection elements anymore (for stable iteration)
- synchronized (this.beanDefinitionMap) {
- this.beanDefinitionMap.put(beanName, beanDefinition);
- List
updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); - updatedDefinitions.addAll(this.beanDefinitionNames);
- updatedDefinitions.add(beanName);
- this.beanDefinitionNames = updatedDefinitions;
- removeManualSingletonName(beanName);
- }
- }
- else {
- // Still in startup registration phase
- this.beanDefinitionMap.put(beanName, beanDefinition);
- this.beanDefinitionNames.add(beanName);
- removeManualSingletonName(beanName);
- }
- this.frozenBeanDefinitionNames = null;
- }
- if (existingDefinition != null || containsSingleton(beanName)) {
- resetBeanDefinition(beanName);
- }
- else if (isConfigurationFrozen()) {
- clearByTypeCache();
- }
- }
registerBeanDefinition 方法是在 BeanDefinitionRegistry 接口中聲明的,DefaultListableBeanFactory 類(lèi)實(shí)現(xiàn)了 BeanDefinitionRegistry 接口,并實(shí)現(xiàn)了該方法,我們來(lái)看分析下該方法:
- 首先對(duì)傳入的 beanDefinition 對(duì)象進(jìn)行校驗(yàn),這也是注冊(cè)前的最后一次校驗(yàn),不過(guò)這個(gè)時(shí)候 BeanDefinition 對(duì)象已經(jīng)到手了,所以這個(gè)校驗(yàn)并非 XML 文件校驗(yàn),這里主要是對(duì) methodOverrides 的校驗(yàn)。
- 接下來(lái)會(huì)根據(jù) beanName 從 beanDefinitionMap 中獲取 BeanDefinition,看看當(dāng)前 Bean 是否已經(jīng)定義過(guò)了。beanDefinitionMap 是一個(gè) Map 集合,這個(gè)集合中 key 是 beanName,value 是 BeanDefinition 對(duì)象。
- 如果 BeanDefinition 已經(jīng)存在了,那么接下來(lái)會(huì)判斷是否允許 BeanDefinition 覆蓋,如果不允許,就直接拋出異常(不知道小伙伴們有沒(méi)有印象,在松哥前面的 OAuth2 系列教程中,經(jīng)常需要配置允許 BeanDefinition 的覆蓋,就是因?yàn)檫@個(gè)原因,公眾號(hào)【江南一點(diǎn)雨】后臺(tái)回復(fù) OAuth2 獲取該教程),如果允許 BeanDefinition 的覆蓋,那就向 beanDefinitionMap 中再次存一次值,覆蓋之前的值。
- 如果 BeanDefinition 不存在,那就直接注冊(cè)。直接注冊(cè)分兩種情況:項(xiàng)目已經(jīng)運(yùn)行了和項(xiàng)目還沒(méi)運(yùn)行。
- 如果項(xiàng)目已經(jīng)運(yùn)行,由于 beanDefinitionMap 是一個(gè)全局變量,可能存在并發(fā)問(wèn)題,所以要加鎖處理。否則就直接注冊(cè),所謂的注冊(cè)就是把對(duì)象存入 beanDefinitionMap 中,同時(shí)將 beanName 都存入 beanDefinitionNames 集合中。
這便是 registerBeanDefinition 方法的工作流程。
有小伙伴會(huì)說(shuō),這個(gè)方法從頭到尾都是 BeanDefinition,跟 Bean 有什么關(guān)系呢?
咋一看確實(shí)好像和 Bean 沒(méi)有直接關(guān)系。
其實(shí)這涉及到另外一個(gè)問(wèn)題,就是 Bean 的懶加載。這個(gè)時(shí)候先把 BeanDefinition 定義好,等到真正調(diào)用 Bean 的時(shí)候,才會(huì)去初始化 Bean。我們可以在 User 類(lèi)的構(gòu)造方法中打印日志看下,如下:
- public class User {
- private String username;
- private String address;
- public User() {
- System.out.println("--------user init--------");
- }
- @Override
- public String toString() {
- return "User{" +
- "username='" + username + '\'' +
- ", address='" + address + '\'' +
- '}';
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username) {
- this.username = username;
- }
- public String getAddress() {
- return address;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- }
從下圖可以看到,當(dāng) BeanDefinition 注冊(cè)完成后,User 并沒(méi)有初始化,等到 getBean 方法被調(diào)用的時(shí)候,User 才初始化了。
需要注意的是,我們?nèi)粘i_(kāi)發(fā)中使用的 ApplicationContext 并非懶加載,這個(gè)在松哥的 Spring 入門(mén)視頻中可以看到效果【??https://www.bilibili.com/video/BV1Wv41167TU】,具體原理松哥將在本系列后面的文章中和大家分享。
那么如果不想懶加載該怎么辦呢?當(dāng)然有辦法。
4.提前注冊(cè) Bean
在 DefaultListableBeanFactory 中還有一個(gè) preInstantiateSingletons 方法可以提前注冊(cè) Bean,該方法是在 ConfigurableListableBeanFactory 接口中聲明的,DefaultListableBeanFactory 類(lèi)實(shí)現(xiàn)了 ConfigurableListableBeanFactory 接口并實(shí)現(xiàn)了接口中的方法:
- @Override
- public void preInstantiateSingletons() throws BeansException {
- if (logger.isTraceEnabled()) {
- logger.trace("Pre-instantiating singletons in " + this);
- }
- // Iterate over a copy to allow for init methods which in turn register new bean definitions.
- // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
- List
beanNames = new ArrayList<>(this.beanDefinitionNames); - // Trigger initialization of all non-lazy singleton beans...
- for (String beanName : beanNames) {
- RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
- if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
- if (isFactoryBean(beanName)) {
- Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
- if (bean instanceof FactoryBean) {
- final FactoryBean> factory = (FactoryBean>) bean;
- boolean isEagerInit;
- if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
- isEagerInit = AccessController.doPrivileged((PrivilegedAction
) - ((SmartFactoryBean>) factory)::isEagerInit,
- getAccessControlContext());
- }
- else {
- isEagerInit = (factory instanceof SmartFactoryBean &&
- ((SmartFactoryBean>) factory).isEagerInit());
- }
- if (isEagerInit) {
- getBean(beanName);
- }
- }
- }
- else {
- getBean(beanName);
- }
- }
- }
- // Trigger post-initialization callback for all applicable beans...
- for (String beanName : beanNames) {
- Object singletonInstance = getSingleton(beanName);
- if (singletonInstance instanceof SmartInitializingSingleton) {
- final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
- if (System.getSecurityManager() != null) {
- AccessController.doPrivileged((PrivilegedAction
- smartSingleton.afterSingletonsInstantiated();
- return null;
- }, getAccessControlContext());
- }
- else {
- smartSingleton.afterSingletonsInstantiated();
- }
- }
- }
- }
preInstantiateSingletons 方法的整體邏輯比較簡(jiǎn)單,就是遍歷 beanNames,對(duì)符合條件的 Bean 進(jìn)行實(shí)例化,而且大家注意,這里所謂的提前初始化其實(shí)就是在我們調(diào)用 getBean 方法之前,它自己先調(diào)用了一下 getBean。
我們可以在案例中手動(dòng)調(diào)用該方法:
- DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
- GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
- MutablePropertyValues pvs = new MutablePropertyValues();
- pvs.add("username", "javaboy");
- pvs.add("address", "www.javaboy.org");
- userBeanDefinition.setPropertyValues(pvs);
- userBeanDefinition.setBeanClass(User.class);
- defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
- defaultListableBeanFactory.preInstantiateSingletons();
- User user = defaultListableBeanFactory.getBean(User.class);
- System.out.println("user = " + user);
此時(shí)在調(diào)用 getBean 方法之前,User 就已經(jīng)初始化了,如下圖:
5.getBean
DefaultListableBeanFactory 中另外一個(gè)重量級(jí)方法就是 getBean 了。不過(guò) getBean 方法的真正實(shí)現(xiàn)是在 DefaultListableBeanFactory 的父類(lèi) AbstractBeanFactory 中,具體的實(shí)現(xiàn)方法是 doGetBean,本來(lái)想和大家子在這里聊一聊這個(gè)問(wèn)題,但是發(fā)現(xiàn)這是一個(gè)非常龐大的問(wèn)題,BeanFactory 和 FactoryBean 都還沒(méi)和大家分享,所以這個(gè)話(huà)題我們還是暫且押后,一個(gè)點(diǎn)一個(gè)點(diǎn)來(lái)。
6.小結(jié)
好啦,今天就先說(shuō)這么多,每篇源碼我都盡量配置套一些小案例來(lái)演示,這樣避免大家看的太枯燥了,我們下周繼續(xù)~
本文轉(zhuǎn)載自微信公眾號(hào)「江南一點(diǎn)雨 」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系江南一點(diǎn)雨公眾號(hào)。
網(wǎng)站欄目:聊聊容器的始祖DefaultListableBeanFactory
標(biāo)題來(lái)源:http://fisionsoft.com.cn/article/dhjhpgj.html


咨詢(xún)
建站咨詢(xún)
