SpringBoot是对Spring的一种扩展,其中比较重要的扩展功能就是自动装配:通过注解对常用的配置做默认配置,简化xml配置内容。本文会对Spring的自动配置的原理和部分源码进行解析,本文主要参考了Spring的官方文档。
自动装配的组件
SpringBoot自动装配通过多部分组件协调完成,这些组件主要有下面几种,这几种组件之间协调工作,最终完成了SpringBoot的自动装配。
@EnableAutoConfiguration:用于根据用户所引用的jar包自动装配Spring容器,比如用户在ClassPath中包含了HSQLDB,但是没有手动配置数据库连接,那么Spring会自动使用HSQLDB作为数据源。
@Condition:不同情况下按照条件进行装配,Spring的JdbcTemplate是不是在Classpath里面?如果是,并且DataSource也存在,就自动配置一个JdbcTemplate的Bean
@ComponentScan:扫描指定包下面的@Component注解的组件。
@EnableAutoConfiguration注解
Spring的自动装配发展大致可以分为三个阶段:
全手工配置的XML文件阶段,用户需要的Bean全部需要在XML文件中声明,用户手工管理全部的Bean。
半手工配置的注解阶段,用户可以安装需求Enable对应的功能模块,如添加@EnableWebMvc可以启用MVC功能。
全自动配置的SpringBoot,用户只需要引入对应的starter包,Spring会通过factories机制自动装配需要的模块。
全手工配置的XML文件示意图:
xml手工装配
半自动注解配置示意图:
半自动配置
全自动注解配置示意图:
全自动配置
Spring启用全自动配置功能的注解就是@EnableAutoConfiguration,应用添加了@EnableAutoConfiguration注解之后,会读取所有jar包下面的spring.factories文件,获取文件中配置的自动装配模块,然后去装配对应的模块。
@EnableAutoConfiguration的功能可总结为:使Spring启用factories机制导入各个starter模块的配置。
原理分析
通过上面的分析我们知道Spring的@EnableAutoConfiguration主要功能是使Spring启用factories机制导入各个starter模块的配置。下面我们会对@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 {};
}
@EnableAutoConfiguration注解的定义有两部分比较重要的内容:
@AutoConfigurationPackage:将添加该注解的类所在的package作为自动配置package进行管理。
@Import({AutoConfigurationImportSelector.class}):用于导入factories文件中的AutoConfiguration。
@Import({AutoConfigurationImportSelector.class})
首先我们需要知道@Import注解的作用,从字面意思就可以看出来,@Import用于把一个Bean注入到Spring的容器中,@Import可以导入三种类型的Bean:
导入普通的Bean,通常是@Configuration注解的Bean,也可以是任意的@Component组件类型的类。
导入实现了ImportSelector接口的Bean,ImportSelector接口可以根据注解信息导入需要的Bean。
导入实现了ImportBeanDefinitionRegistrar注解的Bean, ImportBeanDefinitionRegistrar接口可以直接向容器中注入指定的Bean。
@Import({AutoConfigurationImportSelector.class})中的AutoConfigurationImportSelector实现了ImportSelector接口,会按照注解内容去装载需要的Bean。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 获取需要自动装配的AutoConfiguration列表
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
// 获取自动装配类的类名称列表
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// 获取需要自动装配的AutoConfiguration列表
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取注解中的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取所有META-INF/spring.factories中的AutoConfiguration类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 删除重复的类
configurations = this.removeDuplicates(configurations);
// 获取注解中Execlud的类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
// 移除所有被Exclude的类
configurations.removeAll(exclusions);
// 使用META-INF/spring.factories中配置的过滤器
configurations = this.getConfigurationClassFilter().filter(configurations);
// 广播相关的事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回符合条件的配置类。
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
@AutoConfigurationPackage
@AutoConfigurationPackage用于将添加该注解的类所在的package作为自动配置package进行管理,听起来是不是和@ComponentScan功能有所重复?我们来分析一下其具体实现,可以看到这个注解依旧是通过@Import注解向容器中注册Bean。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@AutoConfigurationPackage注解导入了Registrar.class,其本质是一个ImportBeanDefinitionRegistrar,会把当前注解类所在的包注入到Spring容器中。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPack