首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot 自动配置

SpringBoot 自动配置

原创
作者头像
ruochen
修改2021-11-25 13:06:59
修改2021-11-25 13:06:59
1.6K0
举报
@SpringBootApplication

看@SpringBootApplication内部源码进行分析 ,核心代码具体如下

代码语言:txt
复制
@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中 
代码语言:txt
复制
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时 
代码语言:txt
复制
@Documented //表示注解可以记录在javadoc中 
代码语言:txt
复制
@Inherited //表示可以被子类继承该注解 
代码语言:txt
复制
@SpringBootConfiguration // 标明该类为配置类 
代码语言:txt
复制
@EnableAutoConfiguration // 启动自动配置功能 
代码语言:txt
复制
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = 
代码语言:txt
复制
TypeExcludeFilter.class), 
代码语言:txt
复制
@Filter(type = FilterType.CUSTOM, classes = 
代码语言:txt
复制
AutoConfigurationExcludeFilter.class) }) 
代码语言:txt
复制
public @interface SpringBootApplication { 
代码语言:txt
复制
// 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。 
代码语言:txt
复制
@AliasFor(annotation = EnableAutoConfiguration.class) 
代码语言:txt
复制
Class<?>[] exclude() default {}; 
代码语言:txt
复制
// 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全 
代码语言:txt
复制
类名字符串数组。 
代码语言:txt
复制
@AliasFor(annotation = EnableAutoConfiguration.class) 
代码语言:txt
复制
String[] excludeName() default {}; 
代码语言:txt
复制
// 指定扫描包,参数是包名的字符串数组。 
代码语言:txt
复制
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages") 
代码语言:txt
复制
String[] scanBasePackages() default {}; 
代码语言:txt
复制
// 扫描特定的包,参数类似是Class类型数组。 
代码语言:txt
复制
@AliasFor(annotation = ComponentScan.class, attribute = 
代码语言:txt
复制
"basePackageClasses") 
代码语言:txt
复制
Class<?>[] scanBasePackageClasses() default {}; 
代码语言:txt
复制
}

从上述源码可以看出,@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据信

息, 我们主要看后面 3 个注解:@SpringBootConfiguration、@EnableAutoConfiguration、

@ComponentScan三个核心注解,关于这三个核心注解的相关说明具体如下

@SpringBootConfiguration

@SpringBootConfiguration : SpringBoot 的配置类,标注在某个类上,表示这是一个 SpringBoot

的配置类。

查看@SpringBootConfiguration注解源码,核心代码具体如下。

代码语言:txt
复制
@Target({ElementType.TYPE}) 
代码语言:txt
复制
@Retention(RetentionPolicy.RUNTIME) 
代码语言:txt
复制
@Documented 
代码语言:txt
复制
@Configuration // 配置类的作用等同于配置文件,配置类也是容器中的一个对象 
代码语言:txt
复制
public @interface SpringBootConfiguration { 
代码语言:txt
复制
}

从上述源码可以看出,@SpringBootConfiguration注解内部有一个核心注解@Configuration,该注解

是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描

器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一

个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封

装命名而已

@EnableAutoConfiguration
代码语言:txt
复制
package org.springframework.boot.autoconfigure; 
代码语言:txt
复制
// 自动配置包 
代码语言:txt
复制
@AutoConfigurationPackage 
代码语言:txt
复制
// Spring的底层注解@Import,给容器中导入一个组件; 
代码语言:txt
复制
// 导入的组件是AutoConfigurationPackages.Registrar.class 
代码语言:txt
复制
@Import(AutoConfigurationImportSelector.class) 
代码语言:txt
复制
// 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。 
代码语言:txt
复制
public @interface EnableAutoConfiguration { 
代码语言:txt
复制
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; 
代码语言:txt
复制
// 返回不会被导入到 Spring 容器中的类 
代码语言:txt
复制
Class<?>[] exclude() default {}; 
代码语言:txt
复制
// 返回不会被导入到 Spring 容器中的类名 
代码语言:txt
复制
String[] excludeName() default {}; 
代码语言:txt
复制
}

Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的

