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"文件进行配置事,就会将相同名称的进行覆盖(由于配置文件加载顺序是先加载".yml",再加载“.yaml”,然后加载“.properties”文件,所以当同时配了这几个文件后,“.properties”会覆盖前面的配置),如上图端口号name为server.port,配置server.port=xxxx,就覆盖了。