Spring Boot當前版本爲2.2.0,之前一直對Spring Boot的各個模塊比較模糊,很難理解自動裝配等註解與容器啓動的關係等。但是後面梳理完了之後發現,Spring Boot只是在Spring的基礎上做了比較多的封裝和擴展,個人理解。 還是先看一下,
一、Spring Boot的特性
1、獨立運行Spring項目
之前的時候Spring只是作爲一個服務的一部分,或者說是容器。特別是之前的web項目是通過監聽Servlet服務器規範的ContextLoadeoaderListener(或者ContextLoaderPlugin)等方式,監聽服務器啓動後啓動Ioc容器。現在不論是jar還是war都可以使用java -jar xxx.jar方式啓動項目。
2、嵌入式Servlet服務器(如Tomcat)
獨立運行Spring項目,特別是web項目的情況下,使用內嵌的Servlet服務器(如Tomcat提供的內嵌版本)。首先是啓動Spring項目,然後在AbstractApplicationContext的refresh方法,在執行模板方法的onRefresh的擴展部分,調用createWebServer方法以創建Servlet服務器,是Spring與Tomcat角色的轉變。
3、提供starter簡化maven配置
Spring Boot提供了一系列的starter的maven依賴,特別是spring-boot-starter-parent。只需要一個基礎的依賴就能引入大部分的自動裝配的模塊(功能)。
4、自動裝配
自動裝配使用 @Import和ImportBeanDefinitionRegistrar回調接口的方式實現,那麼發生的時機是 SpringApplication.run執行時,會執行AbstractApplicationContext的refresh方法時執行。具體自動裝配的執行時機是在Spring源碼-ImportSelector實現分析或參見SpringIoc源碼(十)- ApplicationContext(六)- refresh(ConfigurationClassPostProcessor上)。
5、準生產的應用監控
Spring Boot在原有Spring的Environment的基礎上進行擴展出Actuator模塊,可以完成內存等的常規運營監控,特別適合小公司能快速搭建監控。
6、無代碼生成和xml配置
使用條件註解(@Conditional以及其擴展註解)和Java配置組合,不需要任何的xml配置就能實現所有配置。
在梳理完Spring的源碼後發現,其強大之處在於Ioc容器、ApplicationContext的生命週期、回調接口(BeanPostProcessor等),以及每個Bean的生命週期回調。生命週期也就意味着模板方法模式定義的回調,同樣Spring Boot也定義了自己的回調組件。
SpringApplicationRunListener、(貫穿了Spring Boot的整個啓動過程)
ApplicationContextInitializer(只是AbstractApplicationContext的準備階段)
ApplicationRunner、CommandLineRunner(環境參數相關回調,只是前者參數是被封裝過的,後者是main方法的原參數)
二、SpringApplication結構
1、主要字段
// @SpringBootApplication註解標註的類
private Set<Class<?>> primarySources;
// 註冊成Bean的全限定名,默認爲空後續會將primarySources添加進去
private Set<String> sources = new LinkedHashSet<>();
// main方法類,一般與primarySources相同
private Class<?> mainApplicationClass;
// 啓動Banner圖,默認會啓動SpringBootBanner
private Banner.Mode bannerMode = Banner.Mode.CONSOLE;
// 是否大於啓動的StopWatch的信息
private boolean logStartupInfo = true;
// 是否大於main方法啓動的args參數
private boolean addCommandLineProperties = true;
// 是否爲ApplicationContext添加ConversionService
private boolean addConversionService = true;
// Banner圖
private Banner banner;
// 類加載器,用於在ApplicationContext還沒有啓動時,加載類
// ApplicationContextInitializer等類型的類
private ResourceLoader resourceLoader;
// Bean的名稱生成器,否則走默認的名稱生成方式(第一個字母小寫的駝峯,多個大寫連着的除外)
private BeanNameGenerator beanNameGenerator;
// Spring的Environment
private ConfigurableEnvironment environment;
// ApplicationContext的子類類型
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
// Application類型,用於確認上面生成的applicationContextClass類型
private WebApplicationType webApplicationType;
// 配置headless的值
private boolean headless = true;
// 是否註冊鉤子
private boolean registerShutdownHook = true;
// 初始化器(後面會詳細分析)
private List<ApplicationContextInitializer<?>> initializers;
// 監聽(後面會詳細分析)
private List<ApplicationListener<?>> listeners;
// 默認配置,會疊加@ConfigurationProperties
private Map<String, Object> defaultProperties;
// Spring Profiles
private Set<String> additionalProfiles = new HashSet<>();
// 是否允許BeanDefinition覆蓋
private boolean allowBeanDefinitionOverriding;
// 是否自定義Environment,因爲Spring的ApplicationContext在refresh時會初始化
private boolean isCustomEnvironment = false;
// 是否加載LazyInitializationBeanFactoryPostProcessor類型的BeanPostProcessor
private boolean lazyInitialization = false;
2、構造器和run方法
// 1、類加載器、main方法類的Class
SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources);
// 2、main方法類的Class
SpringApplication(Class<?>... primarySources);
首先構造器可以傳入類加載器,一般都不傳入,則在啓動時候使用當前線程進行獲取類加載器,後續主要用於加載ApplicationContextInitializer和ApplicationListener等類型的類。
primarySources爲@SpringBootApplication註解標記的類,主要用於後續需要使用該類的包(作爲基準包)進行@Conponent的掃描。以及判斷當前類是否爲@Conponent,是的話則註冊爲Bean。
// main方法類的Class、main方法啓動參數
run(Class<?> primarySource, String... args);
run(Class<?>[] primarySources, String[] args);
run(String... args);
如果構造器沒有傳入primarySource參數,則在run方法時還可以傳入。但是run方法執行時,一定需要傳入args,很多地方會進行使用,並且會進行封裝成ApplicationArguments。若不需要使用自動掃描基礎包下的@Component的功能,則可以調用方法(不建議):
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class<?>[0], args);
}