【修煉內功】[spring-framework] [4] ApplicationContext給開發者提供了哪些(默認)擴展

本文已收錄 【修煉內功】躍遷之路

spring-framework.jpg

林中小舍.png

[spring-framework] [3] Bean是如何被建立的 一文中介紹了Spring體系內bean的建立及銷燬過程,也簡單提到了Spring預留給開發者的一些擴展點java

  • @Value中的表達式是如何解析的
  • 代理類是如何生成的(AOP)
  • 各類Aware的setter方法是如何被調用的
  • Event是如何被監聽並處理的
  • 各類開箱即用的starter是如何動態註冊bean的
  • ...

本文就Spring註冊擴展點的方式、默認擴展點、自定義擴展點進行展開介紹,除此外也但願能經過Spring的代碼設計,擴充本身在代碼乃至系統設計上的能力spring

什麼是ApplicationContext?

BeanFactory實現了IOC的基礎能力,ApplicationContext又是什麼?json

ApplicationContext是BeanFactoryBean的子類,除了繼承IOC的基礎能力外segmentfault

  • 支持國際化(MessageSource
  • 支持資源訪問(ResourcePatternResolver,詳見[spring-framework] [1] Resource
  • 事件機制(ApplicationEventPublisher
  • 默認初始化全部Singleton
  • 提供擴展能力,並註冊衆多默認擴展能力
  • ...

本文就擴展能力着重介紹app

Context.ApplicationContext

從上圖中看到了熟悉的ClasspathXmlApplicationContextAnnotationConfigApplicationContext,不管何種功能的ApplicationContext,在作完基本的初始化後均會調用AbstractApplicationContext#refresh,這也是今天的入口框架

Flowchart.refresh

從上圖中彷佛看到了一些熟悉的內容,Aware、BeanFactoryPostProcessor、BeanPostProcessor、等等,或許能夠解釋上篇 [spring-framework] [3] Bean是如何被建立的 及文章開始處的一些問題異步

準備上下文

AbstractApplicationContext#prepareRefreshjvm

該部分主要實現對上下文的準備工做,其主要涉及到兩個接口AbstractApplicationContext#initPropertySourcesConfigurablePropertyResolver#validateRequiredPropertiesasync

前者由子類實現,用於初始化PropertySource;後者用於對必要屬性進行驗證,如ide

public class MyClasspathXmlApplicationContext extends ClassPathXmlApplicationContext {
    @Override
    protected void initPropertySources() {
        super.initPropertySources();
        getEnvironment().setRequiredProperties("runtimeEnv");
    }
}

重寫initPropertySources方法,並添加runtimeEnv爲必須的環境變量屬性,如此在系統啓動的時候便會進行檢測,對於不存在任何一個必要環境變量的狀況均會拋出異常終止啓動

加載BeanFactory

AbstractApplicationContext#obtainFreshBeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  // 子類實現
    refreshBeanFactory();
    return getBeanFactory();
}

該函數內部實現比較簡單,重點在refreshBeanFactory,該函數一樣由子類實現

☆ 對於AbstractRefreshableApplicationContext(),refreshBeanFactory基本步驟爲

  1. 建立BeanFactory (DefaultListableBeanFactory)
  2. 設置BeanFactory
  3. 加載BeanDefinition

在第3步中,AbstractXmlApplicationContext的實現則是對xml配置文件的解析及加載(見[spring-framework] [3] Bean是如何被建立的);AnnotationConfigWebApplicationContext的實現則是對class文件的掃描並加載(基於註解的Bean掃描及加載邏輯會放在SpringBoot系列文章中);等其餘基於AbstractRefreshableApplicationContext的ApplicationContext實現

☆ 對於GenericApplicationContext,BeanFactory的建立及BeanDefinition的加載在refresh調用以前早已完成,refreshBeanFactory的實現則是對BeanFactory加載狀態的簡單校驗(AnnotationConfigApplicationContext的Bean掃描及加載邏輯會放在SpringBoot系列文章中)

填充部分擴展

AbstractApplicationContext#prepareBeanFactory

該會函數執行如下邏輯

  1. 設置BeanFactory的ClassLoader
  2. 註冊默認BeanExpressionResolver,用於依賴注入時SpEL的支持
  3. 註冊默認PropertyEditor,用於依賴注入時對參數的解析轉換
  4. 註冊幾個特殊Aware的處理邏輯
  5. 註冊AspectJ相關的幾個處理器,用於AOP的支持
  6. 註冊幾個特殊的BeanDefinition

註冊默認BeanExpressionResolver

beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

StandardBeanExpressionResolve的做用在於使用SpEL解析 #{ ... }類型的值,具體的解析邏輯見StandardBeanExpressionResolver#evaluate,那Spring在何時使用StandardBeanExpressionResolve呢?還記得上篇文章[spring-framework] [3] Bean是如何被建立的介紹的,在解析依賴的值時DefaultListableBeanFactory#doResolveDependency

Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
    if (value instanceof String) {
        String strVal = resolveEmbeddedValue((String) value);
        BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                getMergedBeanDefinition(beanName) : null);
    // 使用StandardBeanExpressionResolve,利用SpEL,對 #{ ... } 類型的值進行解析
        value = evaluateBeanDefinitionString(strVal, bd);
    }
}
Q: Spring中默認的,除了能夠處理 #{ ... } 外,還能夠處理 ${ ... },後者的處理器是如何註冊的?

