SpringBoot的一大优势就是省去了很多的配置,当SpringBoot启动的时候,在内部进行了自动装配的工作,使得开发人员无需配置或者只需要很少的配置就能直接开发业务。
SpringBoot的自动装配
SpringBoot项目主函数入口的注解为SpringBootApplication,其定义如下:
@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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
这里面有三个重要的注解:
@Configuration(SpringBootConfiguration注解实质就是一个@Configuration)
@EnableAutoConfiguration
@ComponentScan
也就是说,在开发的时候,加上这三个注解,就等同于SpringBootApplication注解。
@Configuration
该注解表明这是一个配置类,相当于一个beans.xml文件。
@ComponentScan
该注解的功能是自动扫描并加载符合条件的组件或bean定义,并将其加入到Spring容器中。
@EnableAutoConfiguration
Spring中所有的@Enablexxx都是开启某一项功能的注解,比如@EnableScheduling表示开启spring定时任务。原理是借助@Import的帮助,将所有符合配置条件的bean加载到spring容器中。@EnableAutoConfiguration表示开启自动装配。
@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可以将所有符合条件的@Configuration配置都加载到当前的spring容器中。
自动装配原理
AutoConfigurationImportSelector类定义如下:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = {};
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;
@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());
}
...
}
可以看到,该类实现了很多的xxxAware接口和DeferredImportSelector接口,所有的Aware都优先于selectImports方法执行。
selectImports执行过程如下:
(1)首先通过loadMetadata方法加载“META-INF/spring-autoconfigure-metadata.properties"文件。
(2)再通过getAutoConfigurationEntry方法获取自动装配入口,下面是源码:
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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 new AutoConfigurationEntry(configurations, exclusions);
}
在getAutoConfigurationEntry方法中,先通过getAttributes方法获取注解的属性和值。
然后在getCandidateConfigurations方法中,通过工具类SpringFactoriesLoader的loadFactoryNames方法在所有的“META-INF/spring.factories”文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并将其封装到一个List中返回。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories中加载配置。将@EnableAutoConfiguration的完整类名作为查找的key,获取对应的一组@Configuration类。
总结
@EnableAutoConfiguration作用就是从classpath中加载有的的META-INF/spring.factories配置文件,然后以EnableAutoConfiguration的完整类名作为key来查找对应的配置项,再通过反射将其实例化为对应的标注了@Configuration的JavaConfig形式的IOC容器配置类,最后汇总为一个并加载IOC容器。
redis自动装配
下面以redis为例来说明自动装配过程。
查找spring.factories中的redis配置项
从spring-boot-autoconfigure-2.1.5.RELEASE.jar的spring.factories文件中找到redis相关配置类RedisAutoConfiguration。
一般来说,一个功能配置类会负责管理多个相关的功能类,如RedisAutoConfiguration就负责JedisConnectionFactory、RedisTemplate、StringRedisTemplate这三个功能类的创建。
从上图可以看到,RedisAutoConfiguration配置类生效有一个条件:@CondictionOnClass({RedisOperations.class}),也就是说在classpath路径下要有RedisOperations类存在。如果在pom.xml中引入了对应的jar包,就可以匹配到这个类。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
匹配成功后配置类生效,在该配置类中会自动注入默认的属性配置类@EnableConfigurationProperties(RedisProperties.class)
这个属性配置类会从配置文件中读取spring.redis开头的配置项。
最终,ReidsAutoConfiguration配置类会生成JedisConnectionFactory、RedisTemplate,并将加载到IOC容器中。
参考资料
[1]. https://www.jianshu.com/p/88eafeb3f351