Bean ,并加载到 IOC 容器。

@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到

IoC容器。

@AutoConfigurationPackage
代码语言:txt
复制
package org.springframework.boot.autoconfigure; 
代码语言:txt
复制
@Import(AutoConfigurationPackages.Registrar.class) // 导入Registrar中注册的组件 
代码语言:txt
复制
public @interface AutoConfigurationPackage { 
代码语言:txt
复制
}

@AutoConfigurationPackage :自动配置包,它也是一个组合注解,其中最重要的注解是

@Import(AutoConfigurationPackages.Registrar.class) ,它是 Spring 框架的底层注解,它的作

用就是给容器中导入某个组件类,例如

@Import(AutoConfigurationPackages.Registrar.class) ,它就是将 Registrar 这个组件类导入

到容器中,可查看 Registrar 类中 registerBeanDefinitions 方法:

代码语言:txt
复制
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
代码语言:txt
复制
    Registrar() {
代码语言:txt
复制
    }
代码语言:txt
复制
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
代码语言:txt
复制
        AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
代码语言:txt
复制
    }
代码语言:txt
复制
    public Set<Object> determineImports(AnnotationMetadata metadata) {
代码语言:txt
复制
        return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
代码语言:txt
复制
    }
代码语言:txt
复制
}

再看register方法

代码语言:txt
复制
public static void register(BeanDefinitionRegistry registry, String... 
代码语言:txt
复制
packageNames) { 
代码语言:txt
复制
// 这里参数 packageNames 缺省情况下就是一个字符串,是使用了注解 
代码语言:txt
复制
// @SpringBootApplication 的 Spring Boot 应用程序入口类所在的包 
代码语言:txt
复制
if (registry.containsBeanDefinition(BEAN)) { 
代码语言:txt
复制
// 如果该bean已经注册,则将要注册包名称添加进去 
代码语言:txt
复制
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); 
代码语言:txt
复制
ConstructorArgumentValues constructorArguments = beanDefinition 
代码语言:txt
复制
.getConstructorArgumentValues(); 
代码语言:txt
复制
constructorArguments.addIndexedArgumentValue(0, 
代码语言:txt
复制
addBasePackages(constructorArguments, packageNames)); 
代码语言:txt
复制
}
代码语言:txt
复制
else {
代码语言:txt
复制
//如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去 
代码语言:txt
复制
GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); 
代码语言:txt
复制
beanDefinition.setBeanClass(BasePackages.class); 
代码语言:txt
复制
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
代码语言:txt
复制
packageNames); 
代码语言:txt
复制
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); 
代码语言:txt
复制
registry.registerBeanDefinition(BEAN, beanDefinition); 
代码语言:txt
复制
} 
代码语言:txt
复制
} 

AutoConfigurationPackages.Registrar这个类就干一个事,注册一个 Bean ,这个 Bean 就是

org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages

,它有

一个参数,这个参数是使用了 @AutoConfigurationPackage 这个注解的类所在的包路径,保存自动配置

类以供之后的使用,比如给 JPA entity 扫描器用来扫描开发人员通过注解 @Entity 定义的 entity

类。

@Import(AutoConfigurationImportSelector.class)

@Import({AutoConfigurationImportSelector.class}) :将

AutoConfigurationImportSelector 这个类导入到 Spring 容器中,

AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration

配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。

image.png

AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种

Aware 接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。

其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回

调。

确定自动配置实现逻辑的入口方法:

跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处,

因此我们就从 DeferredImportSelectorGrouping 类的 getImports 方法来开始分析SpringBoot的自

动配置源码好了。