註冊默認的PropertyEditor

beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

嚴格來說,這裏並無直接註冊PropertyEditor,而是註冊PropertyEditorRegistrar,前者用於真正的類型轉換,後者用於PropertyEditor的註冊

PropertyEditor - supports a variety of different kinds of ways of displaying and updating property values. Most PropertyEditors will only need to support a subset of the different options available in this API.

PropertyEditorRegistry - Encapsulates methods for registering JavaBeans PropertyEditors. This is the central interface that a PropertyEditorRegistrar operates on.

addPropertyEditorRegistrar的邏輯很是簡單,將須要註冊的PropertyEditorRegistrar存放在AbstractBeanFactory#propertyEditorRegistrars,那什麼時候使用呢?

還記得上篇文章中介紹的BeanWrapper麼?在實例化bean以後屬性依賴注入以前,會將實例化的bean包裝爲BeanWrapper並進行初始化(AbstractBeanFactory#initBeanWrapper),其內部會使用到AbstractBeanFactory#registerCustomEditors

// org.springframework.beans.factory.support.AbstractBeanFactory#registerCustomEditors 節選
if (!this.propertyEditorRegistrars.isEmpty()) {
  // 1. 使用 propertyEditorRegistrars 註冊
    for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
            registrar.registerCustomEditors(registry);
  }
}
if (!this.customEditors.isEmpty()) {
  // 2. 使用 customEditors 註冊
    this.customEditors.forEach((requiredType, editorClass) ->
            registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
}

回頭看ResourceEditorRegistrar#registerCustomEditors,其一共註冊了12種PropertyEditor(源碼比較簡單,再也不貼出)

Type PropertyEditor
Resource ResourceEditor
Resource[] ResourceArrayPropertyEditor
ContextResource ResourceEditor
InputStream InputStreamEditor
InputSource InputStreamEditor
File FileEditor
Path PathEditor
Reader ReaderEditor
URL URLEditor
URI URIEditor
Class ClassEditor
Class[] ClassArrayEditor

AbstractBeanFactory#registerCustomEditors提供了兩種註冊方式

  • 使用AbstractBeanFactory#propertyEditorRegistrars批量註冊
  • 使用AbstractBeanFactory#customEditors單個註冊

Q: 是否能夠自定義PropertyEditor註冊?

那,PropertyEditor什麼時候使用?這裏就要再次回到解析依賴的值時的DefaultListableBeanFactory#doResolveDependency(參考[spring-framework] [3] Bean是如何被建立的

Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
    if (value instanceof String) {
        String strVal = resolveEmbeddedValue((String) value);
        BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                getMergedBeanDefinition(beanName) : null);
    // 使用StandardBeanExpressionResolve,利用SpEL,對 #{ ... } 類型的值進行解析
        value = evaluateBeanDefinitionString(strVal, bd);
    }
  TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  // 使用PropertyEditors進行類型轉換
    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}

結合上篇文章,事情是否慢慢變得明朗起來?但,Spring提供給開發者的PropertyEditor僅僅以上12種麼?若是仔細源碼源碼的話會發現,在TypeConverterDelegate#convertIfNecessary的邏輯中,若是在上述已註冊的PropertyEditors中找不到合適的Editor時,則會在默認的PropertyEditors中繼續匹配,默認的PropertyEditor邏輯在PropertyEditorRegistrySupport#createDefaultEditors,一樣以表格形式列出

