1. 程式人生 > 實用技巧 >c++ range庫

c++ range庫

上一節我們已經分析到AbsractApplicationContext類refresh方法中的postProcessBeanFactory方法,在分析registerBeanPostProcessors之前我們先介紹一下Spring 的鉤子介面,後面我們會在Spring原始碼中介紹到這些鉤子介面的呼叫時機。

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            
// Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);
try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }

一、鉤子介面介紹

Spring 提供了非常多的擴充套件介面,官方將這些介面稱之為鉤子,這些鉤子會在特定的時間被回撥,以此來增強 Spring 功能,眾多優秀的框架也是通過擴充套件這些介面,來實現自身特定的功能,如 SpringBoot、mybatis 等。

二、Aware介面

Aware從字面的意思理解就是"知道"、“感知”的意思,是用來獲取Spring內部物件的介面。Aware自身是一個頂級介面,它有一系列子介面,在一個 Bean 中實現這些子介面並重寫裡面的 set 方法後,Spring 容器啟動時,就會回撥該 set 方法,而相應的物件會通過方法引數傳遞進去。我們以其中的 ApplicationContextAware 介面為例。

2.1、ApplicationContextAware

大部分 Aware 系列介面都有一個規律,它們以物件名稱為字首,獲取的就是該物件,所以 ApplicationContextAware 獲取的物件是 ApplicationContext 。

public interface ApplicationContextAware extends Aware {

    /**
     * Set the ApplicationContext that this object runs in.
     * Normally this call will be used to initialize the object.
     * <p>Invoked after population of normal bean properties but before an init callback such
     * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
     * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
     * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
     * {@link MessageSourceAware}, if applicable.
     * @param applicationContext the ApplicationContext object to be used by this object
     * @throws ApplicationContextException in case of context initialization errors
     * @throws BeansException if thrown by application context methods
     * @see org.springframework.beans.factory.BeanInitializationException
     */
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

ApplicationContextAware 原始碼非常簡單,其繼承了 Aware 介面,並定義一個 set 方法,引數就是 ApplicationContext 物件,當然,其它系列的 Aware 介面也是類似的定義。其具體使用方式如下:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware {
    /*
     * 儲存應用上下文
     */
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        //輸出所有BeanDefinition name
        for(String name:applicationContext.getBeanDefinitionNames()) {
            log.info(name);
        }
    }
}

在 Spring 啟動過程中,會回撥 setApplicationContext 方法,並傳入 ApplicationContext 物件,之後就可對該物件進行操作。我們獲取到ApplicationContext物件,並將所有BeanDefinition名稱輸出:

其它系列的 Aware 介面也是如此使用。具體的呼叫時機會在後面詳細介紹。

以下是幾種常用的 Aware 介面:

  • BeanFactoryAware:獲取 BeanFactory 物件,它是基礎的容器介面。
  • BeanNameAware:獲取 Bean 的名稱。
  • EnvironmentAware:獲取 Environment 物件,它表示整個的執行時環境,可以設定和獲取配置屬性。
  • ApplicationEventPublisherAware:獲取 ApplicationEventPublisher 物件,它是用來發布事件的。
  • ResourceLoaderAware:獲取 ResourceLoader 物件,它是獲取資源的工具。

三、InitializingBean, DisposableBean

3.1、InitializingBean

InitializingBean 是一個可以在 Bean 的生命週期執行自定義操作的介面,凡是實現該介面的 Bean,在初始化階段都可以執行自定義的操作。

public interface InitializingBean {

    /**
     * Invoked by the containing {@code BeanFactory} after it has set all bean properties
     * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
     * <p>This method allows the bean instance to perform validation of its overall
     * configuration and final initialization when all bean properties have been set.
     * @throws Exception in the event of misconfiguration (such as failure to set an
     * essential property) or if initialization fails for any other reason
     */
    void afterPropertiesSet() throws Exception;

}

從 InitializingBean 原始碼中可以看出它有一個 afterPropertiesSet 方法,當一個 Bean 實現該介面時,在 Bean 的初始化階段,會回撥 afterPropertiesSet 方法,其初始化階段具體指 Bean 設定完屬性之後。

3.2、DisposableBean

同理,DisposableBean在Bean銷燬時執行自定義的操作,必須資源的釋放。

public interface DisposableBean {

    /**
     * Invoked by the containing {@code BeanFactory} on destruction of a bean.
     * @throws Exception in case of shutdown errors. Exceptions will get logged
     * but not rethrown to allow other beans to release their resources as well.
     */
    void destroy() throws Exception;

}