代码语言:txt
复制
// ConfigurationClassParser.java 
代码语言:txt
复制
public Iterable<Group.Entry> getImports() { 
代码语言:txt
复制
// 遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合 
代码语言:txt
复制
装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector 
代码语言:txt
复制
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { 
代码语言:txt
复制
// 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定 
代码语言:txt
复制
导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了) 
代码语言:txt
复制
this.group.process(deferredImport.getConfigurationClass().getMetadata(), 
代码语言:txt
复制
deferredImport.getImportSelector()); 
代码语言:txt
复制
}
代码语言:txt
复制
// 【2】,经过上面的处理后,然后再进行选择导入哪些配置类 
代码语言:txt
复制
return this.group.selectImports(); 
代码语言:txt
复制
}

标 【1】 处的的代码是我们分析的重中之重,自动配置的相关的绝大部分逻辑全在这里了。那么

this.group.process(deferredImport.getConfigurationClass().getMetadata(),

deferredImport.getImportSelector()) ;主要做的事情就是在 this.group 即

AutoConfigurationGroup 对象的 process 方法中,传入的 AutoConfigurationImportSelector

对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类

代码语言:txt
复制
注:
代码语言:txt
复制
AutoConfigurationGroup:是AutoConfigurationImportSelector的内部类,主要用来处理自动配 
代码语言:txt
复制
置相关的逻辑,拥有process和selectImports方法,然后拥有entries和 
代码语言:txt
复制
autoConfigurationEntries集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道 
代码语言:txt
复制
这些就足够了; 
代码语言:txt
复制
AutoConfigurationImportSelector:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配 
代码语言:txt
复制
置类; 
代码语言:txt
复制
metadata:标注在SpringBoot启动类上的@SpringBootApplication注解元数据 
代码语言:txt
复制
标【2】的this.group.selectImports的方法主要是针对前面的process方法处理后的自动配置类再进一 
代码语言:txt
复制
步有选择的选择导入

image.png

图中我们可以看到,跟自动配置逻辑相关的入口方法在process方法中

自动配置的主要逻辑
代码语言:txt
复制
// AutoConfigurationImportSelector$AutoConfigurationGroup.java 
代码语言:txt
复制
// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类 
代码语言:txt
复制
public void process(AnnotationMetadata annotationMetadata, 
代码语言:txt
复制
DeferredImportSelector deferredImportSelector) { 
代码语言:txt
复制
Assert.state( 
代码语言:txt
复制
deferredImportSelector instanceof AutoConfigurationImportSelector, 
代码语言:txt
复制
() -> String.format("Only %s implementations are supported, got %s", 
代码语言:txt
复制
AutoConfigurationImportSelector.class.getSimpleName(), 
代码语言:txt
复制
deferredImportSelector.getClass().getName())); 
代码语言:txt
复制
// 【1】,调用getAutoConfigurationEntry方法得到自动配置类放入 autoConfigurationEntry对象中 
代码语言:txt
复制
AutoConfigurationEntry autoConfigurationEntry = 
代码语言:txt
复制
((AutoConfigurationImportSelector) deferredImportSelector) 
代码语言:txt
复制
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), 
代码语言:txt
复制
annotationMetadata);
代码语言:txt
复制
//上面代码中我们再来看标 【1】 的方法 getAutoConfigurationEntry ,这个方法主要是用来获取自动 配置类有关,承担了自动配置的主要逻辑。直接上代码: 
代码语言:txt
复制
//深入 getCandidateConfigurations 方法 
代码语言:txt
复制
// 【2】,又将封装了自动配置类的autoConfigurationEntry对象装进 
代码语言:txt
复制
//autoConfigurationEntries集合 
代码语言:txt
复制
this.autoConfigurationEntries.add(autoConfigurationEntry); 
代码语言:txt
复制
// 【3】,遍历刚获取的自动配置类 
代码语言:txt
复制
for (String importClassName : autoConfigurationEntry.getConfigurations()) { 
代码语言:txt
复制
// 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合 
代码语言:txt
复制
this.entries.putIfAbsent(importClassName, annotationMetadata); 
代码语言:txt
复制
} 
代码语言:txt
复制
}

