目錄 一、前言
二、目標(biāo)
三、設(shè)計(jì)
四、實(shí)現(xiàn)
1. 工程結(jié)構(gòu)
2. 定義 BeanFactoryPostProcessor
3. 定義 BeanPostProcessor
4. 定義上下文接口
5. 應(yīng)用上下文抽象類實(shí)現(xiàn)
6. 獲取Bean工廠和加載資源
7. 上下文中對配置信息的加載
8. 應(yīng)用上下文實(shí)現(xiàn)類(ClassPathXmlApplicationContext)
9. 在Bean創(chuàng)建時完成前置和后置處理
五、測試
六、總結(jié)
七、系列推薦
一、前言 你這代碼,可不能寫死了呀!
依照項(xiàng)目落地經(jīng)驗(yàn)來看,我們在承接緊急的產(chǎn)品需求時候,通常會選擇在原有同類項(xiàng)目中進(jìn)行擴(kuò)展,如果沒有相關(guān)類型項(xiàng)目的儲備,也可能會選擇臨時搭建出一個工程來實(shí)現(xiàn)產(chǎn)品的需求。但這個時候就會遇到非?,F(xiàn)實(shí)的問題,選擇完整的設(shè)計(jì)和開發(fā)就可能滿足不了上線時間,臨時拼湊式的完成需求又可能不具備上線后響應(yīng)產(chǎn)品的臨時調(diào)整。
上線后的調(diào)整有哪些呢?項(xiàng)目剛一上線,運(yùn)營了還不到半天,老板發(fā)現(xiàn)自己的配置的活動好像金額配置的太小了,用戶都不來,割不到韭菜呀。趕緊半夜聯(lián)系產(chǎn)品,來來來,你給我這改改,那修修,把人均優(yōu)惠1萬元放大大的,把可能兩字縮小放在后面。再把優(yōu)惠的獎金池配置從10元調(diào)整11元,快快快,趕緊修改,你修改了咱們能賺1個億?。。?/code>
好家伙,項(xiàng)目是臨時開發(fā)堆出來的,沒有后臺系統(tǒng)、沒有配置中心、沒有模塊拆分,老板一句句改改改,產(chǎn)品來傳達(dá)催促,最后背鍋的可就是研發(fā)了。你這不能寫死,這優(yōu)惠配置得抽出來,這文案也后臺下發(fā)吧,這接口入?yún)⒁矊懰懒?,再寫一個新接口吧! 一頓操作猛如虎,研發(fā)搬磚修接口,運(yùn)營折騰好幾宿,最后PV150!
無論業(yè)務(wù)、產(chǎn)品、運(yùn)營如何,但就研發(fā)自身來講,盡可能的要不避免臨時堆出一個服務(wù)來,尤其是在團(tuán)隊(duì)建設(shè)初期或者運(yùn)營思路經(jīng)常調(diào)整的情況下,更要注重設(shè)計(jì)細(xì)節(jié)和實(shí)現(xiàn)方案。哪怕去報(bào)風(fēng)險(xiǎn)延期,也不要讓自己背上一個明知是爛坑還要接的活。
而本章節(jié)說到不把代碼寫死,就是因?yàn)槲覀冃枰^續(xù)在手寫 Spring 框架中繼續(xù)擴(kuò)展新的功能,如一個Bean的定義和實(shí)例化的過程前后,是否可以滿足我們進(jìn)行自定義擴(kuò)展,對Bean對象執(zhí)行一些修改、增強(qiáng)、記錄等操作呢?這個過程基本就是你在使用 Spring 容器框架時候做的一些中間件擴(kuò)展開發(fā)。
二、目標(biāo) 如果你在自己的實(shí)際工作中開發(fā)過基于 Spring 的技術(shù)組件,或者學(xué)習(xí)過關(guān)于 SpringBoot 中間件設(shè)計(jì)和開發(fā) 等內(nèi)容。那么你一定會繼承或者實(shí)現(xiàn)了 Spring 對外暴露的類或接口,在接口的實(shí)現(xiàn)中獲取了 BeanFactory 以及 Bean 對象的獲取等內(nèi)容,并對這些內(nèi)容做一些操作,例如:修改 Bean 的信息,添加日志打印、處理數(shù)據(jù)庫路由對數(shù)據(jù)源的切換、給 RPC 服務(wù)連接注冊中心等。
在對容器中 Bean 的實(shí)例化過程添加擴(kuò)展機(jī)制的同時,還需要把目前關(guān)于 Spring.xml 初始化和加載策略進(jìn)行優(yōu)化,因?yàn)槲覀儾惶赡茏屆嫦?Spring 本身開發(fā)的 DefaultListableBeanFactory 服務(wù),直接給予用戶使用。修改點(diǎn)如下:
DefaultListableBeanFactory、XmlBeanDefinitionReader,是我們在目前 Spring 框架中對于服務(wù)功能測試的使用方式,它能很好的體現(xiàn)出 Spring 是如何對 xml 加載以及注冊Bean對象的操作過程,但這種方式是面向 Spring 本身的,還不具備一定的擴(kuò)展性。 就像我們現(xiàn)在需要提供出一個可以在 Bean 初始化過程中,完成對 Bean 對象的擴(kuò)展時,就很難做到自動化處理。所以我們要把 Bean 對象擴(kuò)展機(jī)制功能和對 Spring 框架上下文的包裝融合起來,對外提供完整的服務(wù)。 三、設(shè)計(jì) 為了能滿足于在 Bean 對象從注冊到實(shí)例化的過程中執(zhí)行用戶的自定義操作,就需要在 Bean 的定義和初始化過程中插入接口類,這個接口再有外部去實(shí)現(xiàn)自己需要的服務(wù)。那么在結(jié)合對 Spring 框架上下文的處理能力,就可以滿足我們的目標(biāo)需求了。整體設(shè)計(jì)結(jié)構(gòu)如下圖:
滿足于對 Bean 對象擴(kuò)展的兩個接口,其實(shí)也是 Spring 框架中非常具有重量級的兩個接口:BeanFactoryPostProcess 和 BeanPostProcessor,也幾乎是大家在使用 Spring 框架額外新增開發(fā)自己組建需求的兩個必備接口。 BeanFactoryPostProcessor,是由 Spring 框架組建提供的容器擴(kuò)展機(jī)制,允許在 Bean 對象注冊后但未實(shí)例化之前,對 Bean 的定義信息 BeanDefinition 執(zhí)行修改操作。 BeanPostProcessor,也是 Spring 提供的擴(kuò)展機(jī)制,不過 BeanPostProcessor 是在 Bean 對象實(shí)例化之后修改 Bean 對象,也可以替換 Bean 對象。這部分與后面要實(shí)現(xiàn)的 AOP 有著密切的關(guān)系。 同時如果只是添加這兩個接口,不做任何包裝,那么對于使用者來說還是非常麻煩的。我們希望于開發(fā) Spring 的上下文操作類,把相應(yīng)的 XML 加載 、注冊、實(shí)例化以及新增的修改和擴(kuò)展都融合進(jìn)去,讓 Spring 可以自動掃描到我們的新增服務(wù),便于用戶使用。 四、實(shí)現(xiàn) 1. 工程結(jié)構(gòu)small-spring-step-06 └── src ├── main │ └── java │ └── cn.bugstack.springframework │ ├── beans │ │ ├── factory │ │ │ ├── factory │ │ │ │ ├── AutowireCapableBeanFactory.java │ │ │ │ ├── BeanDefinition.java │ │ │ │ ├── BeanFactoryPostProcessor.java │ │ │ │ ├── BeanPostProcessor.java │ │ │ │ ├── BeanReference.java │ │ │ │ ├── ConfigurableBeanFactory.java │ │ │ │ └── SingletonBeanRegistry.java │ │ │ ├── support │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java │ │ │ │ ├── AbstractBeanDefinitionReader.java │ │ │ │ ├── AbstractBeanFactory.java │ │ │ │ ├── BeanDefinitionReader.java │ │ │ │ ├── BeanDefinitionRegistry.java │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java │ │ │ │ ├── DefaultListableBeanFactory.java │ │ │ │ ├── DefaultSingletonBeanRegistry.java │ │ │ │ ├── InstantiationStrategy.java │ │ │ │ └── SimpleInstantiationStrategy.java │ │ │ ├── support │ │ │ │ └── XmlBeanDefinitionReader.java │ │ │ ├── BeanFactory.java │ │ │ ├── ConfigurableListableBeanFactory.java │ │ │ ├── HierarchicalBeanFactory.java │ │ │ └── ListableBeanFactory.java │ │ ├── BeansException.java │ │ ├── PropertyValue.java │ │ └── PropertyValues.java │ ├── context │ │ ├── support │ │ │ ├── AbstractApplicationContext.java │ │ │ ├── AbstractRefreshableApplicationContext.java │ │ │ ├── AbstractXmlApplicationContext.java │ │ │ └── ClassPathXmlApplicationContext.java │ │ ├── ApplicationContext.java │ │ └── ConfigurableApplicationContext.java │ ├── core.io │ │ ├── ClassPathResource.java │ │ ├── DefaultResourceLoader.java │ │ ├── FileSystemResource.java │ │ ├── Resource.java │ │ ├── ResourceLoader.java │ │ └── UrlResource.java │ └── utils │ └── ClassUtils.java └── test └── java └── cn.bugstack.springframework.test ├── bean │ ├── UserDao.java │ └── UserService.java ├── common │ ├── MyBeanFactoryPostProcessor.java │ └── MyBeanPostProcessor.java └── ApiTest.java工程源碼 :公眾號「bugstack蟲洞?!梗貜?fù):Spring 專欄,獲取完整源碼
Spring 應(yīng)用上下文和對Bean對象擴(kuò)展機(jī)制的類關(guān)系,如圖 7-3
圖 7-3 在整個類圖中主要體現(xiàn)出來的是關(guān)于 Spring 應(yīng)用上下文以及對 Bean 對象擴(kuò)展機(jī)制的實(shí)現(xiàn)。 以繼承了 ListableBeanFactory 接口的 ApplicationContext 接口開始,擴(kuò)展出一系列應(yīng)用上下文的抽象實(shí)現(xiàn)類,并最終完成 ClassPathXmlApplicationContext 類的實(shí)現(xiàn)。而這個類就是最后交給用戶使用的類。 同時在實(shí)現(xiàn)應(yīng)用上下文的過程中,通過定義接口:BeanFactoryPostProcessor、BeanPostProcessor 兩個接口,把關(guān)于對 Bean 的擴(kuò)展機(jī)制串聯(lián)進(jìn)去了。 2. 定義 BeanFactoryPostProcessorcn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor
public interface BeanFactoryPostProcessor { /** * 在所有的 BeanDefinition 加載完成后,實(shí)例化 Bean 對象之前,提供修改 BeanDefinition 屬性的機(jī)制 * * @param beanFactory * @throws BeansException */ void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException ; }在 Spring 源碼中有這樣一段描述 Allows for custom modification of an application context's bean definitions,adapting the bean property values of the context's underlying bean factory. 其實(shí)也就是說這個接口是滿足于在所有的 BeanDefinition 加載完成后,實(shí)例化 Bean 對象之前,提供修改 BeanDefinition 屬性的機(jī)制。 3. 定義 BeanPostProcessorcn.bugstack.springframework.beans.factory.config.BeanPostProcessor
public interface BeanPostProcessor { /** * 在 Bean 對象執(zhí)行初始化方法之前,執(zhí)行此方法 * * @param bean * @param beanName * @return * @throws BeansException */ Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException ; /** * 在 Bean 對象執(zhí)行初始化方法之后,執(zhí)行此方法 * * @param bean * @param beanName * @return * @throws BeansException */ Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException ; }在 Spring 源碼中有這樣一段描述 Factory hook that allows for custom modification of new bean instances,e.g. checking for marker interfaces or wrapping them with proxies.也就是提供了修改新實(shí)例化 Bean 對象的擴(kuò)展點(diǎn)。 另外此接口提供了兩個方法:postProcessBeforeInitialization 用于在 Bean 對象執(zhí)行初始化方法之前,執(zhí)行此方法、postProcessAfterInitialization用于在 Bean 對象執(zhí)行初始化方法之后,執(zhí)行此方法。 4. 定義上下文接口cn.bugstack.springframework.context.ApplicationContext
public interface ApplicationContext extends ListableBeanFactory { }context 是本次實(shí)現(xiàn)應(yīng)用上下文功能新增的服務(wù)包 ApplicationContext,繼承于 ListableBeanFactory,也就繼承了關(guān)于 BeanFactory 方法,比如一些 getBean 的方法。另外 ApplicationContext 本身是 Central 接口,但目前還不需要添加一些獲取ID和父類上下文,所以暫時沒有接口方法的定義。 cn.bugstack.springframework.context.ConfigurableApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext { /** * 刷新容器 * * @throws BeansException */ void refresh () throws BeansException ; }ConfigurableApplicationContext 繼承自 ApplicationContext,并提供了 refresh 這個核心方法。如果你有看過一些 Spring 源碼,那么一定會看到這個方法。 接下來也是需要在上下文的實(shí)現(xiàn)中完成刷新容器的操作過程。 5. 應(yīng)用上下文抽象類實(shí)現(xiàn)cn.bugstack.springframework.context.support.AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { @Override public void refresh () throws BeansException { // 1. 創(chuàng)建 BeanFactory,并加載 BeanDefinition refreshBeanFactory(); // 2. 獲取 BeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // 3. 在 Bean 實(shí)例化之前,執(zhí)行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.) invokeBeanFactoryPostProcessors(beanFactory); // 4. BeanPostProcessor 需要提前于其他 Bean 對象實(shí)例化之前執(zhí)行注冊操作 registerBeanPostProcessors(beanFactory); // 5. 提前實(shí)例化單例Bean對象 beanFactory.preInstantiateSingletons(); } protected abstract void refreshBeanFactory () throws BeansException ; protected abstract ConfigurableListableBeanFactory getBeanFactory () ; private void invokeBeanFactoryPostProcessors (ConfigurableListableBeanFactory beanFactory) { Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class ) ; for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) { beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); } } private void registerBeanPostProcessors (ConfigurableListableBeanFactory beanFactory) { Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class ) ; for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) { beanFactory.addBeanPostProcessor(beanPostProcessor); } } //... getBean、getBeansOfType、getBeanDefinitionNames 方法 }AbstractApplicationContext 繼承 DefaultResourceLoader 是為了處理 spring.xml 配置資源的加載。 之后是在 refresh() 定義實(shí)現(xiàn)過程,包括: BeanPostProcessor 需要提前于其他 Bean 對象實(shí)例化之前執(zhí)行注冊操作 在 Bean 實(shí)例化之前,執(zhí)行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.) 創(chuàng)建 BeanFactory,并加載 BeanDefinition 另外把定義出來的抽象方法,refreshBeanFactory()、getBeanFactory() 由后面的繼承此抽象類的其他抽象類實(shí)現(xiàn)。 6. 獲取Bean工廠和加載資源cn.bugstack.springframework.context.support.AbstractRefreshableApplicationContext
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { private DefaultListableBeanFactory beanFactory; @Override protected void refreshBeanFactory () throws BeansException { DefaultListableBeanFactory beanFactory = createBeanFactory(); loadBeanDefinitions(beanFactory); this .beanFactory = beanFactory; } private DefaultListableBeanFactory createBeanFactory () { return new DefaultListableBeanFactory(); } protected abstract void loadBeanDefinitions (DefaultListableBeanFactory beanFactory) ; @Override protected ConfigurableListableBeanFactory getBeanFactory () { return beanFactory; } }在 refreshBeanFactory() 中主要是獲取了 DefaultListableBeanFactory 的實(shí)例化以及對資源配置的加載操作 loadBeanDefinitions(beanFactory),在加載完成后即可完成對 spring.xml 配置文件中 Bean 對象的定義和注冊,同時也包括實(shí)現(xiàn)了接口 BeanFactoryPostProcessor、BeanPostProcessor 的配置 Bean 信息。 但此時資源加載還只是定義了一個抽象類方法 loadBeanDefinitions(DefaultListableBeanFactory beanFactory),繼續(xù)由其他抽象類繼承實(shí)現(xiàn)。 7. 上下文中對配置信息的加載cn.bugstack.springframework.context.support.AbstractXmlApplicationContext
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext { @Override protected void loadBeanDefinitions (DefaultListableBeanFactory beanFactory) { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this ); String[] configLocations = getConfigLocations(); if (null != configLocations){ beanDefinitionReader.loadBeanDefinitions(configLocations); } } protected abstract String[] getConfigLocations(); }在 AbstractXmlApplicationContext 抽象類的 loadBeanDefinitions 方法實(shí)現(xiàn)中,使用 XmlBeanDefinitionReader 類,處理了關(guān)于 XML 文件配置信息的操作。 同時這里又留下了一個抽象類方法,getConfigLocations(),此方法是為了從入口上下文類,拿到配置信息的地址描述。 8. 應(yīng)用上下文實(shí)現(xiàn)類(ClassPathXmlApplicationContext)cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private String[] configLocations; public ClassPathXmlApplicationContext () { } /** * 從 XML 中加載 BeanDefinition,并刷新上下文 * * @param configLocations * @throws BeansException */ public ClassPathXmlApplicationContext (String configLocations) throws BeansException { this (new String[]{configLocations}); } /** * 從 XML 中加載 BeanDefinition,并刷新上下文 * @param configLocations * @throws BeansException */ public ClassPathXmlApplicationContext (String[] configLocations) throws BeansException { this .configLocations = configLocations; refresh(); } @Override protected String[] getConfigLocations() { return configLocations; } }ClassPathXmlApplicationContext,是具體對外給用戶提供的應(yīng)用上下文方法。 在繼承了 AbstractXmlApplicationContext 以及層層抽象類的功能分離實(shí)現(xiàn)后,在此類 ClassPathXmlApplicationContext 的實(shí)現(xiàn)中就簡單多了,主要是對繼承抽象類中方法的調(diào)用和提供了配置文件地址信息。 9. 在Bean創(chuàng)建時完成前置和后置處理cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); @Override protected Object createBean (String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { Object bean = null ; try { bean = createBeanInstance(beanDefinition, beanName, args); // 給 Bean 填充屬性 applyPropertyValues(beanName, bean, beanDefinition); // 執(zhí)行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置處理方法 bean = initializeBean(beanName, bean, beanDefinition); } catch (Exception e) { throw new BeansException("Instantiation of bean failed" , e); } addSingleton(beanName, bean); return bean; } public InstantiationStrategy getInstantiationStrategy () { return instantiationStrategy; } public void setInstantiationStrategy (InstantiationStrategy instantiationStrategy) { this .instantiationStrategy = instantiationStrategy; } private Object initializeBean (String beanName, Object bean, BeanDefinition beanDefinition) { // 1. 執(zhí)行 BeanPostProcessor Before 處理 Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName); // 待完成內(nèi)容:invokeInitMethods(beanName, wrappedBean, beanDefinition); invokeInitMethods(beanName, wrappedBean, beanDefinition); // 2. 執(zhí)行 BeanPostProcessor After 處理 wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName); return wrappedBean; } private void invokeInitMethods (String beanName, Object wrappedBean, BeanDefinition beanDefinition) { } @Override public Object applyBeanPostProcessorsBeforeInitialization (Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (null == current) return result; result = current; } return result; } @Override public Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (null == current) return result; result = current; } return result; } }實(shí)現(xiàn) BeanPostProcessor 接口后,會涉及到兩個接口方法,postProcessBeforeInitialization、postProcessAfterInitialization,分別作用于 Bean 對象執(zhí)行初始化前后的額外處理。 也就是需要在創(chuàng)建 Bean 對象時,在 createBean 方法中添加 initializeBean(beanName, bean, beanDefinition); 操作。而這個操作主要主要是對于方法 applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 的使用。 另外需要提一下,applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 兩個方法是在接口類 AutowireCapableBeanFactory 中新增加的。 五、測試 1. 事先準(zhǔn)備cn.bugstack.springframework.test.bean.UserDao
public class UserDao { private static Map<String, String> hashMap = new HashMap<>(); static { hashMap.put("10001" , "小傅哥" ); hashMap.put("10002" , "八杯水" ); hashMap.put("10003" , "阿毛" ); } public String queryUserName (String uId) { return hashMap.get(uId); } }cn.bugstack.springframework.test.bean.UserService
public class UserService { private String uId; private String company; private String location; private UserDao userDao; public void queryUserInfo () { return userDao.queryUserName(uId); } // ...get/set }Dao、Service,是我們平常開發(fā)經(jīng)常使用的場景。在 UserService 中注入 UserDao,這樣就能體現(xiàn)出Bean屬性的依賴了。 另外這里新增加了 company、location,兩個屬性信息,便于測試 BeanPostProcessor、BeanFactoryPostProcessor 兩個接口對 Bean 屬性信息擴(kuò)展的作用。 2. 實(shí)現(xiàn) BeanPostProcessor 和 BeanFactoryPostProcessorcn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService" ); PropertyValues propertyValues = beanDefinition.getPropertyValues(); propertyValues.addPropertyValue(new PropertyValue("company" , "改為:字節(jié)跳動" )); } }cn.bugstack.springframework.test.common.MyBeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { if ("userService" .equals(beanName)) { UserService userService = (UserService) bean; userService.setLocation("改為:北京" ); } return bean; } @Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { return bean; } }如果你在 Spring 中做過一些組件的開發(fā)那么一定非常熟悉這兩個類,本文的測試也是實(shí)現(xiàn)了這兩個類,對實(shí)例化過程中的 Bean 對象做一些操作。 3. 配置文件基礎(chǔ)配置,無BeanFactoryPostProcessor、BeanPostProcessor,實(shí)現(xiàn)類
<?xml version="1.0" encoding="UTF-8"?> <beans > <bean id ="userDao" class ="cn.bugstack.springframework.test.bean.UserDao" /> <bean id ="userService" class ="cn.bugstack.springframework.test.bean.UserService" > <property name ="uId" value ="10001" /> <property name ="company" value ="騰訊" /> <property name ="location" value ="深圳" /> <property name ="userDao" ref ="userDao" /> </bean > </beans > 增強(qiáng)配置,有BeanFactoryPostProcessor、BeanPostProcessor,實(shí)現(xiàn)類
<?xml version="1.0" encoding="UTF-8"?> <beans > <bean id ="userDao" class ="cn.bugstack.springframework.test.bean.UserDao" /> <bean id ="userService" class ="cn.bugstack.springframework.test.bean.UserService" > <property name ="uId" value ="10001" /> <property name ="company" value ="騰訊" /> <property name ="location" value ="深圳" /> <property name ="userDao" ref ="userDao" /> </bean > <bean class ="cn.bugstack.springframework.test.common.MyBeanPostProcessor" /> <bean class ="cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor" /> </beans > 這里提供了兩個配置文件,一個是不包含BeanFactoryPostProcessor、BeanPostProcessor,另外一個是包含的。之所以這樣配置主要對照驗(yàn)證,在運(yùn)用 Spring 新增加的應(yīng)用上下文和不使用的時候,都是怎么操作的。 4. 不用應(yīng)用上下文@Test public void test_BeanFactoryPostProcessorAndBeanPostProcessor () { // 1.初始化 BeanFactory DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 2. 讀取配置文件&注冊Bean XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("classpath:spring.xml" ); // 3. BeanDefinition 加載完成 & Bean實(shí)例化之前,修改 BeanDefinition 的屬性值 MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor(); beanFactoryPostProcessor.postProcessBeanFactory(beanFactory); // 4. Bean實(shí)例化之后,修改 Bean 屬性信息 MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor(); beanFactory.addBeanPostProcessor(beanPostProcessor); // 5. 獲取Bean對象調(diào)用方法 UserService userService = beanFactory.getBean("userService" , UserService.class ) ; String result = userService.queryUserInfo(); System.out.println("測試結(jié)果:" + result); }DefaultListableBeanFactory 創(chuàng)建 beanFactory 并使用 XmlBeanDefinitionReader 加載配置文件的方式,還是比較熟悉的。 接下來就是對 MyBeanFactoryPostProcessor 和 MyBeanPostProcessor 的處理,一個是在BeanDefinition 加載完成 & Bean實(shí)例化之前,修改 BeanDefinition 的屬性值,另外一個是在Bean實(shí)例化之后,修改 Bean 屬性信息。 測試結(jié)果
測試結(jié)果:小傅哥,改為:字節(jié)跳動,改為:北京 Process finished with exit code 0 通過測試結(jié)果可以看到,我們配置的屬性信息已經(jīng)與 spring.xml 配置文件中不一樣了。 5. 使用應(yīng)用上下文@Test public void test_xml () { // 1.初始化 BeanFactory ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml" ); // 2. 獲取Bean對象調(diào)用方法 UserService userService = applicationContext.getBean("userService" , UserService.class ) ; String result = userService.queryUserInfo(); System.out.println("測試結(jié)果:" + result); }另外使用新增加的 ClassPathXmlApplicationContext 應(yīng)用上下文類,再操作起來就方便多了,這才是面向用戶使用的類 ,在這里可以一步把配置文件交給 ClassPathXmlApplicationContext,也不需要管理一些自定義實(shí)現(xiàn)的 Spring 接口的類。 測試結(jié)果
測試結(jié)果:小傅哥,改為:字節(jié)跳動,改為:北京 Process finished with exit code 0 這與不用應(yīng)用上下文的測試結(jié)果是一樣,不過現(xiàn)在的方式更加方便了。 六、總結(jié) 本文主要新增了 Spring 框架中兩個非常重要的接口 BeanFactoryPostProcess、BeanPostProcessor 同時還添加了關(guān)于應(yīng)用上下文的實(shí)現(xiàn),ApplicationContext 接口的定義是繼承 BeanFactory 外新增加功能的接口,它可以滿足于自動識別、資源加載、容器事件、監(jiān)聽器等功能,同時例如一些國際化支持、單例Bean自動初始化等,也是可以在這個類里實(shí)現(xiàn)和擴(kuò)充的。 通過本文的實(shí)現(xiàn)一定會非常了解 BeanFactoryPostProcess、BeanPostProcessor,以后再做一些關(guān)于 Spring 中間件的開發(fā)時,如果需要用到 Bean 對象的獲取以及修改一些屬性信息,那么就可以使用這兩個接口了。同時 BeanPostProcessor 也是實(shí)現(xiàn) AOP 切面技術(shù)的關(guān)鍵所在。 有人問:面試問那么多,可是工作又用不到,是嘎哈么呢?,嘎哈么,那你說你開車上橋的時候,會每次都撞兩邊的護(hù)欄嗎,不撞是吧,那不要修了哇,直接就鋪一個平板,還省材料了。其實(shí)核心技術(shù)的原理學(xué)習(xí),是更有助于你完成更復(fù)雜的架構(gòu)設(shè)計(jì),當(dāng)你的知識能更全面覆蓋所承接的需求時,也就能更好的做出合理的架構(gòu)和落地。 七、系列推薦