SpringBoot原理分析
1.起步依賴原理分析
1)分析spring-boot-starter-parent
按住Ctrl點選pom.xml中的spring-boot-starter-parent,
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.6.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
可以看到了spring-boot-starter-parent的pom.xml。
發現它還有一個parent
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.6.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
而此parent有一個<properties>標籤
<properties> <activemq.version>5.15.6</activemq.version> <antlr2.version>2.7.7</antlr2.version> <appengine-sdk.version>1.9.66</appengine-sdk.version> <artemis.version>2.4.0</artemis.version> <aspectj.version>1.8.13</aspectj.version> <assertj.version>3.9.1</assertj.version> <atomikos.version>4.0.6</atomikos.version> <bitronix.version>2.1.4</bitronix.version> <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version> <byte-buddy.version>1.7.11</byte-buddy.version> ...省略
此標籤裡有很多的<xxx.version>,便是maven的版本控制,當我們給與spring-boot-starter-parent版本後,它會給相關的座標鎖定版本。而spring的缺點就是因為版本不一致導致jar包衝突,SpringBoot直接給我們鎖定了相關jar包的版本,也就避免的這個問題。
而spring-boot-dependencies.xml檔案中還有一個<dependencyManagement>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.0.6.RELEASE</version>
...省略
裡面包含autoconfigure,devtools...等依賴的管理
而build標籤中包含如下
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
表示springBoot讓我們配置的檔案是".yml",".yaml"和".properties",並以application開頭。
所以spring-boot-starter-parent作用主要是我們的SpringBoot工程繼承spring-boot-starter-parent後已經具備版本鎖定等配置,而起步依賴的作用就是進行依賴的傳遞。
2)分析spring-boot-starter-web
開啟spring-boot-starter-web.pom檔案:發現如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.0.6.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.6.RELEASE</version>
<scope>compile</scope>
</dependency>
開啟spring-boot-starter-json有如下座標:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.10.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
<scope>compile</scope>
</dependency>
這裡的jackson就是springMVC中轉換json所需要的jar包。
spring-boot-starter-web.pom檔案中spring-boot-starter-tomcat的座標便是tomcat相關功能,而檔案中包含了tomcat的相關座標。不僅如此,spring-boot-starter-web.pom檔案中還引入了spring-web和spring-webmvc的座標。所以當我們引入spring-boot-starter-web座標後工程自動將我們需要的spring環境進行了進入。這樣我們的工程只要引入spring-boot-starter-web起步依賴的座標就可以進行web開發了,同樣體現了依賴傳遞的作用。
2.自動配置原理解析
SpringBoot工程的入口是@SpringBootApplication,按住Ctrl點選此註解,打開發現如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...省略
而其中@SpringBootConfiguration檔案中有一個@Configuration註解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
而@Configuration表示該類為springg的一個配置類,即@SpringBootConfiguration和@Configuration作用是相同的。所以,當類上加上@SpringBootApplication註解後,它具備@Configuration的作用。可以看到@SpringBootApplication註解還包含@EnableAutoConfiguration註解和@ComponentScan,
而@ComponentScan是主鍵掃描,而它的規則是引導類(類上有@SpringBootApplication註解)所在的包及其子包都會掃描:
所以當我們配置了@Controller後,並沒有配置掃描包,一樣能掃描到。
@EnableAutoConfiguration這是一個自動配置註解,開啟檔案發現如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
裡面有一個@Import註解,表示當前註解引入AutoConfigurationImportSelector.class,所以 AutoConfigurationImportSelector有的功能 EnableAutoConfiguration也會存在。進入AutoConfigurationImportSelector,有一個selectImports()方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
其中List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);即載入配置,getCandidateConfigurations方法中如下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
SpringFactoriesLoader.loadFactoryNames 方法的作用就是從META-INF/spring.factories檔案中讀取指定類對應的類名稱列表,這裡就是AutoConfigurationImportSelector類所在的org.springframework.boot.autoconfigure,而“No auto configuration classes found in META-INF/spring.factories. If you...”說明在包下存在一個META-INF/spring.factories檔案
開啟spring.factories配置檔案存在大量的以Configuration為結尾的類名稱,這些類就是存有自動配置資訊的類,而 SpringApplication在獲取這些類名後再載入
例如:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
找到ServletWebServerFactoryAutoConfiguration類,有一個@EnableConfigurationProperties註解
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
開啟ServerProperties類,發現他有@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)註解;
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
private Boolean useForwardHeaders;
private String serverHeader;
private int maxHttpHeaderSize = 0; // bytes
private Duration connectionTimeout;
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private final Compression compression = new Compression();
@NestedConfigurationProperty
private final Http2 http2 = new Http2();
private final Servlet servlet = new Servlet();
private final Tomcat tomcat = new Tomcat();
private final Jetty jetty = new Jetty();
private final Undertow undertow = new Undertow();
其中,prefix = "server" 表示SpringBoot配置檔案中的字首,SpringBoot會將配置檔案中以server開始的屬性對映到該類的欄位中,而這個配置檔案就是和spring.factories在同一目錄下的spring-configuration-metadata.json檔案
開啟檔案可以看到如圖:
很明顯這裡就是工程啟動tomcat預設的埠地址。
所以當這些預設的配置配好後,會通過ServerProperties類進行載入,載入完後在ServletWebServerFactoryAutoConfiguration中進行引入(ServletWebServerFactoryAutoConfiguration類通過@EnableConfigurationProperties(ServerProperties.class)),引入完後AutoConfigurationImportSelector的 getCandidateConfigurations方法會去載入ServletWebServerFactoryAutoConfiguration的預設配置,這樣就配好了。當我們通過".yml",".yaml"和".properties"檔案進行配置事,就會將相同名稱的進行覆蓋,如上圖埠號name為server.port,配置server.port=xxxx,就覆蓋了。