Type PropertyEditor
Charset CharsetEditor
Class ClassEditor
Class[] ClassArrayEditor
Currency CurrencyEditor
File FileEditor
InputStream InputStreamEditor
InputSource InputSourceEditor
Locale LocaleEditor
Path PathEditor
Pattern PatternEditor
Properties PropertiesEditor
Reader ReaderEditor
Resource[] ResourceArrayPropertyEditor
TimeZone TimeZoneEditor
URI URIEditor
URL URLEditor
UUID UUIDEditor
ZoneId ZoneIdEditor
Collection CustomCollectionEditor
Set CustomCollectionEditor
SortedSet CustomCollectionEditor
List CustomCollectionEditor
SortedMap CustomMapEditor
byte[] ByteArrayPropertyEditor
char[] CharArrayPropertyEditor
char CharacterEditor
Character CharacterEditor
boolean CustomBooleanEditor
Boolean CustomBooleanEditor
byte CustomNumberEditor
Byte CustomNumberEditor
short CustomNumberEditor
hort CustomNumberEditor
int CustomNumberEditor
Integer CustomNumberEditor
long CustomNumberEditor
Long CustomNumberEditor
float CustomNumberEditor
Float CustomNumberEditor
double CustomNumberEditor
Double CustomNumberEditor
BigDecimal CustomNumberEditor
BigInteger CustomNumberEditor
BigInteger[] StringArrayPropertyEditor
short[] StringArrayPropertyEditor
int[] StringArrayPropertyEditor
long[] StringArrayPropertyEditor

註冊自定義的PropertyEditor

是否嚐到了PropertyEditor的甜頭,那如何註冊本身的PropertyEditor呢?上面瞭解到,能夠經過設置AbstractBeanFactorypropertyEditorRegistrars或者customEditors實現批量或者單個PropertyEditor的註冊,這裏Spring爲開發者提供了CustomEditorConfigurer,咱們只須要註冊一個CustomEditorConfigurer類型的Bean,並設置其中的propertyEditorRegistrarscustomEditors便可

