手寫Spring,定義標記型別Aware介面,實現感知容器物件

小傅哥發表於2021-06-29


作者:小傅哥
部落格:https://bugstack.cn

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

一、前言

同事寫的程式碼,我竟絲毫看不懂!

大佬的程式碼,就像 “賴蛤蟆泡青蛙,張的醜玩的花” :一個類實現了多個介面、繼承的類又繼承了其他類、介面還可以和介面繼承、實現介面的抽象類再由類實現抽象類方法、類A繼承的類B實現了類A實現的介面C,等等。

看上去複雜又難懂的程式碼,卻又能一次次滿足需求的高效迭代和順利擴充套件,而像螺絲釘一樣搬磚的你,只是在大佬寫的程式碼裡,完成某個介面下的一小塊功能,甚至寫完了也不知道怎麼就被呼叫執行了,整個過程像看 Spring 原始碼一樣神奇,跳來跳去的摸不著頭緒!

其實這主要是因為你的程式碼是否運用了設計模式,當然設計模式也沒那麼神奇,就像你們兩家都是120平米的房子,他家有三室兩廳一廚一衛,南北通透,全陽採光。但你家就不一樣了,你家是鍋碗瓢盆、衛浴馬桶、沙發茶几還有那1.8的雙人床,在120平米的房子裡敞開了放,沒有動靜隔離,也沒有乾溼分離,純自由發揮。所以你的程式碼看上去就亂的很!

二、目標

目前已實現的 Spring 框架,在 Bean 操作上能提供出的能力,包括:Bean 物件的定義和註冊,以及在操作 Bean 物件過程中執行的,BeanFactoryPostProcessor、BeanPostProcessor、InitializingBean、DisposableBean,以及在 XML 新增的一些配置處理,讓我們可以 Bean 物件有更強的操作性。

那麼,如果我們想獲得 Spring 框架提供的 BeanFactory、ApplicationContext、BeanClassLoader等這些能力做一些擴充套件框架的使用時該怎麼操作呢。所以我們本章節希望在 Spring 框架中提供一種能感知容器操作的介面,如果誰實現了這樣的一個介面,就可以獲取介面入參中的各類能力。

三、設計

如果說我希望拿到 Spring 框架中一些提供的資源,那麼首先需要考慮以一個什麼方式去獲取,之後你定義出來的獲取方式,在 Spring 框架中該怎麼去承接,實現了這兩項內容,就可以擴充套件出你需要的一些屬於 Spring 框架本身的能力了。

在關於 Bean 物件例項化階段我們操作過一些額外定義、屬性、初始化和銷燬的操作,其實我們如果像獲取 Spring 一些如 BeanFactory、ApplicationContext 時,也可以通過此類方式進行實現。那麼我們需要定義一個標記性的介面,這個介面不需要有方法,它只起到標記作用就可以,而具體的功能由繼承此介面的其他功能性介面定義具體方法,最終這個介面就可以通過 instanceof 進行判斷和呼叫了。整體設計結構如下圖:

  • 定義介面 Aware,在 Spring 框架中它是一種感知標記性介面,具體的子類定義和實現能感知容器中的相關物件。也就是通過這個橋樑,向具體的實現類中提供容器服務
  • 繼承 Aware 的介面包括:BeanFactoryAware、BeanClassLoaderAware、BeanNameAware和ApplicationContextAware,當然在 Spring 原始碼中還有一些其他關於註解的,不過目前我們還是用不到。
  • 在具體的介面實現過程中你可以看到,一部分(BeanFactoryAware、BeanClassLoaderAware、BeanNameAware)在 factory 的 support 資料夾下,另外 ApplicationContextAware 是在 context 的 support 中,這是因為不同的內容獲取需要在不同的包下提供。所以,在 AbstractApplicationContext 的具體實現中會用到向 beanFactory 新增 BeanPostProcessor 內容的 ApplicationContextAwareProcessor 操作,最後由 AbstractAutowireCapableBeanFactory 建立 createBean 時處理相應的呼叫操作。關於 applyBeanPostProcessorsBeforeInitialization 已經在前面章節中實現過,如果忘記可以往前翻翻

四、實現

1. 工程結構