上面代码中我们再来看标 【1】 的方法 getAutoConfigurationEntry ,这个方法主要是用来获取自动

配置类有关,承担了自动配置的主要逻辑。

代码语言:txt
复制
// AutoConfigurationImportSelector.java 
代码语言:txt
复制
// 获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费 
代码语言:txt
复制
protected AutoConfigurationEntry getAutoConfigurationEntry( 
代码语言:txt
复制
AutoConfigurationMetadata autoConfigurationMetadata, 
代码语言:txt
复制
AnnotationMetadata annotationMetadata) { 
代码语言:txt
复制
// 获取是否有配置spring.boot.enableautoconfiguration属性,默认返回true 
代码语言:txt
复制
if (!isEnabled(annotationMetadata)) { 
代码语言:txt
复制
return EMPTY_ENTRY; 
代码语言:txt
复制
}
代码语言:txt
复制
// 获得@Congiguration标注的Configuration类即被审视introspectedClass的注解数据, 
代码语言:txt
复制
// 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class) 
代码语言:txt
复制
// 将会获取到exclude = FreeMarkerAutoConfiguration.class和excludeName=""的注解 数据 
代码语言:txt
复制
AnnotationAttributes attributes = getAttributes(annotationMetadata); 
代码语言:txt
复制
// 【1】得到spring.factories文件配置的所有自动配置类 
代码语言:txt
复制
List<String> configurations = getCandidateConfigurations(annotationMetadata, 
代码语言:txt
复制
attributes); 
代码语言:txt
复制
// 利用LinkedHashSet移除重复的配置类 
代码语言:txt
复制
configurations = removeDuplicates(configurations); 
代码语言:txt
复制
// 得到要排除的自动配置类,比如注解属性exclude的配置类 
代码语言:txt
复制
// 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class) 
代码语言:txt
复制
// 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据 
代码语言:txt
复制
Set<String> exclusions = getExclusions(annotationMetadata, attributes); 
代码语言:txt
复制
// 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常 
代码语言:txt
复制
checkExcludedClasses(configurations, exclusions); 
代码语言:txt
复制
// 【2】将要排除的配置类移除 
代码语言:txt
复制
configurations.removeAll(exclusions); 
代码语言:txt
复制
// 【3】因为从spring.factories文件获取的自动配置类太多,如果有些不必要的自动配置类都加载 
代码语言:txt
复制
//进内存,会造成内存浪费,因此这里需要进行过滤 
代码语言:txt
复制
// 注意这里会调用AutoConfigurationImportFilter的match方法来判断是否符合 
代码语言:txt
复制
//@ConditionalOnBean,@ConditionalOnClass或@ConditionalOnWebApplication,后面会重点分 
代码语言:txt
复制
//析一下
代码语言:txt
复制
configurations = filter(configurations, autoConfigurationMetadata); 
代码语言:txt
复制
// 【4】获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件, 
代码语言:txt
复制
// 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类 
代码语言:txt
复制
// 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处 
代码语言:txt
复制
//理器时触发 
代码语言:txt
复制
fireAutoConfigurationImportEvents(configurations, exclusions); 
代码语言:txt
复制
// 【5】将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回 
代码语言:txt
复制
return new AutoConfigurationEntry(configurations, exclusions); 
代码语言:txt
复制
}

此流程遍历整个ClassLoader中所有jar包下的spring.factories文件,并进行过滤。

spring.factories里面保存着springboot的默认提供的自动配置类。

META-INF/spring.factories

image.png

主要逻辑

AutoConfigurationEntry 方法主要做的事情就是获取符合条件的自动配置类,避免加载不必要的自

动配置类从而造成内存浪费。我们下面总结下 AutoConfigurationEntry 方法主要做的事情:

【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配

置类如图所示。

【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类

排除掉;

【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些

不符合条件的自动配置类;

【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉

ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;

【5】 最后再将符合条件的自动配置类返回。

