天天看點

我想挑戰下我的軟肋,動手實作個Spring應用上下文!

我想挑戰下我的軟肋,動手實作個Spring應用上下文!

作者:小傅哥

部落格:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收獲!😄

目錄

  • [x] 第 1 章:開篇介紹,我要帶你撸 Spring 啦!
  • [x] 第 2 章:小試牛刀,實作一個簡單的Bean容器
  • [x] 第 3 章:初顯身手,運用設計模式,實作 Bean 的定義、注冊、擷取
  • [x] 第 4 章:嶄露頭角,基于Cglib實作含構造函數的類執行個體化政策
  • [x] 第 5 章:一鳴驚人,為Bean對象注入屬性和依賴Bean的功能實作
  • [x] 第 6 章:氣吞山河,設計與實作資源加載器,從Spring.xml解析和注冊Bean對象
  • [x] 第 7 章:所向披靡,實作應用上下文,自動識别、資源加載、擴充機制
  • [ ] 第 8 章:待歸檔...

一、前言

你這代碼,可不能寫死了呀!

依照項目落地經驗來看,我們在承接緊急的産品需求時候,通常會選擇在原有同類項目中進行擴充,如果沒有相關類型項目的儲備,也可能會選擇臨時搭建出一個工程來實作産品的需求。但這個時候就會遇到非常現實的問題,選擇完整的設計和開發就可能滿足不了上線時間,臨時拼湊式的完成需求又可能不具備上線後響應産品的臨時調整。

上線後的調整有哪些呢?項目剛一上線,營運了還不到半天,老闆發現自己的配置的活動好像金額配置的太小了,使用者都不來,割不到韭菜呀。

趕緊半夜聯系産品,來來來,你給我這改改,那修修,把人均優惠1萬元放大大的,把可能兩字縮小放在後面。再把優惠的獎金池配置從10元調整11元,快快快,趕緊修改,你修改了咱們能賺1個億!!!

好家夥,項目是臨時開發堆出來的,沒有背景系統、沒有配置中心、沒有子產品拆分,老闆一句句改改改,産品來傳達催促,最後背鍋的可就是研發了。你這不能寫死,這優惠配置得抽出來,這文案也背景下發吧,這接口入參也寫死了,再寫一個新接口吧! 一頓操作猛如虎,研發搬磚修接口,營運折騰好幾宿,最後PV150!

無論業務、産品、營運如何,但就研發自身來講,盡可能的要不避免臨時堆出一個服務來,尤其是在團隊建設初期或者營運思路經常調整的情況下,更要注重設計細節和實作方案。哪怕去報風險延期,也不要讓自己背上一個明知是爛坑還要接的活。

而本章節說到

不把代碼寫死

,就是因為我們需要繼續在手寫 Spring 架構中繼續擴充新的功能,如一個Bean的定義和執行個體化的過程前後,是否可以滿足我們進行自定義擴充,對Bean對象執行一些修改、增強、記錄等操作呢? 這個過程基本就是你在使用 Spring 容器架構時候做的一些中間件擴充開發。

二、目标

如果你在自己的實際工作中開發過基于 Spring 的技術元件,或者學習過關于 SpringBoot 中間件設計和開發 等内容。那麼你一定會繼承或者實作了 Spring 對外暴露的類或接口,在接口的實作中擷取了 BeanFactory 以及 Bean 對象的擷取等内容,并對這些内容做一些操作,例如:修改 Bean 的資訊,添加日志列印、處理資料庫路由對資料源的切換、給 RPC 服務連接配接注冊中心等。

在對容器中 Bean 的執行個體化過程添加擴充機制的同時,還需要把目前關于 Spring.xml 初始化和加載政策進行優化,因為我們不太可能讓面向 Spring 本身開發的

DefaultListableBeanFactory

服務,直接給予使用者使用。修改點如下:

我想挑戰下我的軟肋,動手實作個Spring應用上下文!
  • DefaultListableBeanFactory、XmlBeanDefinitionReader,是我們在目前 Spring 架構中對于服務功能測試的使用方式,它能很好的展現出 Spring 是如何對 xml 加載以及注冊Bean對象的操作過程,但這種方式是面向 Spring 本身的,還不具備一定的擴充性。
  • 就像我們現在需要提供出一個可以在 Bean 初始化過程中,完成對 Bean 對象的擴充時,就很難做到自動化處理。是以我們要把 Bean 對象擴充機制功能和對 Spring 架構上下文的包裝融合起來,對外提供完整的服務。

三、設計

為了能滿足于在 Bean 對象從注冊到執行個體化的過程中執行使用者的自定義操作,就需要在 Bean 的定義和初始化過程中插入接口類,這個接口再有外部去實作自己需要的服務。那麼在結合對 Spring 架構上下文的處理能力,就可以滿足我們的目标需求了。整體設計結構如下圖:

我想挑戰下我的軟肋,動手實作個Spring應用上下文!
  • 滿足于對 Bean 對象擴充的兩個接口,其實也是 Spring 架構中非常具有重量級的兩個接口:

    BeanFactoryPostProcess

    BeanPostProcessor

    ,也幾乎是大家在使用 Spring 架構額外新增開發自己組建需求的兩個必備接口。
  • BeanFactoryPostProcessor,是由 Spring 架構組建提供的容器擴充機制,允許在 Bean 對象注冊後但未執行個體化之前,對 Bean 的定義資訊

    BeanDefinition

    執行修改操作。
  • BeanPostProcessor,也是 Spring 提供的擴充機制,不過 BeanPostProcessor 是在 Bean 對象執行個體化之後修改 Bean 對象,也可以替換 Bean 對象。這部分與後面要實作的 AOP 有着密切的關系。
  • 同時如果隻是添加這兩個接口,不做任何包裝,那麼對于使用者來說還是非常麻煩的。我們希望于開發 Spring 的上下文操作類,把相應的 XML 加載 、注冊、執行個體化以及新增的修改和擴充都融合進去,讓 Spring 可以自動掃描到我們的新增服務,便于使用者使用。

四、實作

1. 工程結構

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蟲洞棧」,回複:Spring 專欄,擷取完整源碼

Spring 應用上下文和對Bean對象擴充機制的類關系,如圖 7-3

我想挑戰下我的軟肋,動手實作個Spring應用上下文!
  • 在整個類圖中主要展現出來的是關于 Spring 應用上下文以及對 Bean 對象擴充機制的實作。
  • 以繼承了 ListableBeanFactory 接口的 ApplicationContext 接口開始,擴充出一系列應用上下文的抽象實作類,并最終完成

    ClassPathXmlApplicationContext

    類的實作。而這個類就是最後交給使用者使用的類。
  • 同時在實作應用上下文的過程中,通過定義接口:

    BeanFactoryPostProcessor

    BeanPostProcessor

    兩個接口,把關于對 Bean 的擴充機制串聯進去了。

2. 定義 BeanFactoryPostProcessor

cn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor

public interface BeanFactoryPostProcessor {