// 註冊
@Bean
public CustomEditorConfigurer myEditors() {
  CustomEditorConfigurer editorConfigurer = new CustomEditorConfigurer();
  // 設置CustomEditors,或者PropertyEditorRegistrars
  editorConfigurer.setCustomEditors(new HashMap<Class<?>, Class<? extends PropertyEditor>> {{
    put(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy/MM/dd HH:mm"), false));
  }});
  return editorConfigurer;
}

// 使用
@Value("2020/04/19 15:09")
private Date now;

其原理在於CustomEditorConfigurer繼承了BeanFactoryPostProcessor,Spring會自動調用CustomEditorConfigurer#postProcessBeanFactory方法將上述自定義的值設置到AbstractBeanFactorypropertyEditorRegistrarscustomEditors中(繼續向下閱讀,瞭解BeanFactoryPostProcessor的做用)

public class CustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {
  // 自定義PropertyEditorRegistrar實現批量註冊
  @Nullable
    private PropertyEditorRegistrar[] propertyEditorRegistrars;

  // 自定義PropertyEditor實現單個註冊
    @Nullable
    private Map<Class<?>, Class<? extends PropertyEditor>> customEditors;
  
  // ... setters & setters
}

註冊特殊Aware的處理邏輯

參考上篇文章[spring-framework] [3] Bean是如何被建立的,在bean實例化而且注入依賴以後會執行AbstractAutowireCapableBeanFactory#initializeBean對bean進行最後的初始化,其中在在AbstractAutowireCapableBeanFactory#invokeAwareMethods內分別針對BeanNameAwareBeanClassLoaderAwareBeanFactoryAware進行了處理(調用setter方法設置相應的注入),除此以外Spring還有提供其餘的Aware麼?

AbstractApplicationContext#prepareBeanFactory中Spring會註冊一個特殊的BeanPostProcessor

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

其實現了postProcessBeforeInitialization方法,其內部調用ApplicationContextAwareProcessor#invokeAwareInterfaces針對另外的幾類Aware進行了處理

Aware Invoke From
EnvironmentAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
EmbeddedValueResolverAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
ResourceLoaderAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
ApplicationEventPublisherAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
MessageSourceAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
ApplicationContextAware ApplicationContextAwareProcessor#postProcessBeforeInitialization
BeanNameAware AbstractAutowireCapableBeanFactory#invokeAwareMethods
BeanClassLoaderAware AbstractAutowireCapableBeanFactory#invokeAwareMethods
BeanFactoryAware AbstractAutowireCapableBeanFactory#invokeAwareMethods

除此以外,Spring會將上述幾類Aware設置爲ignoreDependencyInterface,這意味着以上幾類Bean的注入只能經過Aware的方式而不能經過其餘屬性依賴注入的方式(屬性注入、函數參數注入、等)

依賴注入有兩種方式,其各有所長

  • 經過屬性、方法參數注入
  • 經過Aware注入

Q: 是否能夠自定義Aware並實現特殊依賴的注入?

註冊特殊的Bean

在使用Spring時,是否有過直接注入BeanFactory亦或是ResourceLoader,這些bean正是在這裏被Spring註冊進去的,除以上外Spring還注入了

  • BeanFactory
  • ResourceLoader
  • ApplicationEventPublisher
  • ApplicationContext
  • Environment
  • systemProperties - Environment#.getSystemProperties:Map<String, Object>
  • systemEnvironment - Environment#.getSystemEnvironment:Map<String, Object>

以上都可直接注入使用

激活BeanFactoryPostProcessor

AbstractApplicationContext#invokeBeanFactoryPostProcessors

PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors

BeanPostProcessor在bean的實例化過程當中起着重要的做用([spring-framework] [3] Bean是如何被建立的),BeanFactoryPostProcessor一樣在BeanFactory的初始化過程當中起着重要的做用

BeanFactoryPostProcessor的定義很是簡單,其postProcessBeanFactory方法容許在bean實例化前對BeanFactory作一些額外的設置

public interface BeanFactoryPostProcessor {
    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

最典型的BeanFactoryPostProcessor即是PropertyResourceConfigurer

// org.springframework.beans.factory.config.PropertyResourceConfigurer
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  // SpringBoot中默認讀取application.properties/yml及application-[env].properties/yml等並作合併
  Properties mergedProps = mergeProperties();
  convertProperties(mergedProps);
  // 將Properties封裝爲PlaceholderResolvingStringValueResolver
  // 註冊到AbstractBeanFactory#embeddedValueResolvers
  processProperties(beanFactory, mergedProps);
}

PlaceholderResolvingStringValueResolver能夠作什麼?在注入依賴時處理${ ... }類型的值!何時調用?再再次回到解析依賴的值時的DefaultListableBeanFactory#doResolveDependency(參考[spring-framework] [3] Bean是如何被建立的

Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
    if (value instanceof String) {
    // 使用StringValueResolvers對特殊類型的字符串進行解析,如 ${ ... }
        String strVal = resolveEmbeddedValue((String) value);
        BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                getMergedBeanDefinition(beanName) : null);
    // 使用StandardBeanExpressionResolve,利用SpEL,對 #{ ... } 類型的值進行解析
        value = evaluateBeanDefinitionString(strVal, bd);
    }
  TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  // 使用PropertyEditors進行類型轉換
    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
Q: 如何自定義 StringValueResolver並註冊,解析特殊場景的字符串?

那相似PropertyResourceConfigurer之類的BeanFactoryPostProcessor,其postProcessBeanFactory方法是在何時調用的?

回到AbstractApplicationContext#invokeBeanFactoryPostProcessors,其代碼雖然比較長,但內部邏輯比較清晰

Flowchart.invokeBeanFactoryPostProcessors

這裏涉及到兩種類型,BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor前者爲後者的子類,BeanDefinitionRegistryPostProcessors提供了額外的接口postProcessBeanDefinitionRegistry,用於更加方便地動態地註冊額外的BeanDefinition,如讀取配置文件(json、properties、yml)並解析(或者任何其餘的形式),並經過該接口註冊相應的BeanDefinition,基於Spring Boot Starter的不少框架均使用該方式進行bean的註冊

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
Spring Boot提供了衆多開箱即用的starter,如spring-boot-starter-data-jpa,只須要在配置文件中設置spring.datasource.*,即可以直接注入 DataSourceJdbcTemplate等bean

Q: 在Spring Boot中,各類starter是如何經過配置文件實現bean的動態註冊的?

Q: 除了使用BeanDefinitionRegistryPostProcessor實現BeanDefinition的動態註冊外,還有沒有其餘的方式?ImportBeanDefinitionRegistrar的實現邏輯是怎樣的?

以上流程圖能夠看出,優先執行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry,再執行BeanFactoryPostProcessor#postProcessBeanFactory,各自內部優先執行PriorityOrdered實現,再執行Ordered實現,最後執行無任何排序的實現

註冊BeanPostProcessor

AbstractApplicationContext#registerBeanPostProcessors

