spring boot 的 auto-configuration 功能会根据你的应用程序所依赖的 pom 来进行自动配置。 例如,咱们在 pom 中添加
spring-boot-starter-web
的依赖,spring 就会帮咱们自动完成 spring mvc 相关的配置而不须要咱们手动来进行。咱们只须要将@EnableAutoConfiguration
或者@SpringBootApplication
注解标注在@Configuration
配置类上面便可启用自动装配。web
那么 auto-configuration 是如何工做的呢?带着问题,咱们经过阅读相关的源代码来一探究竟。spring
既然开启 auto-configuration 须要经过 @EnableAutoConfiguration
或 @SpringBootApplication
来驱动,那么咱们就从这两个注解着手。api
注:本文基于 spring boot 版本 1.5.12.RELEASE。数组
@SpringBootApplication
websocket
源码以下:mvc
@Target(ElementType.TYPE)
app
@Retention(RetentionPolicy.RUNTIME)
socket
@Documented
ide
@Inherited
spring-boot
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 省略
}
@SpringBootApplication
注解自己也标注了 @EnableAutoConfiguration
注解,因此自动装配最终仍是经过 @EnableAutoConfiguration
来启用。
@SpringBootApplication
还标注了 @ComponentScan
和 @SpringBootConfiguration
, @SpringBootConfiguration
上又标注 @Configuration
注解。
由于 @SpringBootApplication
将上述多个注解集成于一身,因此咱们只要在类上标注 @SpringBootApplication
就等价于同时添加了 @EnableAutoConfiguration
、 @ComponentScan
和 @Configuration
。
注:类上标注的注解(direct annotation)和注解上的注解(meta-annotation)之因此都能生效这和 spring 自己对注解的处理有关。
@SpringBootApplication
比较简单,看来再来看看 @EnableAutoConfiguration
。
@EnableAutoConfiguration
源码以下:
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@EnableAutoConfiguration
上标注了 @Import
,引入了 EnableAutoConfigurationImportSelector
。
EnableAutoConfigurationImportSelector
源码以下:
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
EnableAutoConfigurationImportSelector
源码并没能给咱们提供太多参考信息,只重写了 isEnabled
方法,咱们来看看他的基类 AutoConfigurationImportSelector
,源码以下:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
// 省略
}
咱们看到 AutoConfigurationImportSelector
实现了 DeferredImportSelector
接口(从 ImportSelector
派生)。 ImportSelector
源码以下:
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
selectImports
方法中定义了参数importingClassMetadata
,类型是AnnotationMetadata
,这是啥?咱们把
importingClassMetadata
这个词组分解一下,importing + class metadata:importing:动名词,强调的是干了 import 这件事的发起者(这里指配置类)
class metadata:类的元信息,什么元信息,注解元信息,即
AnnotationMetadata
。那么
selectImports
方法的逻辑,咱们能够这么描述:基于发起 import 操做的配置类(@Configuration class)的元信息进行运算并返回计算结果(class 名称数组)。
那么 spring 对返回结果中的 class 有没有什么特别要求呢?
实际上你能够返回任意的 class 名称,而不至于使程序出错,固然,你确定不会返回没有任何意义的 class 名称。笔者总结的返回结果分为下面两类:
@Configuration
配置类
ImportSelector
(或DeferredImportSelector
)实现类
ImportSelector
与DeferredImportSelector
的区别?
DeferredImportSelector
是ImportSelector
的变体,两者的触发前后顺序不一样,DeferredImportSelector
在全部的@Configuration
bean 都被处理了以后再进行处理,这与它的名称 deferred (推迟)很是贴合。题外话:经过以上内容,咱们能够看出 spring 在命名上很是讲究,代码阅读起来比较符合人的逻辑思惟。
selectImports
方法的实现由ConfigurationClassParser
类的 parse 方法触发,ConfigurationClassParser
的 parse 方法会被ConfigurationClassPostProcessor
类的 postProcessBeanDefinitionRegistry 方法调用,而触发这一切的最上层是 spring application context 的refresh()
方法。
信息量有点大,咱们经过下图来作简单说明:
Application 启动
⥥ // refresh spring application context
AbstractApplicationContext.refresh
⥥ // 执行 BeanFactoryPostProcessor 钩子回调
AbstractApplicationContext.invokeBeanFactoryPostProcessors
⥥ // 委托给 PostProcessorRegistrationDelegate 来执行钩子回调
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors
⥥ // ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的实现类,被触发
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
⥥ // ConfigurationClassPostProcessor 委托 ConfigurationClassParser 来对配置类进行解析
ConfigurationClassParser.parse
⥥ // ConfigurationClassParser 处理 DeferredImportSelector
ConfigurationClassParser.processDeferredImportSelectors
⥥ // 执行 selectImports
DeferredImportSelector.selectImports
关于 ConfigurationClassParser
类对 @Configuration
配置类的处理,本文并不打算做过多讲解,笔者会放在后续文章中来和你们进行探讨。
好了,有了对 ImportSelector
的相关了解后,咱们来看看 AutoConfigurationImportSelector
的 selectImports
方法的实现,源码以下:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 检查是否启用 auto-configuration,默认为启用
// 由子类 EnableAutoConfigurationImportSelector 重写
if (!isEnabled(annotationMetadata)) {
// 若是不开启 auto-configuration,返回 emtpy array
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 经过 SpringFactoriesLoader 找到全部的侯选配置类(auto-configuration 类)
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
// 去除重复的配置类
configurations = removeDuplicates(configurations);
// 根据 auto-configuration 的前后配置顺序的要求进行排序
// 可经过 @AutoConfigureBefore & @AutoConfigureAfter 来指定
configurations = sort(configurations, autoConfigurationMetadata);
// 须要被剔除的 auto-configuration 类
// 可在 properties 配置文件中经过 spring.autoconfigure.exclude 来指定
// 也可经过 @EnableAutoConfiguration 注解的 exclude() & excludeName() 属性来指定
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 从侯选配置类中剔除须要被排除在外的(auto-configuration 类)
configurations.removeAll(exclusions);
// 经过 AutoConfigurationImportFilter 来过滤侯选配置类,再次进行剔除
// 经过 SpringFactoriesLoader 获取所的 AutoConfigurationImportFilter 实现
configurations = filter(configurations, autoConfigurationMetadata);
// 发布 AutoConfigurationImportEvent 事件,通知 AutoConfigurationImportListener,
// 触发 onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) 方法
// 经过 SpringFactoriesLoader 获取所的 AutoConfigurationImportListener 实现
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回须要处理的 auto-configuration 类(名称)
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
protected boolean isEnabled(AnnotationMetadata metadata) {
return true;
}
相信你们看了笔者所添加的相关注释后对 AutoConfigurationImportSelector
的逻辑已经有了一个大体的了解。
AutoConfigurationImportSelector
的selectImports
方法的主要逻辑就是经过SpringFactoriesLoader
找到全部的 auto-configuration 侯选类,而后在此基础上进行去重、排序和剔除操做,最终获得须要进行 auto-configuration 的全部类的名称。拿到了所的 auto-configuration 类,spring boot 就能够加载这些 class,因为这些类自己标注了
@Configuration
,而后就能够被ConfigurationClassParser
类来进行解析了,最终@Bean
工厂方法就会被调用,完成 bean 的加载。
在笔者的注释中,不止一次提到了 SpringFactoriesLoader
,这个类究竟有何神奇之处?
一样的,为了搞清楚这件事,咱们还得看 spring 源码,部分源码以下:
public abstract class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// 经过 ClassLoader 加载全部的全部 META-INF/spring.factories 文件,解析成 Properties 对象,
// 根据 key (指定的 class 名称) 来获取全部的配置项 (类名称)
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
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();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
// 省略
}
}
}
原来 spring 使用了一种相似
ServiceLoader
(jdk 1.6 中增长)的处理方式。经过这种约定,咱们只须要将要进行处理的目标类名称配置在相对固定的配置文件中,spring 按照统一的方式来读取便可。对咱们的应用程序而言并不须要知道具体的实现类是哪些,也不须要知道这些类存在于哪些 jar 中,均可以被轻松获取。相对固定指的是:
相对于 classpath, META-INF/spring.factories 文件的位置固定和名称固定。
同一类别的配置,不一样配置文件中 key 名称固定不变。
注:若是你对
ServiceLoader
感到陌生,请查看 jdk api 了解相关内容。
既然了解了 spring 的 spring.factories
的套路,那么咱们就来找找看都有哪些 auto-configuration 类,
咱们的 key 是 @EnableAutoConfiguration
注解的 class 名称,即 org.springframework.boot.autoconfigure.EnableAutoConfiguration
,展开 spring-boot-autoconfigure-1.5.12.RELEASE.jar 文件,找到 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.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
# ……
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
# ……
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
# ……
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
上述这些 auto-configuration 侯选类名称都会被
AutoConfigurationImportSelector
经过SpringFactoriesLoader
所获取,而后AutoConfigurationImportSelector
就能够进行后续的去重、排序和剔除过滤操做了。
好了,本文至此,你是否已经对 spring boot 的 auto-configuration 工做机制有所了解了呢?
本文,咱们主要介绍了 spring boot auto-configuration 的工做机制,提到了几个重要的概念:
@EnableAutoConfiguration
ImportSelector
SpringFactoriesLoader
经过
@EnableAutoConfiguration
注解驱动引入AutoConfigurationImportSelector
,该类经过SpringFactoriesLoader
加载全部的META-INF/spring.factories
文件,获取到全部的 auto-configuration 候选类,而后进行去重、排序和剔除过滤等操做获得待处理的 auto-configuration 类。