我们在启动类使用该注解可以自动完成springboot
项目的自动处理注解,自动导入各种依赖包等功能。那么追根溯源,这些是如何通过一个注解就完成的呢?下面我将结合源码学习探究底层的实现原理。
该篇文章涉及到对配置类的解析,需要提前了解ConfigurationClassPostProcessor是如何判定配置类,对配置类进行解析的,可以参考我写的另一篇文章
内置bean工厂后置处理器ConfigurationClassPostProcessor源码学习.
该注解是一个多层的注解,元注解如下
@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 {
出去基本的四个元注解,该注解主要包括以下几个复合注解
这几个注解完成了@SpringBootApplication
的主要功能
该注解定义如下
可以看出该注解就是springboot下的@Configuration
注解,也就是该注解同@Configuration
拥有同样的作用。被该注解修饰意味这该类是一个配置类,可以被ConfigurationClassPostProcessor
后置处理器进行处理,从而完成对@Import,@ComponentScan和@Component
等注解的处理和对应bd注册。
该注解定义如下
该注解也是个复合的注解,主要是引入了两个重要注解来实现自动配置
该注解定义如下
主要是通过@Import导入了AutoConfigurationPackages.Registrar
这个类该类继承关系如下
继承接口ImportBeanDefinitionRegistrar
实现了注册bean的功能,DeterminableImports
设置添加指定注解类所在的包为自动配置包。
也就是说添加了注解@AutoConfigurationPackage
会将该类所在的package作为自动配置的package。
主要是导入AutoConfigurationImportSelector
这个类,该类继承关系如下
Aware
接口,可以通过invokeAwareMethods
感知设置所需要的各种上下文属性。ImportSelector
接口,可以动态选择需要导入的配置类,直接实现的DeferredImportSelector
是在配置类解析完之后再处理该类,进行自动导入等。实现该注解可以对basePackages进行扫描,将各种@Component解析注册为bd,定义如下
启动类实现该注解,也就可以解析启动类所在包中各个子包中实现了指定组件注解的类。
由于该注解包括了@SpringBootConfiguration
,所以被修饰的启动类会被当做配置类处理。对该注解的处理最终会进入到ConfigurationClassParser.doProcessConfigurationClass
,其实主要是对@Import的处理,
//处理@Import导入的类,getImports获取所有@Import导入的类包括注解中的@Import
this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
进入该函数内部
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
//没有@Import直接返回
if (importCandidates.isEmpty()) {
return;
}
/**执行循环引入检查
* 通过内置静态类ImportStack的imports判断是否存在循环导入,比如a导入b,b导入c,c导入a
* 出现循环导入问题直接抛出错误
*/
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//候选类是否是ImportSelector的子接口或者子类
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);
}
}
//候选类是否是ImportBeanDefinitionRegistrar的子接口或者子类
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);
}
由于其满足candidate.isAssignable(ImportBeanDefinitionRegistrar.class)
进入该if语句,主要是加载该类Class,并实例化(ParserStrategyUtils.instantiateClass)
由于AutoConfigurationPackages.Registrar
没有实现各种Aware
接口,所以不会注入各种属性。
由于实现了ImportSelector和DeferredImportSelector
对于处理如下
//进入该if语句
if (candidate.isAssignable(ImportSelector.class)) {
//加载类信息
candidateClass = candidate.loadClass();
实例化,并进行各种Aware注入
ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
//进入该if语句
if (selector instanceof DeferredImportSelector) {
//并放入deferredImportSelectors,当所有配置类处理完,再处理该延迟导入选择类
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
} else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
主要做了下面几个事:
DeferredImportSelectorHolder
。deferredImportSelectors
中,后续进行延迟处理。再处理完配置类,会回到ConfigurationClassParser.parse
函数处理延迟导入选择器
this.deferredImportSelectorHandler.process()
,处理逻辑
//获取对应的holder
List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
//新建DeferredImportSelectorGroupingHandler
ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
//排序
deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);
注册deferredImports到hanler
deferredImports.forEach(handler::register);
//真正处理逻辑
handler.processGroupImports();
}
public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
//获取DeferredImportSelector中的group
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
//创建DeferredImportSelectorGrouping,设置group属性
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {
return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));
});
//设置deferredImports属性
grouping.add(deferredImport);
//添加到缓存中configurationClasses
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}
该处理过程主要对DeferredImportSelectorGroupingHandler
设置其中的属性值:groupings
和configurationClasses
,从而再2.2.2进行处理
Iterator var1 = this.groupings.values().iterator();
//遍历处理groupings
while(var1.hasNext()) {
//获取grouping属性
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
//获取过滤器,后边对配置类进行处理,可以根据此进行过滤
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
//获取自动导入的类,并放入缓存中
grouping.getImports().forEach((entry) -> {
ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());
try {
//解析刚才添加的自动导入的类主要是解析这些类导入的@Import
ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
}
上述获取自动导入的类主要是grouping.getImports()
该方法主要是调用process获取,过滤自动导入类,并通过selectImports
返回结果
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
//获取自动配置条目--核心逻辑
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
//把自动配置类放到缓存中
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
该方法主要负责对获取自动配置类,并放到AutoConfigurationImportSelector.AutoConfigurationGroup.entries
中
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//从系统环境查找是否存在环境属性"spring.boot.enableautoconfiguration",不存在返回默认值true,这里直接返回true,不进入该if语句
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取注解属性,这里是EnableAutoConfiguration属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//加载获取spring.factory中自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//删除重复项
configurations = removeDuplicates(configurations);
//获取需要排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//检查排除类
checkExcludedClasses(configurations, exclusions);
//从候选类中去除需要排除的类
configurations.removeAll(exclusions);
//对候选类根据过滤条件进行过滤(主要是根据配置文件spring-autoconfigure-metadata.properties,都是自动配置类注解元数据),只加载需要的
configurations = getConfigurationClassFilter().filter(configurations);
//触发自动配置事件,调用监听器处理(如果实现了)
fireAutoConfigurationImportEvents(configurations, exclusions);
//封装返回最终的加载的类
return new AutoConfigurationEntry(configurations, exclusions);
}
该类主要是是加载spring.factories自动导入类,进行去重、根据spring-autoconfigure-metadata.properties进行过滤等。
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;
主要是调用SpringFactoriesLoader.loadFactoryNames,
该方法获取类加载器,然后调用loadSpringFactories,该方法核心是加载配置文件
//是否已经加载过
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
//加载spring.factories,并进行解析放到cache缓存中
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
该类主要就是加载spring.factories
文件内容放到缓存cache
中,方便后续直接缓存获取,不用每次都要磁盘加载。
getOrDefault方法对cache中内容根据org.springframework.boot.autoconfigure.EnableAutoConfiguration
进行筛选,获取该名称下的所有自动配置类
加载的结果如下总共133个条目(2.6.4版本)
该方法主要是获取spring.factories
中配置的自动配置过滤,如下图
该方法核心逻辑如下
if (this.configurationClassFilter == null) {
/**
** 该方法同上文加载spring.factory文件类似,由于此时缓存cache已经有值
** 所有直接获取org.springframework.boot.autoconfigure.AutoConfigurationImportFilter对应的值,并进行了实例化
**/
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
//实现对各种Aware的感知和设置
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
过滤逻辑
String[] candidates = StringUtils.toStringArray(configurations);
//设置标志位是否跳过,默认false
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
//false,需要跳过,赋值null,设置标志位为true
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
//没有要跳过的,直接返回
if (!skipped) {
return configurations;
}
//将跳过的忽略,剩余的放到result,返回result
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
过滤原理
再上述进行过滤后剩余结果如下
后续我们就可以对这些结果注册到bd中,进行正常的实例化,初始化等设置。
针对@SpringBootApplication注解,从启动类的调用过程到解析为bd的主要流程处理如下,不包括后续的实例化过程,和@Import导入类的注册bd过程,具体如下图
本文主要从@SpringBootApplication注解的组成,源码的解析过程和处理的流程图做了一个学习记录,部分内容没有详细进行介绍,如有迷惑之处,可以参考源码进行学习。