天天看點

手寫Spring,定義标記類型Aware接口,實作感覺容器對象

手寫Spring,定義标記類型Aware接口,實作感覺容器對象

作者:小傅哥

部落格: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

進行判斷和調用了。整體設計結構如下圖:

手寫Spring,定義标記類型Aware接口,實作感覺容器對象
  • 定義接口 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

手寫Spring,定義标記類型Aware接口,實作感覺容器對象
  • 以上整個類關系就是關于 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
    手寫Spring,定義标記類型Aware接口,實作感覺容器對象
  • 本章節關于 Aware 的感覺接口的四個繼承接口 BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware 的實作,又擴充了 Spring 的功能。如果你有做過關于 Spring 中間件的開發那麼一定會大量用到這些類,現在你不隻是用過,而且還知道他們都是什麼時候觸達的,在以後想排查類的執行個體化順序也可以有一個清晰的思路了。
  • 每一章節内容的實作都是在以設計模式為核心的結構上填充各項子產品的功能,單純的操作編寫代碼并不會有太多收獲,一定是要了解為什麼這麼設計,這麼設計的好處是什麼,怎麼就那麼多接口和抽象類的應用,這些才是 Spring 架構學習的核心所在。

七、系列推薦

  • 大學畢業要寫多少行代碼,才能不用花錢教育訓練就找到一份開發工作?
  • 90%的程式員,都沒用過多線程和鎖,怎麼成為架構師?
  • 握草,你竟然在代碼裡下毒!
  • Netty+JavaFx實戰:仿桌面版微信聊天
  • 剛火了的中台轉頭就拆,一大波公司放不下又拿不起來!

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