过滤方法
代码语言:txt
复制
// AutoConfigurationImportSelector.java 
代码语言:txt
复制
private List<String> filter(List<String> configurations, 
代码语言:txt
复制
AutoConfigurationMetadata autoConfigurationMetadata) { 
代码语言:txt
复制
long startTime = System.nanoTime(); 
代码语言:txt
复制
// 将从spring.factories中获取的自动配置类转出字符串数组 
代码语言:txt
复制
String[] candidates = StringUtils.toStringArray(configurations); 
代码语言:txt
复制
// 定义skip数组,是否需要跳过。注意skip数组与candidates数组顺序一一对应 
代码语言:txt
复制
boolean[] skip = new boolean[candidates.length]; 
代码语言:txt
复制
boolean skipped = false; 
代码语言:txt
复制
// getAutoConfigurationImportFilters方法:拿到OnBeanCondition, 
代码语言:txt
复制
OnClassCondition和OnWebApplicationCondition 
代码语言:txt
复制
// 然后遍历这三个条件类去过滤从spring.factories加载的大量配置类 
代码语言:txt
复制
for (AutoConfigurationImportFilter filter : 
代码语言:txt
复制
getAutoConfigurationImportFilters()) { 
代码语言:txt
复制
// 调用各种aware方法,将beanClassLoader,beanFactory等注入到filter对象中, 
代码语言:txt
复制
// 这里的filter对象即OnBeanCondition,OnClassCondition或 
代码语言:txt
复制
OnWebApplicationCondition 
代码语言:txt
复制
invokeAwareMethods(filter); 
代码语言:txt
复制
// 判断各种filter来判断每个candidate(这里实质要通过candidate(自动配置类)拿到其标 
代码语言:txt
复制
注的 
代码语言:txt
复制
// @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里 
代码语言:txt
复制
面的注解值)是否匹配, 
代码语言:txt
复制
// 注意candidates数组与match数组一一对应 
代码语言:txt
复制
/**********************【主线,重点关注】********************************/ 
代码语言:txt
复制
boolean[] match = filter.match(candidates, autoConfigurationMetadata); 
代码语言:txt
复制
// 遍历match数组,注意match顺序跟candidates的自动配置类一一对应 
代码语言:txt
复制
for (int i = 0; i < match.length; i++) { 
代码语言:txt
复制
// 若有不匹配的话 
代码语言:txt
复制
if (!match[i]) { 
代码语言:txt
复制
// 不匹配的将记录在skip数组,标志skip[i]为true,也与candidates数组一一 
代码语言:txt
复制
对应 
代码语言:txt
复制
skip[i] = true; 
代码语言:txt
复制
// 因为不匹配,将相应的自动配置类置空 
代码语言:txt
复制
candidates[i] = null; 
代码语言:txt
复制
// 标注skipped为true 
代码语言:txt
复制
skipped = true; 
代码语言:txt
复制
} 
代码语言:txt
复制
} 
代码语言:txt
复制
}
代码语言:txt
复制
// 这里表示若所有自动配置类经过OnBeanCondition,OnClassCondition和 
代码语言:txt
复制
OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回 
代码语言:txt
复制
if (!skipped) { 
代码语言:txt
复制
return configurations; 
代码语言:txt
复制
}
代码语言:txt
复制
// 建立result集合来装匹配的自动配置类 
代码语言:txt
复制
List<String> result = new ArrayList<>(candidates.length); 
代码语言:txt
复制
for (int i = 0; i < candidates.length; i++) { 
代码语言:txt
复制
// 若skip[i]为false,则说明是符合条件的自动配置类,此时添加到result集合中 
代码语言:txt
复制
if (!skip[i]) { 
代码语言:txt
复制
result.add(candidates[i]); 
代码语言:txt
复制
} 
代码语言:txt
复制
}
代码语言:txt
复制
// 打印日志
代码语言:txt
复制
if (logger.isTraceEnabled()) { 
代码语言:txt
复制
int numberFiltered = configurations.size() - result.size(); 
代码语言:txt
复制
logger.trace("Filtered " + numberFiltered + " auto configuration class 
代码语言:txt
复制
in " 
代码语言:txt
复制
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) 
代码语言:txt
复制
+ " ms"); 
代码语言:txt
复制
}
代码语言:txt
复制
// 最后返回符合条件的自动配置类 
代码语言:txt
复制
return new ArrayList<>(result); 
代码语言:txt
复制
}