small-spring-step-08
└── 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
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── HierarchicalBeanFactory.java
    │           │   │   ├── InitializingBean.java
    │           │   │   └── ListableBeanFactory.java
    │           │   ├── BeansException.java
    │           │   ├── PropertyValue.java
    │           │   └── PropertyValues.java 
    │           ├── context
    │           │   ├── support
    │           │   │   ├── AbstractApplicationContext.java 
    │           │   │   ├── AbstractRefreshableApplicationContext.java 
    │           │   │   ├── AbstractXmlApplicationContext.java 
    │           │   │   ├── ApplicationContextAwareProcessor.java 
    │           │   │   └── ClassPathXmlApplicationContext.java 
    │           │   ├── ApplicationContext.java 
    │           │   ├── ApplicationContextAware.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
                └── ApiTest.java

工程原始碼公眾號「bugstack蟲洞棧」,回覆:Spring 專欄,獲取完整原始碼

Spring 感知介面的設計和實現類關係,如圖 9-2

圖 9-2

  • 以上整個類關係就是關於 Aware 感知的定義和對容器感知的實現。
  • Aware 有四個繼承的介面,其他這些介面的繼承都是為了繼承一個標記,有了標記的存在更方便類的操作和具體判斷實現。
  • 另外由於 ApplicationContext 並不是在 AbstractAutowireCapableBeanFactory 中 createBean 方法下的內容,所以需要像容器中註冊 addBeanPostProcessor ,再由 createBean 統一呼叫 applyBeanPostProcessorsBeforeInitialization 時進行操作。

2. 定義標記介面

cn.bugstack.springframework.beans.factory.Aware

/**
 * Marker superinterface indicating that a bean is eligible to be
 * notified by the Spring container of a particular framework object
 * through a callback-style method.  Actual method signature is
 * determined by individual subinterfaces, but should typically
 * consist of just one void-returning method that accepts a single
 * argument.
 *
 * 標記類介面,實現該介面可以被Spring容器感知
 *
 */
public interface Aware {
}
  • 在 Spring 中有特別多類似這樣的標記介面的設計方式,它們的存在就像是一種標籤一樣,可以方便統一摘取出屬於此類介面的實現類,通常會有 instanceof 一起判斷使用。

3. 容器感知類

3.1 BeanFactoryAware

cn.bugstack.springframework.beans.factory.BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}
  • Interface to be implemented by beans that wish to be aware of their owning {@link BeanFactory}.
  • 實現此介面,既能感知到所屬的 BeanFactory

3.2 BeanClassLoaderAware

cn.bugstack.springframework.beans.factory.BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware{

    void setBeanClassLoader(ClassLoader classLoader);

}
  • Callback that allows a bean to be aware of the bean{@link ClassLoader class loader}; that is, the class loader used by the present bean factory to load bean classes.
  • 實現此介面,既能感知到所屬的 ClassLoader

3.3 BeanNameAware

cn.bugstack.springframework.beans.factory.BeanNameAware

public interface BeanNameAware extends Aware {

    void setBeanName(String name);

}
  • Interface to be implemented by beans that want to be aware of their bean name in a bean factory.
  • 實現此介面,既能感知到所屬的 BeanName

3.4 ApplicationContextAware

cn.bugstack.springframework.context.ApplicationContextAware

public interface ApplicationContextAware extends Aware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}
  • Interface to be implemented by any object that wishes to be notifiedof the {@link ApplicationContext} that it runs in.
  • 實現此介面,既能感知到所屬的 ApplicationContext

4. 包裝處理器(ApplicationContextAwareProcessor)

cn.bugstack.springframework.context.support.ApplicationContextAwareProcessor

public class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ApplicationContextAware){
            ((ApplicationContextAware) bean).setApplicationContext(applicationContext);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}
  • 由於 ApplicationContext 的獲取並不能直接在建立 Bean 時候就可以拿到,所以需要在 refresh 操作時,把 ApplicationContext 寫入到一個包裝的 BeanPostProcessor 中去,再由 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization 方法呼叫。

5. 註冊 BeanPostProcessor

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. 新增 ApplicationContextAwareProcessor,讓繼承自 ApplicationContextAware 的 Bean 物件都能感知所屬的 ApplicationContext
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

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

        // 5. BeanPostProcessor 需要提前於其他 Bean 物件例項化之前執行註冊操作
        registerBeanPostProcessors(beanFactory);

        // 6. 提前例項化單例Bean物件
        beanFactory.preInstantiateSingletons();
    }
    
 	// ...   
}    
  • refresh() 方法就是整個 Spring 容器的操作過程,與上一章節對比,本次新增加了關於 addBeanPostProcessor 的操作。
  • 新增 ApplicationContextAwareProcessor,讓繼承自 ApplicationContextAware 的 Bean 物件都能感知所屬的 ApplicationContext。

