SpringBoot starter機制java
SpringBoot由衆多Starter組成(一系列的自動化配置的starter插件),SpringBoot之因此流行,也是由於starter。web
starter是SpringBoot很是重要的一部分,能夠理解爲一個可拔插式的插件,正是這些starter使得使用某個功能的開發者不須要關注各類依賴庫的處理,不須要具體的配置信息,由Spring Boot自動經過classpath路徑下的類發現須要的Bean,並織入相應的Bean。redis
例如,你想使用Reids插件,那麼可使用spring-boot-starter-redis;若是想使用MongoDB,可使用spring-boot-starter-data-mongodbspring
爲何要自定義startermongodb
開發過程當中,常常會有一些獨立於業務以外的配置模塊。若是咱們將這些可獨立於業務代碼以外的功能配置模塊封裝成一個個starter,複用的時候只須要將其在pom中引用依賴便可,SpringBoot爲咱們完成自動裝配設計模式
自定義starter的命名規則app
SpringBoot提供的starter以spring-boot-starter-xxx
的方式命名的。官方建議自定義的starter使用xxx-spring-boot-starter
命名規則。以區分SpringBoot生態提供的starterless
整個過程分爲兩部分:maven
首先,先完成自定義starteride
(1)新建maven jar工程,工程名爲zdy-spring-boot-starter,導入依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.2.2.RELEASE</version> </dependency> </dependencies>
(2)編寫javaBean
@EnableConfigurationProperties(SimpleBean.class) @ConfigurationProperties(prefix = "simplebean") public class SimpleBean { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "SimpleBean{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
(3)編寫配置類MyAutoConfiguration
@Configuration @ConditionalOnClass //@ConditionalOnClass:當類路徑classpath下有指定的類的狀況下進行自動配置 public class MyAutoConfiguration { static { System.out.println("MyAutoConfiguration init...."); } @Bean public SimpleBean simpleBean(){ return new SimpleBean(); } }
(4)resources下建立/META-INF/spring.factories
注意:META-INF是本身手動建立的目錄,spring.factories也是手動建立的文件,在該文件中配置本身的自動配置類
<img
src="./images/image-20200111123116471.png"
alt="image-20200111123116471" style="zoom:67%;" />
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.lagou.config.MyAutoConfiguration
使用自定義starter
(1)導入自定義starter的依賴
<dependency> <groupId>com.lagou</groupId> <artifactId>zdy-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
(2)在全局配置文件中配置屬性值
simplebean.id=1 simplebean.name=自定義starter
(3)編寫測試方法
//測試自定義starter @Autowired private SimpleBean simpleBean; @Test public void zdyStarterTest(){ System.out.println(simpleBean); }
2.4 執行原理
每一個Spring Boot項目都有一個主程序啓動類,在主程序啓動類中有一個啓動項目的main()方法,在該方法中經過執行SpringApplication.run()便可啓動整個Spring Boot程序。
問題:那麼SpringApplication.run()方法究竟是如何作到啓動Spring Boot項目的呢?
下面咱們查看run()方法內部的源碼,核心代碼具體以下:
@SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); } }
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
從上述源碼能夠看出,SpringApplication.run()方法內部執行了兩個操做,分別是SpringApplication實例的初始化建立和調用run()啓動項目,這兩個階段的實現具體說明以下
1.SpringApplication實例的初始化建立
查看SpringApplication實例對象初始化建立的源碼信息,核心代碼具體以下
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //把項目啓動類.class設置爲屬性存儲起來 this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); //判斷當前webApplicationType應用的類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 設置初始化器(Initializer),最後會調用這些初始化器 this.setInitializers(this.getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 設置監聽器(Listener) this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 用於推斷並設置項目main()方法啓動的主程序啓動類 this.mainApplicationClass = this.deduceMainApplicationClass();
從上述源碼能夠看出,SpringApplication的初始化過程主要包括4部分,具體說明以下。
(1)this.webApplicationType
= WebApplicationType.deduceFromClasspath()
用於判斷當前webApplicationType應用的類型。deduceFromClasspath()方法用於查看Classpath類路徑下是否存在某個特徵類,從而判斷當前webApplicationType類型是SERVLET應用(Spring 5以前的傳統MVC應用)仍是REACTIVE應用(Spring
5開始出現的WebFlux交互式應用)
(2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
用於SpringApplication應用的初始化器設置。在初始化器設置過程當中,會使用Spring類加載器SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores文件中獲取全部可用的應用初始化器類ApplicationContextInitializer。
(3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
用於SpringApplication應用的監聽器設置。監聽器設置的過程與上一步初始化器設置的過程基本同樣,也是使用SpringFactoriesLoader從META-INF/spring.factories類路徑下的META-INF下的spring.factores文件中獲取全部可用的監聽器類ApplicationListener。
(4)this.mainApplicationClass
= this.deduceMainApplicationClass()
用於推斷並設置項目main()方法啓動的主程序啓動類
2.項目的初始化啓動
分析完(new
SpringApplication(primarySources)).run(args)源碼前一部分SpringApplication實例對象的初始化建立後,查看run(args)方法執行的項目初始化啓動過程,核心代碼具體以下:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); // 第一步:獲取並啓動監聽器 SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 第二步:根據SpringApplicationRunListeners以及參數來準備環境 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); // 準備Banner打印器 - 就是啓動Spring Boot的時候打印在console上的ASCII藝術字體 Banner printedBanner = this.printBanner(environment); // 第三步:建立Spring容器 context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context}); // 第四步:Spring容器前置處理 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 第五步:刷新容器 this.refreshContext(context); // 第六步:Spring容器後置處理 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if(this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)) .logStarted(this.getApplicationLog(), stopWatch); } // 第七步:發出結束執行的事件 listeners.started(context); // 返回容器 this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
從上述源碼能夠看出,項目初始化啓動過程大體包括如下部分:
this.getRunListeners(args)和listeners.starting()方法主要用於獲取SpringApplication實例初始化過程當中初始化的SpringApplicationRunListener監聽器並運行。
this.prepareEnvironment(listeners, applicationArguments)方法主要用於對項目運行環境進行預設置,同時經過this.configureIgnoreBeanInfo(environment)方法排除一些不須要的運行環境
根據webApplicationType進行判斷, 肯定容器類型,若是該類型爲SERVLET類型,會經過反射裝載對應的字節碼,也就是AnnotationConfigServletWebServerApplicationContext,接着使用以前初始化設置的context(應用上下文環境)、environment(項目運行環境)、listeners(運行監聽器)、applicationArguments(項目參數)和printedBanner(項目圖標信息)進行應用上下文的組裝配置,並刷新配置
這一步主要是在容器刷新以前的準備動做。設置容器環境,包括各類變量等等,其中包含一個很是關鍵的操做:將啓動類注入容器,爲後續開啓自動化配置奠基基礎
開啓刷新spring容器,經過refresh方法對整個IOC容器的初始化(包括bean資源的定位,解析,註冊等等),同時向JVM運行時註冊一個關機鉤子,在JVM關機時會關閉這個上下文,除非當時它已經關閉
擴展接口,設計模式中的模板方法,默認爲空實現。若是有自定義需求,能夠重寫該方法。好比打印一些啓動結束log,或者一些其它後置處理。
獲取EventPublishingRunListener監聽器,並執行其started方法,而且將建立的Spring容器傳進去了,建立一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的publishEvent方法,也就是說這裏是在Spring容器中發佈事件,並非在SpringApplication中發佈事件,和前面的starting是不一樣的,前面的starting是直接向SpringApplication中的監聽器發佈啓動事件。
用於調用項目中自定義的執行器XxxRunner類,使得在項目啓動完成後當即執行一些特定程序。其中,Spring Boot提供的執行器接口有ApplicationRunner 和CommandLineRunner兩種,在使用時只須要自定義一個執行器類實現其中一個接口並重寫對應的run()方法接口,而後Spring Boot項目啓動後會當即執行這些特定程序
上了拉勾教育的《Java工程師高薪訓練營》,作一下筆記。但願拉勾能給我推到想去的公司,目標:字節!!