在Spring Boot 中我们想要使用某个功能只需要在POM文件中添加对应的依赖。然后整个应用程序便具备了这个功能。那么这是如何实现的呢?
通过分析Spring Boot 示例程序来分析Spring Boot 自动配置原理。
1、Spring Boot自动配置原理
Spring Boot 应用启动的时候,需要传入一个标注@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 {
//省略
}
1.1、@SpringBootConfiguration注解
该注解是Spring Boot 对Spring 的@Configuration注解的一个包装,它的作用就是表名被标注的类是一个配置类!这里就不在过多描述,需要了解可以通过查看官方文档。
1.2、@EnableAutoConfiguration注解
该注解开启了自动配置功能,它也是一个组合注解。
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage注解 配和 @ComponentScan 来决定Spring Boot 从那个包开始扫描组件,有怎样的扫描规则等等。 我们现在主要关心@Import(EnableAutoConfigurationImportSelector.class)
给应用带来了什么
1.3、分析@Import注解
@Import
注解的作用,已经在给IOC容器添加组件的几种方式总结 做过介绍分析。 直接点击EnableAutoConfigurationImportSelector
观察,该类继承了AutoConfigurationImportSelector
,关注这个类的selectImports
方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取所有的候选配置信息
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
观察getCandidateConfigurations
方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
观察getSpringFactoriesLoaderFactoryClass
方法,该方法返回了EnableAutoConfiguration
的类类型
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
返回到getCandidateConfigurations
这个方法,观察loadFactoryNames
方法
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
//获取EnableAutoConfiguration全限定名称
String factoryClassName = factoryClass.getName();
try {
//加载 类路径下 META-INF文件夹中的spring.factories文件
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//将spring.factories文件转为Properties文件
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//读取key为EnableAutoConfiguration全限定名称对应的所有值
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
}
}
这个常量FACTORIES_RESOURCE_LOCATION的值为:META-INF/spring.factories
1.4、查看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,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
## 省略
再返回到selectImports
方法。实际上就是将这个配置文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的值,按照一定的过滤规则,最后把他们交给了容器。 每一个这样的xxxAutoConfiguration类都是容器中的一个组件,并且它们都是一个配置类,当这些组件交给Spring容器管理,也就使得应用程序完成了自动配置。
1.5、扩展
分析完上述内容,就可以自定义一个应用场景启动器。具体步骤如下
- 创建Maven工程,引入Spring Boot提供的最基本的配置
- 创建自定义配置类
- 在类路径下创建META-INF文件夹,在该文件夹中创建
spring.factories
文件。 - 在该文件中添加
org.springframework.boot.autoconfigure.EnableAutoConfiguration
=自定义配置类的全限定名称 - 打包,完成