6. 感知呼叫操作

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);
        }

        // 註冊實現了 DisposableBean 介面的 Bean 物件
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        addSingleton(beanName, bean);
        return bean;
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

        // invokeAwareMethods
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }

        // 1. 執行 BeanPostProcessor Before 處理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 執行 Bean 物件的初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 2. 執行 BeanPostProcessor After 處理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }



    @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;
    }

}
  • 這裡我們去掉了一些類的內容,只保留關於本次 Aware 感知介面的操作。
  • 首先在 initializeBean 中,通過判斷 bean instanceof Aware,呼叫了三個介面方法,BeanFactoryAware.setBeanFactory(this)BeanClassLoaderAware.setBeanClassLoader(getBeanClassLoader())BeanNameAware.setBeanName(beanName),這樣就能通知到已經實現了此介面的類。
  • 另外我們還向 BeanPostProcessor 中新增了 ApplicationContextAwareProcessor,此時在這個方法中也會被呼叫到具體的類實現,得到一個 ApplicationContex 屬性。

五、測試

1. 事先準備

cn.bugstack.springframework.test.bean.UserDao

public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    public void initDataMethod(){
        System.out.println("執行:init-method");
        hashMap.put("10001", "小傅哥");
        hashMap.put("10002", "八杯水");
        hashMap.put("10003", "阿毛");
    }

    public void destroyDataMethod(){
        System.out.println("執行:destroy-method");
        hashMap.clear();
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}

cn.bugstack.springframework.test.bean.UserService

public class UserService implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware {

    private ApplicationContext applicationContext;
    private BeanFactory beanFactory;

    private String uId;
    private String company;
    private String location;
    private UserDao userDao;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("Bean Name is:" + name);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("ClassLoader:" + classLoader);
    }

    // ...get/set
}
  • UserDao 本次並沒有什麼改變,還是提供了關於初始化的方法,並在 Spring.xml 中提供 init-method、destroy-method 配置資訊。
  • UserService 新增加,BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware,四個感知的實現類,並在類中實現相應的介面方法。

2. 配置檔案

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

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao" init-method="initDataMethod" destroy-method="destroyDataMethod"/>

    <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>
  • 本章節中並沒有額外新增加配置資訊,與上一章節內容相同。

3. 單元測試

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

    // 2. 獲取Bean物件呼叫方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("測試結果:" + result);
    System.out.println("ApplicationContextAware:"+userService.getApplicationContext());
    System.out.println("BeanFactoryAware:"+userService.getBeanFactory());
}
  • 測試方法中主要是新增了一寫關於新增 Aware 實現的呼叫,其他不需要呼叫的也列印了相應的日誌資訊,可以在測試結果中看到。

測試結果

執行:init-method
ClassLoader:sun.misc.Launcher$AppClassLoader@14dad5dc
Bean Name is:userService
測試結果:小傅哥,騰訊,深圳
ApplicationContextAware:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@5ba23b66
BeanFactoryAware:cn.bugstack.springframework.beans.factory.support.DefaultListableBeanFactory@2ff4f00f
執行:destroy-method



Process finished with exit code 0
  • 從測試結果可以看到,本章節新增加的感知介面對應的具體實現(BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware),已經可以如期輸出結果了。

六、總結

  • 目前關於 Spring 框架的實現中,某些功能點已經越來趨向於完整,尤其是 Bean 物件的生命週期,已經有了很多的體現。整體總結如圖 9-3

    圖 9-3

  • 本章節關於 Aware 的感知介面的四個繼承介面 BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware 的實現,又擴充套件了 Spring 的功能。如果你有做過關於 Spring 中介軟體的開發那麼一定會大量用到這些類,現在你不只是用過,而且還知道他們都是什麼時候觸達的,在以後想排查類的例項化順序也可以有一個清晰的思路了。

  • 每一章節內容的實現都是在以設計模式為核心的結構上填充各項模組的功能,單純的操作編寫程式碼並不會有太多收穫,一定是要理解為什麼這麼設計,這麼設計的好處是什麼,怎麼就那麼多介面和抽象類的應用,這些才是 Spring 框架學習的核心所在。

七、系列推薦

相關文章