AutoConfigurationImportSelector 的 filter 方法主要做的事情就是调用

AutoConfigurationImportFilter 接口的 match 方法来判断每一个自动配置类上的条件注解(若有

的话) @ConditionalOnClass , @ConditionalOnBean 或 @ConditionalOnWebApplication

是否满足

条件,若满足,则返回true,说明匹配,若不满足,则返回false说明不匹配。

我们现在知道 AutoConfigurationImportSelector 的 filter 方法主要做了什么事情就行了

关于条件注解的讲解

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册

bean。

@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。

@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。

@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式

的条件判断。

@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。

@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。

@ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。

@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。

@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。

@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。

@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。

@ConditionalOnResource:当类路径下有指定的资源时触发实例化。

@ConditionalOnJndi:在JNDI存在的条件下触发实例化。

@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首

选的Bean时触发实例化。

有选择的导入自动配置类

this.group.selectImports 方法是如何进一步有选择的导入自动配置类的。

代码语言:txt
复制
// AutoConfigurationImportSelector$AutoConfigurationGroup.java 
代码语言:txt
复制
public Iterable<Entry> selectImports() { 
代码语言:txt
复制
if (this.autoConfigurationEntries.isEmpty()) { 
代码语言:txt
复制
return Collections.emptyList(); 
代码语言:txt
复制
}
代码语言:txt
复制
// 这里得到所有要排除的自动配置类的set集合 
代码语言:txt
复制
Set<String> allExclusions = this.autoConfigurationEntries.stream() 
代码语言:txt
复制
.map(AutoConfigurationEntry::getExclusions)
代码语言:txt
复制
.flatMap(Collection::stream).collect(Collectors.toSet()); 
代码语言:txt
复制
// 这里得到经过滤后所有符合条件的自动配置类的set集合 
代码语言:txt
复制
Set<String> processedConfigurations = this.autoConfigurationEntries.stream() 
代码语言:txt
复制
.map(AutoConfigurationEntry::getConfigurations) 
代码语言:txt
复制
.flatMap(Collection::stream) 
代码语言:txt
复制
.collect(Collectors.toCollection(LinkedHashSet::new)); 
代码语言:txt
复制
// 移除掉要排除的自动配置类 
代码语言:txt
复制
processedConfigurations.removeAll(allExclusions); 
代码语言:txt
复制
// 对标注有@Order注解的自动配置类进行排序, 
代码语言:txt
复制
return sortAutoConfigurations(processedConfigurations, 
代码语言:txt
复制
getAutoConfigurationMetadata()) 
代码语言:txt
复制
.stream() 
代码语言:txt
复制
.map((importClassName) -> new Entry( 
代码语言:txt
复制
this.entries.get(importClassName), importClassName)) 
代码语言:txt
复制
.collect(Collectors.toList()); 
代码语言:txt
复制
}

可以看到, selectImports 方法主要是针对经过排除掉 exclude 的和被

AutoConfigurationImportFilter 接口过滤后的满足条件的自动配置类再进一步排除 exclude 的自

动配置类,然后再排序

最后,我们再总结下SpringBoot自动配置的原理,主要做了以下事情:

  1. 从spring.factories配置文件中加载自动配置类;
  2. 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置undefined类;
  3. 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有undefined标注的话) @ConditionalOnClass , @ConditionalOnBean 和 @ConditionalOnWebApplication 的条件,若都符合的话则返回匹配结果;
  4. 然后触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评undefined估报告器对象来分别记录符合条件和 exclude 的自动配置类。
  5. 最后spring再将最后筛选后的自动配置类导入IOC容器中