比如:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean {
    /*
     * 建構函式
     */
    public BeanTest(){
        log.info("New object...");
    }

    @Override
    public void destroy()  {
        log.info("Destroying ...");
    }

    @Override
    public void afterPropertiesSet() {
        log.info("Initializing ....");
    }
}

四、BeanPostProcessor、BeanFactoryPostProcessor

4.1、BeanPostProcessor

BeanPostProcessor 和 InitializingBean 有點類似,也是可以在 Bean 的生命週期執行自定義操作,一般稱之為 Bean 的後置處理器,不同的是, BeanPostProcessor 可以在 Bean 初始化前、後執行自定義操作,且針對的目標也不同,InitializingBean 針對的是實現 InitializingBean 介面的 Bean,而 BeanPostProcessor 針對的是所有的 Bean。並且postProcessBeforeInitialization在物件建立之後,afterPropertiesSet之前執行,而postProcessAfterInitialization在afterPropertiesSet之後執行

public interface BeanPostProcessor {

    // Bean 初始化前呼叫
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    // Bean 初始化後呼叫
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

所有的 Bean 在初始化前、後都會回撥介面中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入參是當前正在初始化的 Bean 物件和 BeanName。值得注意的是 Spring 內建了非常多的 BeanPostProcessor ,以此來完善自身功能,這部分會在後面文章深入討論。

我們擴充我們的測試類AwareTest :

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware, BeanPostProcessor {
    /*
     * 儲存應用上下文
     */
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        //輸出所有BeanDefinition name
        for(String name:applicationContext.getBeanDefinitionNames()) {
            log.info(name);
        }
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)  {
        if(beanName.equals("beanTest")) {
        log.info("postProcessBeforeInitialization:" + beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)  {
        if(beanName.equals("beanTest")) {
            log.info("postProcessAfterInitialization:" + beanName);
        }
        return bean;
    }

}

BeanTest :

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean, BeanNameAware {
    /*
     * 儲存當前bean name
     */
    private String beanName;

    /*
     * 建構函式
     */
    public BeanTest(){
        log.info("New object...");
    }

    @Override
    public void destroy()  {
        log.info("Destroying ...");
    }

    @Override
    public void afterPropertiesSet() {
        log.info("Initializing ....");
    }


    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        log.info("Current bean name:" + name);
    }
}

可以看到beanTest物件先是被例項化出來,然後執行BeanPostProcessor的postProcessBeforeInitialization,再執行InitializingBean的afterPropertiesSet,最後執行BeanPostProcessor的postProcessAfterInitialization方法。而ApplicationContextAware的setApplicationContext方法執行時所有BeanDefinition都已載入,但還未例項化Bean

BeanPostProcessor 使用場景其實非常多,因為它可以獲取正在初始化的 Bean 物件,然後可以對Bean 物件做一些定製化的操作,如:判斷該 Bean 是否為某個特定物件、獲取 Bean 的註解元資料等。事實上,Spring 內部也正是這樣使用的,之前我們介紹的Spring Boot -- Spring AOP原理及簡單實現手寫AOP時也是利用了BeanPostProcessor的特性,我們對@Pointcut註解指定的Bean都進行了代理處理。

4.2、BeanFactoryPostProcessor

BeanFactoryPostProcessor 是 Bean 工廠的後置處理器,一般用來修改上下文中的 BeanDefinition,修改 Bean 的屬性值。

public interface BeanFactoryPostProcessor {
    // 入參是一個 Bean 工廠:ConfigurableListableBeanFactory。該方法執行時,所有 BeanDefinition 都已被載入,但還未例項化 Bean。
    // 可以對其進行覆蓋或新增屬性,甚至可以用於初始化 Bean。
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanFactoryPostProcessor 原始碼非常簡單,其提供了一個 postProcessBeanFactory 方法,當所有的 BeanDefinition 被載入時,該方法會被回撥。值得注意的是,Spring 內建了許多 BeanFactoryPostProcessor 的實現,以此來完善自身功能。 這裡,我們來實現一個自定義的 BeanFactoryPostProcessor:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component;

@Data
@Slf4j
@Component
public class AwareTest implements  BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        log.info("------------------------------------------postProcessBeanFactory-------------------------");
        String beanNames[] = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            log.info(beanDefinition.toString());
        }
    }
}

主要是通過 Bean 工廠獲取所有的 BeanDefinition 。

可以看到,BeanDefinition 正確輸出,裡面是一些 Bean 的相關定義,如:是否懶載入、Bean 的 Class 以及 Bean 的屬性等。

五、ImportSelector

參考文章:

[1]Spring(七)核心容器 - 鉤子介面(轉載)