    /**
     * 在所有的 BeanDefinition 加載完成後,執行個體化 Bean 對象之前,提供修改 BeanDefinition 屬性的機制
     *
     * @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.

    其實也就是說這個接口是滿足于在所有的 BeanDefinition 加載完成後,執行個體化 Bean 對象之前,提供修改 BeanDefinition 屬性的機制。

3. 定義 BeanPostProcessor

cn.bugstack.springframework.beans.factory.config.BeanPostProcessor

public interface BeanPostProcessor {

    /**
     * 在 Bean 對象執行初始化方法之前,執行此方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * 在 Bean 對象執行初始化方法之後,執行此方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
           
  • Factory hook that allows for custom modification of new bean instances,e.g. checking for marker interfaces or wrapping them with proxies.

    也就是提供了修改新執行個體化 Bean 對象的擴充點。
  • 另外此接口提供了兩個方法:

    postProcessBeforeInitialization

    用于在 Bean 對象執行初始化方法之前,執行此方法、

    postProcessAfterInitialization

    用于在 Bean 對象執行初始化方法之後,執行此方法。

4. 定義上下文接口

cn.bugstack.springframework.context.ApplicationContext

public interface ApplicationContext extends ListableBeanFactory {
}
           
  • context 是本次實作應用上下文功能新增的服務包
  • ApplicationContext,繼承于 ListableBeanFactory,也就繼承了關于 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 源碼,那麼一定會看到這個方法。 接下來也是需要在上下文的實作中完成重新整理容器的操作過程。

5. 應用上下文抽象類實作

cn.bugstack.springframework.context.support.AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeansException {
        // 1. 建立 BeanFactory,并加載 BeanDefinition
        refreshBeanFactory();

        // 2. 擷取 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        // 3. 在 Bean 執行個體化之前,執行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
        invokeBeanFactoryPostProcessors(beanFactory);

        // 4. BeanPostProcessor 需要提前于其他 Bean 對象執行個體化之前執行注冊操作
        registerBeanPostProcessors(beanFactory);

        // 5. 提前執行個體化單例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() 定義實作過程,包括:
      1. 建立 BeanFactory,并加載 BeanDefinition
      1. 擷取 BeanFactory
      1. 在 Bean 執行個體化之前,執行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
      1. BeanPostProcessor 需要提前于其他 Bean 對象執行個體化之前執行注冊操作
      1. 提前執行個體化單例Bean對象
  • 另外把定義出來的抽象方法,refreshBeanFactory()、getBeanFactory() 由後面的繼承此抽象類的其他抽象類實作。

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

    的執行個體化以及對資源配置的加載操作

    loadBeanDefinitions(beanFactory)

    ,在加載完成後即可完成對 spring.xml 配置檔案中 Bean 對象的定義和注冊,同時也包括實作了接口 BeanFactoryPostProcessor、BeanPostProcessor 的配置 Bean 資訊。
  • 但此時資源加載還隻是定義了一個抽象類方法

    loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

    ,繼續由其他抽象類繼承實作。

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 方法實作中,使用 XmlBeanDefinitionReader 類,處理了關于 XML 檔案配置資訊的操作。
  • 同時這裡又留下了一個抽象類方法,getConfigLocations(),此方法是為了從入口上下文類,拿到配置資訊的位址描述。

8. 應用上下文實作類(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,是具體對外給使用者提供的應用上下文方法。
  • 在繼承了 AbstractXmlApplicationContext 以及層層抽象類的功能分離實作後,在此類 ClassPathXmlApplicationContext 的實作中就簡單多了,主要是對繼承抽象類中方法的調用和提供了配置檔案位址資訊。

9. 在Bean建立時完成前置和後置處理

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);
            // 執行 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. 執行 BeanPostProcessor Before 處理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);
        invokeInitMethods(beanName, wrappedBean, beanDefinition);

        // 2. 執行 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;
    }

}
           
  • 實作 BeanPostProcessor 接口後,會涉及到兩個接口方法,

    postProcessBeforeInitialization

    postProcessAfterInitialization

    ,分别作用于 Bean 對象執行初始化前後的額外處理。
  • 也就是需要在建立 Bean 對象時,在 createBean 方法中添加

    initializeBean(beanName, bean, beanDefinition);

    操作。而這個操作主要主要是對于方法

    applyBeanPostProcessorsBeforeInitialization

    applyBeanPostProcessorsAfterInitialization

    的使用。
  • 另外需要提一下,applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 兩個方法是在接口類

    AutowireCapableBeanFactory

    中新增加的。

五、測試

1. 事先準備

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,是我們平常開發經常使用的場景。在 UserService 中注入 UserDao,這樣就能展現出Bean屬性的依賴了。
  • 另外這裡新增加了 company、location,兩個屬性資訊,便于測試 BeanPostProcessor、BeanFactoryPostProcessor 兩個接口對 Bean 屬性資訊擴充的作用。

2. 實作 BeanPostProcessor 和 BeanFactoryPostProcessor

cn.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", "改為:位元組跳動"));
    }

}
           

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 中做過一些元件的開發那麼一定非常熟悉這兩個類,本文的測試也是實作了這兩個類,對執行個體化過程中的 Bean 對象做一些操作。

3. 配置檔案

基礎配置,無BeanFactoryPostProcessor、BeanPostProcessor,實作類

<?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>
           

增強配置,有BeanFactoryPostProcessor、BeanPostProcessor,實作類

<?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,另外一個是包含的。之是以這樣配置主要對照驗證,在運用 Spring 新增加的應用上下文和不使用的時候,都是怎麼操作的。

4. 不用應用上下文

@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執行個體化之前,修改 BeanDefinition 的屬性值
    MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

    // 4. Bean執行個體化之後,修改 Bean 屬性資訊
    MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor();
    beanFactory.addBeanPostProcessor(beanPostProcessor);

    // 5. 擷取Bean對象調用方法
    UserService userService = beanFactory.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("測試結果:" + result);
}
           
  • DefaultListableBeanFactory 建立 beanFactory 并使用 XmlBeanDefinitionReader 加載配置檔案的方式,還是比較熟悉的。
  • 接下來就是對 MyBeanFactoryPostProcessor 和 MyBeanPostProcessor 的處理,一個是在BeanDefinition 加載完成 & Bean執行個體化之前,修改 BeanDefinition 的屬性值,另外一個是在Bean執行個體化之後,修改 Bean 屬性資訊。

測試結果

測試結果:小傅哥,改為:位元組跳動,改為:北京

Process finished with exit code 0
           
  • 通過測試結果可以看到,我們配置的屬性資訊已經與 spring.xml 配置檔案中不一樣了。

5. 使用應用上下文

@Test
public void test_xml() {
    // 1.初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");

    // 2. 擷取Bean對象調用方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("測試結果:" + result);
}
           
  • 另外使用新增加的 ClassPathXmlApplicationContext 應用上下文類,再操作起來就友善多了,這才是面向使用者使用的類,在這裡可以一步把配置檔案交給 ClassPathXmlApplicationContext,也不需要管理一些自定義實作的 Spring 接口的類。
測試結果:小傅哥,改為:位元組跳動,改為:北京

Process finished with exit code 0
           
  • 這與不用應用上下文的測試結果是一樣,不過現在的方式更加友善了。

六、總結

  • 本文主要新增了 Spring 架構中兩個非常重要的接口 BeanFactoryPostProcess、BeanPostProcessor 同時還添加了關于應用上下文的實作,ApplicationContext 接口的定義是繼承 BeanFactory 外新增加功能的接口,它可以滿足于自動識别、資源加載、容器事件、監聽器等功能,同時例如一些國際化支援、單例Bean自動初始化等,也是可以在這個類裡實作和擴充的。
  • 通過本文的實作一定會非常了解 BeanFactoryPostProcess、BeanPostProcessor,以後再做一些關于 Spring 中間件的開發時,如果需要用到 Bean 對象的擷取以及修改一些屬性資訊,那麼就可以使用這兩個接口了。同時 BeanPostProcessor 也是實作 AOP 切面技術的關鍵所在。
  • 有人問:

    面試問那麼多,可是工作又用不到,是嘎哈麼呢?

    ,嘎哈麼,那你說你開車上橋的時候,會每次都撞兩邊的護欄嗎,不撞是吧,那不要修了哇,直接就鋪一個平闆,還省材料了。其實核心技術的原理學習,是更有助于你完成更複雜的架構設計,當你的知識能更全面覆寫所承接的需求時,也就能更好的做出合理的架構和落地。

七、系列推薦

  • 小傅哥,一個有“副業”的碼農!
  • 久等了,小傅哥的《重學Java設計模式》終于出版了,彩印&紙質!
  • 半年招聘篩選了400+份履歷,告訴你怎麼寫容易被撩!
  • 一次代碼評審,差點過不了試用期!
  • 大學四年到畢業工作5年的學習路線資源彙總

公衆号:bugstack蟲洞棧 | 作者小傅哥多年從事一線網際網路 Java 開發的學習曆程技術彙總,旨在為大家提供一個清晰詳細的學習教程,側重點更傾向編寫Java核心内容。如果能為您提供幫助,請給予支援(關注、點贊、分享)!