终于搞明白Spring Boot自动配置的原理了

768 阅读2分钟

Spring Boot的自动配置是如何工作的?

当我们给应用类加上@EnableAutoConfiguration或者@SpringBootApplication注解时,Spring Boot会尝试推测需要哪些Bean,并配置这些Bean。自动配置会基于应用的classpath及自定义的Bean来配置。 比如, 如果classpath里包含tomcat-embedded.jar,会自动配置TomcatServletWebServerFactory Bean。如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

那么,Spring Boot的自动配置是如何工作的呢?因为@SpringBootApplication注解也是通过@EnableAutoConfiguration注解来完成自动配置的,所以来看一下@EnableAutoConfiguration的定义:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	......
}

@EnableAutoConfiguration定义的特殊之处也仅在这两个注解:

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage做了什么?

先来看一下@AutoConfigurationPackage,定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

对于@Import(AutoConfigurationPackages.Registrar.class),从字面上理解,导入了一个Registrar,这个Registrar实现了ImportBeanDefinitionRegistrar接口,将启动类所在的package存储为自动配置的package。如下:

/**
	 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
	 * configuration.
	 */
	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

@Import({AutoConfigurationImportSelector.class})做了什么?

回到@Import({AutoConfigurationImportSelector.class}),这个主要的方法是selectImports:

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

这个方法做了两件事:

  • AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader),加载 META-INF/spring-autoconfigure-metadata.properties文件,读取自动配置的Meta Data。
  • getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata),加载 META-INF/spring.factories文件,获取所有自动配置类。

META-INF/spring-autoconfigure-metadata.properties:

org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.neo4j.Neo4jBookmarkManagementConfiguration.Configuration=
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration=
org.springframework.boot.autoconfigure.kafka.KafkaAnnotationDrivenConfiguration.Configuration=
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.ConditionalOnClass=org.influxdb.InfluxDB
......

META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
......

这些所有的AutoConfiguration类都是要添加到Spring容器的组件,用于自动配置。

实例说明自动配置原理

以Gson的自动配置为例来说明下自动配置的原理, 在META-INF/spring.factories文件中可以看到Gson自动配置类:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
......

在META-INF/spring-autoconfigure-metadata.properties文件中可以看到Gson自动配置的Meta Data,指定当classpath中包含com.google.gson.Gson时才启动Gson的自动配置:

org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration.ConditionalOnClass=com.google.gson.Gson

对于GsonAutoConfiguration,可以看到与Meta Data配置对应的@ConditionalOnClass(Gson.class),以及@EnableConfigurationProperties(GsonProperties.class), 用于指定Gson有哪些配置项可以在文件中配置,比如application.yaml.

@Configuration
@ConditionalOnClass(Gson.class)
@EnableConfigurationProperties(GsonProperties.class)
public class GsonAutoConfiguration {
	......
}