上篇文章咱們深刻分析了SpringBoot的一站式啓動流程。而後咱們知道SpringBoot的主要功能都是依靠它內部不少的擴展點來完成的,那毋容置疑,這些擴展點是咱們應該深刻了解的,那麼本次且聽我一一道來SpringBoot的各種擴展點。java
下面咱們就一一爲你們來解析這些必須的擴展點:spring
從命名咱們就能夠知道它是一個監聽者,那縱觀整個啓動流程咱們會發現,它實際上是用來在整個啓動流程中接收不一樣執行點事件通知的監聽者。源碼以下:springboot
public interface SpringApplicationRunListener {
void starting();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void finished(ConfigurableApplicationContext context, Throwable exception);
}
對於開發者來講,基本沒有什麼常見的場景要求咱們必須實現一個自定義的SpringApplicationRunListener,即便是SpringBoot中也只默認實現了一個org.springframework.boot.context.eventEventPublishingRunListener
, 用來在SpringBoot的整個啓動流程中的不一樣時間點發布不一樣類型的應用事件(SpringApplicationEvent)。那些對這些應用事件感興趣的ApplicationListener能夠接受並處理(這也解釋了爲何在SpringApplication實例化的時候加載了一批ApplicationListener,但在run方法執行的過程當中並無被使用)。app
若是咱們真的在實際場景中自定義實現SpringApplicationRunListener,有一個點須要注意:任何一個SpringApplicationRunListener實現類的構造方法都須要有兩個構造參數,一個參數的類型就是咱們的org.springframework.boot.SpringApplication,另一個參數就是args參數列表的String[]:框架
package com.hafiz.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* @author hafiz.zhang
* @description:
* @date Created in 2018/6/7 20:07.
*/
public class DemoSpringApplicationRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
public DemoSpringApplicationRunListener(SpringApplication sa, String[] args) {
this.application = sa;
this.args = args;
}
@Override
public void starting() {
System.out.println("自定義starting");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("自定義environmentPrepared");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("自定義contextPrepared");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("自定義contextLoaded");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("自定義finished");
}
}
接着,咱們還要知足SpringFactoriesLoader的約定,在當前SpringBoot項目的classpath下新建META-INF目錄,並在該目錄下新建spring.fatories文件,文件內容以下:ide
org.springframework.boot.SpringApplicationRunListener=\
com.hafiz.springbootdemo.DemoSpringApplicationRunListener
ApplicationListener不是新東西,它屬於Spring框架對Java中實現的監聽者模式的一種框架實現,這裏須要注意的是:對於剛接觸SpringBoot,可是對於Spring框架自己又沒有過多地接觸的開發人員來講,可能會將這個名字與SpringApplicationRunListener弄混。ui
若是咱們有須要爲SpringBoot應用添加咱們自定義的ApplicationListener,那麼有兩種方式:this
經過SpringApplication.addListeners(…)或者SpringApplication.setListener(…)方法添加一個或者多個自定義的ApplicationListener。spa
藉助SpringFactoriesLoader機制,在SpringBoot的項目自定義的META-INF/spring.factories文件中添加配置(如下是SpringBoot默認的ApplicationListener配置):設計
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener
這貨也是Spring框架原有的東西,這個類的主要做用就是在ConfigurableApplicationContext類型(或者子類型)的ApplicationContext作refresh以前,容許咱們對ConfiurableApplicationContext的實例作進一步的設置和處理。
咱們要實現一個自定義的ApplicationContextInitializer也很簡單,它只有一個方法須要咱們的自定義類實現:
package com.hafiz.springbootdemo;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author hafiz.zhang
* @description:
* @date Created in 2018/6/7 20:33.
*/
public class DemoApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("自定義DemoApplicationContextInitializer的initialize方法");
}
}
而後也須要在項目自定義的META-INF/spring.factories文件中註冊:
org.springframework.context.ApplicationContextInitializer=\
com.hafiz.springbootdemo.DemoApplicationContextInitializer
不過咱們通常狀況下是不須要自定義一個ApplicationContextInitializer,即便SpringBoot框架默認也只有如下四個實現而已:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
CommandLineRunner並非Spring框架原有的概念,它屬於SpringBoot應用特定的回調擴展接口:
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
關於這貨,咱們須要關注的點有兩個:
跟其餘幾個擴展點接口類型類似,咱們建議CommandLineRunner的實現類使用@org.springframework.core.annotation.Order進行標註或者實現org.springframework.core.Ordered
接口,便於對他們的執行順序進行排序調整,這是很是有必要的,由於咱們不但願不合適的CommandLineRunner實現類阻塞了後面其餘CommandLineRunner的執行。這個接口很是有用和重要,咱們須要重點關注。
上篇文章咱們知道了,@EnableAutoConfiguration
藉助SpringFactoriesLoader能夠將標註了@Configuration
這個註解的JavaConfig類一併彙總並加載到最終的ApplicationContext,這麼說只是很簡單的解釋,其實基於@EnableAutoConfiguration
的自動配置功能擁有很是強大的調控能力。好比咱們能夠經過配合基於條件的配置能力或定製化加載順序,對自動化配置進行更加細粒度的調整和控制。
這個基於條件的自動配置來源於Spring框架中的"基於條件的配置"特性。在Spring框架中,咱們可使用@Conditional
這個註解配合@Configuration
或@Bean
等註解來干預一個配置或bean定義是否可以生效,它最終實現的效果或者語義類以下僞代碼:
if (複合@Conditional規定的條件) {
加載當前配置(Enable Current Configuration)或者註冊當前bean定義;
}
要實現基於條件的配置,咱們須要經過@Conditional
註解指定本身Condition實現類就能夠了(能夠應用於類型Type的註解或者方法Method的註解)
@Conditional({DemoCondition1.class, DemoCondition2.class})
最重要的是,@Conditional
註解能夠做爲一個Meta Annotaion用來標註其餘註解實現類,從而構建各類複合註解,好比SpringBoot的autoconfigre模塊就基於這一優良的革命傳統,實現了一批這樣的註解(在org.springframework.boot.autoconfigure.condition包下):
有了這些複合Annotation的配合,咱們就能夠結合@EnableAutoConfiguration實現基於條件的自動配置了。其實說白了,SpringBoot可以如此的盛行,很重要的一部分就是它默認提供了一系列自動配置的依賴模塊,而這些依賴模塊都是基於以上的@Conditional複合註解實現的,這也就說明這些全部的依賴模塊都是按需加載的,只有複合某些特定的條件,這些依賴模塊纔會生效,這也解釋了爲何自動配置是「智能」的。
在實現自動配置的過程當中,咱們除了能夠提供基於條件的配置以外,咱們還能對當前要提供的配置或組件的加載順序進行個性化調整,以便讓這些配置或者組件之間的依賴分析和組裝可以順利完成。
最經典的是咱們能夠經過使用@org.springframework.boot.autoconfigure.AutoConfigureBefore
或者@org.springframework.boot.autoconfigure.AutoConfigureAfter
讓當前配置或者組件在某個其餘組件以前或者以後進行配置。例如,假如咱們但願某些JMX操做相關的bean定義在MBeanServer配置完成之後在進行配置,那咱們就能夠提供以下配置:
@Configuration
@AutoConfigureAfter(JmxAutoConfiguration.class)
public class AfterMBeanServerReadyConfiguration {
@Autowired
MBeanServer mBeanServer;
// 經過@Bean添加其餘必要的bean定義
}
截至目前,咱們已經完成了對SpringBoot的核心組件一一解析,總結來講,SpringBoot中大部分東西都是Spring框架中已經存在原有概念和實踐方式,SpringBoot只是在這基礎上對特定的場景進行定製、固化以及升級。而後正式這些固化升級讓咱們感覺到了SpringBoot開發的便捷以及高效。
而後,你會忽然發現,SpringBoot原來是這麼的簡單,其中並沒有任何祕密。可是Spring團隊經過對Spring應用的固化和升級,讓SpringBoot能夠完成開發只關注業務邏輯開發的神奇事件,整個開發過程更加高效以及簡單,可是對於SpringBoot的設計者來講,他們並無耗費巨大的精力。另外一方面來講,開箱即用纔是將來發展的趨勢,"約定大於配置(Convention Over Configuration)"也必將統領江山!