Spring Boot是什麼
Spring Boot是有大名鼎鼎的Spring Framework的開發者設計的,旨在用來簡化基於Spring的應用開發。
Spring Boot特性
Spring Boot 設計原則
Spring Boot應用是什麼樣子的
使用Spring Initializr來快速生成一個Spring Boot應用,下面只列出最重要的部分。
- java
<code>@SpringBootApplication
public class DemoApplication {
\tpublic static void main(String[] args) {
\t\tSpringApplication.run(DemoApplication.class, args);
\t}
}/<code>
- pom.xml
<code>
<project>\txsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
\t<modelversion>4.0.0/<modelversion>
\t<parent>
\t\t<groupid>org.springframework.boot/<groupid>
\t\t<artifactid>spring-boot-starter-parent/<artifactid>
\t\t<version>2.2.4.RELEASE/<version>
\t\t<relativepath>
\t/<parent>
\t<groupid>com.example/<groupid>
\t<artifactid>demo/<artifactid>
\t<version>0.0.1-SNAPSHOT/<version>
\t<name>demo/<name>
\t<description>Demo project for Spring Boot/<description>
\t<properties>
\t\t<java.version>1.8/<java.version>
\t/<properties>
\t<dependencies>
\t\t<dependency>
\t\t\t<groupid>org.mybatis.spring.boot/<groupid>
\t\t\t<artifactid>mybatis-spring-boot-starter/<artifactid>
\t\t\t<version>2.1.1/<version>
\t\t/<dependency>
\t/<dependencies>
\t<build>
\t\t<plugins>
\t\t\t<plugin>
\t\t\t\t<groupid>org.springframework.boot/<groupid>
\t\t\t\t<artifactid>spring-boot-maven-plugin/<artifactid>
\t\t\t/<plugin>
\t\t/<plugins>
\t/<build>
/<project>/<code>
Spring Boot是如何簡化Spring 開發的
創建Spring應用,首先要創建對應的ApplicationContext。Spring Boot根據classpath下的Class會自動推斷創建什麼類型的ApplicationContext,開發者只需引入正確的依賴即可。
Spring Boot 如何加載Bean Definition到ApplicationContext
從應用的入口main方法開始來看Spring Boot如何加載Bean Definition的。
<code> public static void main(String[] args) {
\t\tSpringApplication.run(DemoApplication.class, args);
\t}
/<code>
SpringApplication是用來啟動基於Spring的application的。
第一個參數告訴Spring Boot從哪裡找到Bean Definition信息,第二個參數args是應用啟動時傳入的參數。
在這個例子中Bean Definition信息是DemoApplication.class, 為什麼它可以提供Bean Definition信息呢,因為它是@Configuration Class。任何有@Configuration標記的類都可以是Spring Bean Definition的來源。讓我們回顧一下我們的Demo。
<code>@SpringBootApplication
public class DemoApplication {
\tpublic static void main(String[] args) {
\t\tSpringApplication.run(DemoApplication.class, args);
\t}
}
/<code>
@SpringBootApplication在我們的入口類,看一下它的定義。
<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
\t\t@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
/<code>
@SpringBootApplication包含了@SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan,下面會逐一介紹。
@SpringBootConfiguration
<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
...
}
/<code>
它由@Configuraion標註。所以DemoApplication.class可以用來制定Bean Definition的信息。但是我們在這個類裡面沒有看到任何的Bean Definition信息。那spring boot如何找到Bean 定義信息呢。
@ComponentScan
@ComponentScan 是spring XML context:component-scan 元素對應的註解形式,它告訴Spring從哪裡找到@Component。注意@Configuration也是@Component, 所以@Configuration都可以被@ComponentScan識別。在沒有指定搜索路徑的情況下,Spring 會從該註解所標記的Class所屬的package來進行搜索。一個Spring Boot應用程序的典型佈局如下:
<code>com
+- example
+- myapplication
+- Application.java 應用程序入口
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
/<code>
@SpringBootApplication註解標記在Application.java上,這樣所有的@Configuration和@Component都能被識別到,然後加載到ApplicationContext,這也體現了約定大於配置的原則。
自動配置
自動配置的含義就是一旦啟用自動配置,一些Bean會自動的加載到ApplicationContext,供其他Bean來使用。 Spring Boot 自動配置的秘密都在EnableAutoConfiguration這裡,正如該註解名字所揭示的,它是用來啟用自動配置的。
<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
\t...
}
/<code>
為什麼它可以啟用自動配置呢, 關鍵就在於@Import。@Import與@Configuration結合來引入Bean Definition。@Import可以指定四種Bean Definition來源。
- @Configuration Class
- 實現ImportSelector接口的類,該接口的實現者決定哪些@Configuration Class要導入
- ImportBeanDefinitionRegistrar
- @Component
在@EnableAutoConfiguration中指定的AutoConfigurationImportSelector.class實現了ImportSelector。該類會掃描classpath 下所有的META-INF/spring.factories文件來找到以org.springframework.boot.autoconfigure.EnableAutoConfiguration為Key的配置項。該Key所對應的配置項就是要加載的@Configuarion class。以MyBatis為例,看看是如何配置的。
<code>org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
/<code>
Spring Boot 加載過程
<code>public ConfigurableApplicationContext run(String... args) {
\t\tStopWatch stopWatch = new StopWatch();
\t\tstopWatch.start();
\t\tConfigurableApplicationContext context = null;
\t\tCollection<springbootexceptionreporter> exceptionReporters = new ArrayList<>();
\t\tconfigureHeadlessProperty();
\t\t//加載所有的SpringApplicationRunListener
\t\tSpringApplicationRunListeners listeners = getRunListeners(args);
\t\t//通知SpringApplicationRunListener SpringBoot開始啟動了
\t\tlisteners.starting();
\t\ttry {
\t\t\t//封裝應用啟動參數
\t\t\tApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
\t\t\t//創建Environment,添加應用啟動參數到Environment中,通知environmentPrepared給SpringApplicationRunListener
\t\t\tConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
\t\t\tconfigureIgnoreBeanInfo(environment);
\t\t\tBanner printedBanner = printBanner(environment);
\t\t\t//創建Application Context
\t\t\tcontext = createApplicationContext();
\t\t\texceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
\t\t\t\t\tnew Class[] { ConfigurableApplicationContext.class }, context);
\t\t\t//關聯Enviromnent和ApplciationContext
\t\t\t//通知contextPrepared
\t\t\t//加載@Configuration class到Application Context
\t\t\t//通知contextLoaded\t\t
\t\t\tprepareContext(context, environment, listeners, applicationArguments, printedBanner);
\t\t\t//刷新Application Context,加載所有的non lazy Bean
\t\t\trefreshContext(context);
\t\t\tafterRefresh(context, applicationArguments);
\t\t\tstopWatch.stop();
\t\t\tif (this.logStartupInfo) {
\t\t\t\tnew StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
\t\t\t}
\t\t\t//通知Spring Boot started
\t\t\tlisteners.started(context);
\t\t\t//調用ApplicationRunner和CommandLineRunner
\t\t\tcallRunners(context, applicationArguments);
\t\t}
\t\tcatch (Throwable ex) {
\t\t\thandleRunFailure(context, ex, exceptionReporters, listeners);
\t\t\tthrow new IllegalStateException(ex);
\t\t}
\t\ttry {
\t\t\t//通知listener Spring Boot正在Running
\t\t\tlisteners.running(context);
\t\t}
\t\tcatch (Throwable ex) {
\t\t\thandleRunFailure(context, ex, exceptionReporters, null);
\t\t\tthrow new IllegalStateException(ex);
\t\t}
\t\treturn context;
\t}/<springbootexceptionreporter>/<code>
Spring Boot事件機制
在Spring Boot的加載過程中,SpringApplicationRunListener貫穿其中。每一個階段完成之後就會通知SpringApplicationRunListener。 SpringApplicationRunListener中定義了Spring Boot加載的幾個階段,如下:
- starting
- environmentPrepared
- contextPrepared
- contextLoaded
- started
- running
- failed
SpringApplicationRunListener的具體實現是EventPublishingRunListener。
<code>org.springframework.boot.SpringApplicationRunListener=\\
org.springframework.boot.context.event.EventPublishingRunListener
/<code>
在加載過程的每一階段,EventPublishingRunListener都會把當前階段進行封裝成ApplicationEvent,然後publish出去。這樣ApplicationEvent的監聽者ApplciationListener就可以參與SpringBoot的加載過程來實現任何想要實現的功能。
為了能夠監聽到Spring Boot加載的每一個階段,Spring Boot 從META-INF/spring.factories中加載ApplicationListener,而不是從Spring ApplicationContext中獲取到ApplicationListener,因為ApplicationContext中的ApplicationListener加載的比較晚。
<code># Application Listeners
org.springframework.context.ApplicationListener=\\
org.springframework.boot.ClearCachesApplicationListener,\\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\\
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.context.logging.ClasspathLoggingApplicationListener,\\
org.springframework.boot.context.logging.LoggingApplicationListener,\\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
/<code>
Spring Boot本身就是利用這個機制來實現了application.properties的加載,參見ConfigFileApplicationListener
Spring Boot Starter
Starter有兩個作用
- 引入合適的依賴到項目中
- 自動配置
例如項目中決定使用mybatis,就可以引入mybatis starter到項目中,這樣mybatis的依賴會自動添加到項目中。使用mybatis,SqlSessionFactory是必不可少的, Spring Boot的自動配置機制會自動加載SqlSessionFactory到ApplicationContext中。
閱讀更多 津赫 的文章