SpringBoot 自动配置探究
SpringBoot 自动配置探究
pom.xml
当我们初始化一个 SpringBoot 工程,查看 pom.xml配置,发现有如下 依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5RELEASE</version>
</parent>
根据 maven 的继承依赖关系,点进去发现还会继续依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
再点进去可以看到最终依赖了spring-boot-dependencies
依赖文件,也就是说 SpringBoot的核心依赖在父工程中,这个文件定义了好多jar 包以及各个 Jar 包的 版本,我们写或引入一些 SpringBoot 依赖的时候不需要指定版本就因为有这些版本库。
注意我上面用的是 2.1.5.RELEASE 版本,可以直接查看依赖继承关系,如果是 2.2.2.RELEASE(最新版)点不了,改成从 spring-boot-starter-web 启动器进源码才可以查看
启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 启动器说白了就是 SpringBoot 的启动场景
- 比如 spring-boot-starter-web ,它就会帮我们自动导入 web 环境所有依赖
- SpringBoot 会将所有的功能场景,都变成一个个的启动器,我们需要使用什么功能,只需要找到对应的启动器就可以了
- 官网文档还罗列了好多其它启动器:https://docs.spring.io/spring-boot/docs/2.2.2.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
主程序
@SpringBootApplication
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
}
-
@SpringBootApplication 这个注解它就标注了这是一个 SpringBoot 的应用
-
@SpringBootApplication 是一个组合注解,点进去发现由多个注解组成
@SpringBootConfiguration @EnableAutoConfiguration //扫描当前启动类同等级的包 @ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class} ), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})}) public @interface SpringBootApplication { }
-
@SpringBootConfiguration 表明它是一个 SpringBoot 配置,查看源码发现核心注解由 @Configuration 组成,而 @Configuration 是个 Spring 注解,查看 @Configuration 源码发现最终是个 @Component (组件)
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor(annotation = Component.class) String value() default ""; }
-
@EnableAutoConfiguration 自动配置,查看其源码
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { }
@AutoConfigurationPackage 就是自动配置包,查看其源码,发现使用 @Import 引入了 Registrar 这个类
@Import({Registrar.class}) public @interface AutoConfigurationPackage { }
查看 Registrar 类源码
public abstract class AutoConfigurationPackages { private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class); private static final String BEAN = AutoConfigurationPackages.class.getName(); public AutoConfigurationPackages() { } public static boolean has(BeanFactory beanFactory) { return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty(); } public static List<String> get(BeanFactory beanFactory) { try { return ((AutoConfigurationPackages.BasePackages)beanFactory.getBean(BEAN, AutoConfigurationPackages.BasePackages.class)).get(); } catch (NoSuchBeanDefinitionException var2) { throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages"); } } public static void register(BeanDefinitionRegistry registry, String... packageNames) { if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(2); registry.registerBeanDefinition(BEAN, beanDefinition); } } static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() {} public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()); } public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata)); } } }
-
@Import(AutoConfigurationImportSelector.class) 则引入了 AutoConfigurationImportSelector 类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry(); private static final String[] NO_IMPORTS = new String[0]; private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; public AutoConfigurationImportSelector() { } //选择我们在 pom.xml 文件中引入的 jar 包或 组件 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } //获取自动配置实体 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); //获取候选配置 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } } //META-INF/spring.factories 是自动配置的核心文件 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; } //EnableAutoConfiguration 这个类就是我们上文分析过的自动导入配置类, //这样就可以获取到被这个注解标注的类的所有配置 //而 @SpringBootApplication 注解就继承了这个注解 //也即获取了主启动类加载的所有组件(绕了很远...) protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } }
关于spring.factories: 它是一个properties文件,它位于classpath:/META-INF/目录里面,每个jar包都可以有spring.factories的文件。Spring 提供工具类 SpringFactoriesLoader 负责加载、解析文件,如spring-boot-auto-configure-2.2.0.RELEASE.jar里面的META-INF目录里面就有spring.factories文件
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ .....
再看看 getCandidateConfigurations()方法中的 SpringFactoriesLoader.loadFactoryNames() 源码
public final class SpringFactoriesLoader { //loadFactoryNames方法获取所有的加载配置 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //加载项目资源 Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //遍历了所有的自动配置,封装成 properties 供我们使用 while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); 。。。。。。 } cache.put(classLoader, result); return result; } } } }
自动配置包
@ConditionalOnXXXX 注解
spring.factories 中那么多的配置类,有的并没有生效,需要导入对应的 starter 才能有作用,比如随便找个 AopAutoConfiguration 这个配置类,查看其源码,只有满足了 @ConditionalOnXXXX 注解里面的条件才会生效
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {"auto"},
havingValue = "true",
matchIfMissing = true
)
public class AopAutoConfiguration {
}
再比如的源码,只要满足 @ConditionalOnClass 条件即会生效
@Configuration( proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
}
yml 文件的配置是如何生效的?
根据上文分析,SpringBoot 会加载 spring.factories 的所有自动类,以 DataSourceAutoConfiguration 这个配置类来说,如果 pom 文件有对应的启动类,它就会生效,那么就会通过 @EnableConfigurationProperties 加载默认的配置文件,从源码可以看出默认的配置文件是 DataSourceProperties ,假如我们在 yml 中重新配置了某些属性,比如 jdbc.name、jdbc.username、jdbc.password…,SpringBoot 会读取这些属性并覆盖默认属性。
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
private ClassLoader classLoader;
private String name;
private boolean generateUniqueName;
private Class<? extends DataSource> type;
private String driverClassName;
private String url;
private String username;
private String password;
private String jndiName;
....
}
总结
SpringBoot 所有自动配置都是在启动的时候扫描并加载,spring.factories 所有的自动类配置都得了这里,但是不一定会生效,要判断条件是否成立(通过 @ConditionalOnXXXX 注解),只要导入了对应的 starter 就有对应的启动器了,有了启动器,自动装配就会生效,然后就配置成功。
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 以前我们需要自己配置的东西 , 自动配置类都帮我们解决了
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它将所有需要导入的组件以全类名的方式返回 , 这些组件就会被添加到容器中 ;
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
- 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
推荐阅读