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

猜你喜欢

转载自blog.csdn.net/u010689849/article/details/83959881