EnableAutoConfiguration注解
spring的自动配置就是得益于这个EnableAutoConfiguration
注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.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 {};
}
该注解导入了一个类AutoConfigurationImportSelector
,从该类的名字我们可以看到,它是一个selector类,那么就具有selector类的特性。
我们都知道,spring启动的时候会自动执行selector类的方法selectImports
方法,从而将配置类自动注入容器中。下面就来看看是在哪里执行的这个方法
启动加载selector
spring的启动流程在之前的文章中已经讲过了,我们知道启动的时候会经过AbstractApplicationContext
类的refresh
方法 在该方法中有这样一句:
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
最终会进入到ConfigurationClassPostProcessor
类的processConfigBeanDefinitions
方法:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
}
这里就是在处理标有Configuration注解的类,我们进入parser.parse(candidates);
方法:
ConfigurationClassParser.java
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class ["
configClass.getMetadata().getClassName() "]", ex);
}
finally {
this.importStack.pop();
}
}
}
我们看到有这样一句selector.selectImports(currentSourceClass.getMetadata())
。看到这里,是不是就很清楚了,spring就是这样在启动的时候实现了自动导入配置类的。
下面我们再来看看自动配置类的内部逻辑
AutoConfigurationImportSelector
我们进入selector类,找到selectImports
方法:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
这里我们注意到这行代码List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
,这句代码就是获取所有jar中meta-inf/spring.factories文件中的类,下面具体来看看:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location ["
FACTORIES_RESOURCE_LOCATION "]", ex);
}
}
上面的代码中,我们注意到这样一个常量public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
,意思就是获取该文件中定义的类名,并作为列表返回。我们看一下这个文件的示例:
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.cloud.client.CommonsClientAutoConfiguration,
org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration,
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,
org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration,
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,
org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration,
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration,
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration,
org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration,
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,
org.springframework.cloud.commons.httpclient.HttpClientConfiguration,
org.springframework.cloud.commons.util.UtilAutoConfiguration,
org.springframework.cloud.configuration.CompatibilityVerifierAutoConfiguration,
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=
org.springframework.cloud.configuration.CompatibilityNotMetFailureAnalyzer
好了,我们回到之前的getAutoConfigurationEntry
方法,
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
上面的代码很清晰:
- 1、在配置文件中拿到类名列表
- 2、对该列表去重
- 3、根据注解属性,去掉指定排除的类
- 4、根据自定义
AutoConfigurationImportFilter
类过滤 - 5、回调
AutoConfigurationImportListener
监听的导入事件
AutoConfigurationImportFilter
在上面我们知道的,加载配置类的时候会根据条件过滤某些类,那么这里就来看下具体的实现:
代码语言:javascript复制configurations = getConfigurationClassFilter().filter(configurations);
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
从上面的代码中,我们看到,这里的listener是怎么来的呢?
代码语言:javascript复制protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
看到这个代码里面的调用,又回到了刚刚讲解获取自动配置类的地方,只是这里传入的Class<T> factoryType
变成了AutoConfigurationImportFilter
,
总结一下,也就是说:filter也是从META-INF/spring.factories
配置文件里面获取出来的,示例如下:
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=
org.springframework.boot.autoconfigure.condition.OnBeanCondition,
org.springframework.boot.autoconfigure.condition.OnClassCondition,
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
扩展点一
看到这里我们也可以总结出spring其中的一个扩展点:
如果业务中需要根据条件动态的控制某个类的导入,则可以实现AutoConfigurationImportFilter
接口,并且实现方法match
,然后放入META-INF/spring.factories配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value中。
spring在导入配置类的时候,会自动执行这个扩展方法。
注:spring另外封装了一个类ConfigurationClassFilter
来统一处理
AutoConfigurationImportListener
在上面我们知道的,加载配置类的时候会回调AutoConfigurationImportListener
监听的导入事件,那么这里就来看下具体的实现:
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
从上面的代码中,我们看到,这里的listener是怎么来的呢?
代码语言:javascript复制protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}
看到这个代码里面的调用,又回到了刚刚讲解获取自动配置类的地方,只是这里传入的Class<T> factoryType
变成了AutoConfigurationImportListener
,
总结一下,也就是说: listener也是从META-INF/spring.factories
配置文件里面获取出来的,示例如下:
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
代码语言:javascript复制
扩展点二
看到这里我们也可以总结出spring其中的一个扩展点:
如果业务中需要监控某个类的导入,则可以实现AutoConfigurationImportListener
接口,并且实现方法onAutoConfigurationImportEvent
,然后放入META-INF/spring.factories配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value中。
spring在导入配置类的时候,会自动执行这个扩展方法。
spring.factories
从上面我们知道了,spring就是通过spring.factories
文件让外部实现自定义扩展的,其实,该文件还可以定义其他一些classtype的key,
比如在springboot的启动中,有如下代码:
代码语言:javascript复制public static void main(String[] args) {
SpringApplication.run(xxx.class, args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
可以看出,springboot在启动的时候会加载spring.factories中ApplicationContextInitializer
和ApplicationListener
为key的类
看到这里我们也可以总结出spring的一个扩展点:
扩展点三
如果业务需要在启动的时候自定义下容器的初始化动作,则可以实现ApplicationContextInitializer
接口,并且实现方法initialize
,然后放入META-INF/spring.factories配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value中。
看到这里,我们是不是可以想象下,我们是不是可以在spring.factories中自定义key-value,然后通过库方法自行解析,达到任意扩展的效果呢?答案是可以的。