PostProcessorRegistrationDelegate#registerBeanPostProcessors

先來認識一下BeanPostProcessor

public interface BeanPostProcessor {
    /**
     * Apply this BeanPostProcessor to the given new bean instance before any bean
     * initialization callbacks (like InitializingBean's afterPropertiesSet
     * or a custom init-method). 
     * The returned bean instance may be a wrapper around the original.
     */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * Apply this BeanPostProcessor to the given new bean instance after any bean
     * initialization callbacks (like InitializingBean's afterPropertiesSet
     * or a custom init-method).
     * The returned bean instance may be a wrapper around the original.
     */
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
  1. postProcessBeforeInitialization方法在調用bean的init-method以前執行
  2. postProcessAfterInitialization方法在調用bean的init-method以後執行
  3. 任何一個方法可對現有bean實例作進一步的修改
  4. 任何一個方法可返回新的bean實例,用來替代現有的bean實例

詳見[spring-framework] [3] Bean是如何被建立的

尤爲是第4點,AOP既是使用該特性在postProcessAfterInitialization方法中生成當前bean實例的代理

除此以外還有一個特殊的BeanPostProcessor - InstantiationAwareBeanPostProcessor,其一樣有兩個方法,一個在建立bean實例以前調用,一個在建立bean實例以後、屬性注入以前調用,其具體調用邏輯參見[spring-framework] [3] Bean是如何被建立的

回到AbstractApplicationContext#registerBeanPostProcessors,其內部邏輯與BeanFactoryPostProcessor的註冊邏輯相似,這裏再也不畫流程圖

  1. 找到全部BeanPostProcessor並實例化
  2. 按照實現的Ordered接口分別放入priorityOrderedPostProcessors、orderedPostProcessors、nonOrderedPostProcessors並各自排序
  3. 若是實現了MergedBeanDefinitionPostProcessor則放入internalPostProcessors並排序
  4. 按順序依次註冊priorityOrderedPostProcessors、orderedPostProcessors、nonOrderedPostProcessors
  5. 最後註冊internalPostProcessors

MergedBeanDefinitionPostProcessor是什麼?其有一個接口postProcessMergedBeanDefinition,在bean實例化完成後屬性注入以前被調用,能夠用來對當前的BeanDefinition作進一步的修改,如增長PropertyValue等,實現特殊的屬性依賴注入,可參考[spring-framework] [3] Bean是如何被建立的AutowiredAnnotationBeanPostProcessor

Q: @Value @Autowired @Inject等註解是如何處理的?

參考 InstantiationAwareBeanPostProcessor#postProcessProperties

  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor

初始化MessageSource

AbstractApplicationContext#initMessageSource

這裏不詳細展開,Spring的MessageSource提供了國際化能力,在開發者未註冊MessageSource的狀況下Spring會提供一個默認的DelegatingMessageSource

初始化ApplicationEventMulticaster

AbstractApplicationContext#initApplicationEventMulticaster

Spring提供了一套事件(ApplicationEvent)的發佈&訂閱機制,開發者可自定義事件(繼承ApplicationEvent),註冊事件監聽器來訂閱消費事件(實現ApplicationListener或使用@EventListener註解),並使用ApplicationEventPublisher(直接依賴注入或者使用ApplicationEventPublisherAware)發送事件,使用示例可參考https://www.baeldung.com/spri...

其實ApplicationContext實現了ApplicationEventPublisher,跟蹤其publishEvent方法會發現,最終調用了AbstractApplicationContext#applicationEventMulticaster.multicastEvent,開發者能夠自行註冊一個ApplicationEventMulticaster,若是沒有Spring會提供一個默認的SimpleApplicationEventMulticaster

SimpleApplicationEventMulticaster#multicastEvent的邏輯比較簡單,會根據事件的類型找到能夠處理的全部ApplicationListener,依次調用它們的onApplicationEvent方法消費事件

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
      // 設置了executor,則異步執行
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
      // 不然同步執行
            invokeListener(listener, event);
        }
    }
}