image.png

总解
  1. SpringBoot 启动会加载大量的自动配置类
  2. 我们看我们需要实现的功能有没有 SpringBoot 默认写好的自动配置类
  3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们有我们要用的组件,我们就不需要undefined再来配置了)
  4. 给容器中自动配置类添加组件的时候,会从 properties 类中获取某些属性,我们就可以在配置文undefined件中指定这些属性的值。undefinedxxxAutoConfiguration :自动配置类,用于给容器中添加组件从而代替之前我们手动完成大量繁undefined琐的配置。undefinedxxxProperties : 封装了对应自动配置类的默认属性值,如果我们需要自定义属性值,只需要根据undefinedxxxProperties 寻找相关属性在配置文件设值即可。
例子HttpEncodingAutoConfiguration ( Http 编码自动配置)
代码语言:txt
复制
// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件 
代码语言:txt
复制
@Configuration 
代码语言:txt
复制
// 启动指定类的ConfigurationProperties功能;将配置文件中对应的值和 
代码语言:txt
复制
//HttpEncodingProperties绑定起来; 
代码语言:txt
复制
@EnableConfigurationProperties({HttpEncodingProperties.class}) 
代码语言:txt
复制
// Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效。 
代码语言:txt
复制
// 判断当前应用是否是web应用,如果是,当前配置类生效。并把HttpEncodingProperties加入到 ioc 容器中 
代码语言:txt
复制
@ConditionalOnWebApplication 
代码语言:txt
复制
// 判断当前项目有没有这个CharacterEncodingFilter : SpringMVC中进行乱码解决的过滤器 
代码语言:txt
复制
@ConditionalOnClass({CharacterEncodingFilter.class}) 
代码语言:txt
复制
// 判断配置文件中是否存在某个配置 spring.http.encoding.enabled 如果不存在,判断也是成立的 
代码语言:txt
复制
// matchIfMissing = true 表示即使我们配置文件中不配置 
代码语言:txt
复制
spring.http.encoding.enabled=true,也是默认生效的 
@ComponentScan注解

@ComponentScan使用

主要是从定义的扫描路径中,找出标识了需要装配的类自动装配到spring 的bean容器中。

常用属性如下:

basePackages、value:指定扫描路径,如果为空则以@ComponentScan注解的类所在的包为基

本的扫描路径

basePackageClasses:指定具体扫描的类

includeFilters:指定满足Filter条件的类

excludeFilters:指定排除Filter条件的类

includeFilters和excludeFilters 的FilterType可选:ANNOTATION=注解类型 默认、

ASSIGNABLE_TYPE(指定固定类)、ASPECTJ(ASPECTJ类型)、REGEX(正则表达式)、CUSTOM(自定义类

型),自定义的Filter需要实现TypeFilter接口

@ComponentScan的配置如下:

代码语言:txt
复制
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = 
代码语言:txt
复制
TypeExcludeFilter.class), 
代码语言:txt
复制
@Filter(type = FilterType.CUSTOM, classes = 
代码语言:txt
复制
AutoConfigurationExcludeFilter.class) }) 

借助excludeFilters将TypeExcludeFillter及FilterType这两个类进行排除

当前@ComponentScan注解没有标注basePackages及value,所以扫描路径默认为@ComponentScan

注解的类所在的包为基本的扫描路径(也就是标注了@SpringBootApplication注解的项目启动类所在

的路径)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • @SpringBootApplication
  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)
  • 确定自动配置实现逻辑的入口方法:
  • 自动配置的主要逻辑
  • META-INF/spring.factories
  • 主要逻辑
  • 过滤方法
  • 关于条件注解的讲解
  • 有选择的导入自动配置类
  • 总解
  • 例子HttpEncodingAutoConfiguration ( Http 编码自动配置)
  • @ComponentScan注解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档