這裏有一個比較有意思的地方,默認狀況下會同步、順序的調用listeners的onApplicationEvent方法,只有設置了executor纔會異步調用,不過這樣的控制粒度比較粗,要麼所有同步消費要麼所有異步消費,比較細粒度的控制事件的消費有幾種經常使用方法

  1. 使用@Async註解,獨立控制某一listener異步消費(https://www.baeldung.com/spri...
  2. 自行編碼,將onApplicationEvent邏輯放在線程中執行
  3. 註冊自定義的ApplicationEventMulticaster,內部實現本身的同步、異步Event處理邏輯

註冊ApplicationListener

AbstractApplicationContext#registerListeners

這裏的邏輯比較簡單

  1. 在BeanFactory中找到ApplicationListener類型的bean並實例化
  2. 調用ApplicationEventMulticaster#addApplicationListenerBean方法將ApplicationListeners註冊進去

初始化全部非Lazy Bean

AbstractApplicationContext#finishBeanFactoryInitialization

對於Singleton Bean而言,實例化發生在首次getBean,但你是否有疑惑,咱們只是註冊了衆多Singleton Bean,但在Spring初始化完成後全部的Singleton Bean(Lazy Bean除外)均已經完成實例化

回到AbstractApplicationContext#finishBeanFactoryInitialization,該函數會實現幾個邏輯

  1. 若是自定義了ConversionService(另外一種注入類型轉換的方式)類型bean且bean-name爲conversionService,則將其註冊到BeanFactory中
  2. 若是BeanFactory中不存在EmbeddedValueResolverPropertyResourceConfigurer會註冊一個PlaceholderResolvingStringValueResolver到BeanFactory中),則會註冊一個默認的StringValueResolver用來處理 ${ ... }類型的值(Environment#resolvePlaceholders
  3. 找到全部非Lazy的Singleton BeanDefinition進行實例化(getBean

    1. 若是是FactoryBean,則在bean name前加上’&’,並實例化該FactoryBean,隨後實例化真實的bean
    2. 若是不是FactoryBean,則直接實例化該bean

Refresh的後續動做

AbstractApplicationContext#finishRefresh

這裏,除了一些中間狀態須要清理外,還有兩件比較特殊的地方

LifecycleProcessor

AbstractApplicationContext#initLifecycleProcessor

Spring提供了LifecycleProcessor用於監聽BeanFactory的refresh及close,在BeanFactory的各階段會調用LifecycleProcessoronFreshonClose方法

開發者能夠自行註冊LifecycleProcessor類型的bean,bean-name必須爲「lifecycleProcessor」,不然Spring會提供一個默認的DefaultLifecycleProcessor

以後則會觸發LifecycleProcessoronFresh方法

除此以外,還能夠監聽 ContextRefreshedEventContextClosedEvent消息

refresh事件

在BeanFactory初始化完成後,則會發出ContextRefreshedEvent事件

BeanFactory的銷燬

AbstractApplicationContext#registerShutdownHook

該函數用來註冊BeanFactory的銷燬邏輯

public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        // No shutdown hook registered yet.
        this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

其直接使用了java的addShutdownHook函數,在jvm進程正常退出的時候觸發

AbstractApplicationContext#doClose函數定義了BeanFactory具體的銷燬過程

  1. 發出ContextClosedEvent事件
  2. 觸發LifecycleProcessoronClose方法
  3. 銷燬bean,細節參考[spring-framework] [3] Bean是如何被建立的
  4. 由子類實現的AbstractApplicationContext#closeBeanFactoryAbstractApplicationContext#onClose方法
Q: shutdownHook的註冊邏輯由誰調用?

至此,咱們理解了Spring BeanFactory的生命週期及Spring提供給開發者的(默認)擴展

小結

  • BeanDefinition的加載在AbstractApplicationContext#obtainFreshBeanFactory中實現
  • #{ ... }類型值的解析由StandardBeanExpressionResolve實現
  • ${ ... }類型值的解析由PlaceholderResolvingStringValueResolver實現
  • Spring提供了衆多默認的PropertyEditor,若須要自定義PropertyEditor能夠經過註冊CustomEditorConfigurer實現
  • Spring提供了衆多Aware,若須要自定義Aware能夠經過BeanPostProcessor實現
  • BeanFactoryPostProcessor用於在實例化bean以前對BeanFactory作額外的動做

    如,PropertyResourceConfigurer用來將PlaceholderResolvingStringValueResolver註冊到BeanFactory的embeddedValueResolvers中

  • BeanDefinitionRegistryPostProcessor用於在實例化bean以前(動態)註冊額外的BeanDefinition
  • BeanPostProcessor用於在調用bean的init-method先後,對實例化完成的bean作一些額外的干預

    如,CommonAnnotationBeanPostProcessor用來處理@PostConstructor,AbstractAdvisingBeanPostProcessor用來實現AOP


訂閱號

相關文章
相關標籤/搜索