AutoConfiguration排除指定组件

在 上节中我们获得了 spring.factories 文件中注册的自动加载组件,但如果在实际应用的过程中并不需要其中的某个或某些组件,可通过配置@EnableAutoConfiguration 的注解属性 exclude 或 excludeName 进行有针对性的排除,当然也可以通过配置文件进行排除。先通过源代码看看如何获取排除组件的功能。

protected Set<String> getExclusions (AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//创建 Set 集合并把待排除的内容存于集合内,LinkedHashSet 具有不可重复性
Set<String> excluded = new L inkedHashSet<>();
excluded . addAll(asList(attributes, "exclude"));
excluded . addAll(Arrays. asList(attributes. getStringArray(" excludeName")));
exc. luded. addAll(getExcludeAutoConf igurationsProperty());
return excluded ;
}
private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder . get(getEnvironment());
return binder. bind( PROPERTY_ NAME_ AUTOCONFIGURE_ EXCLUDE ,
String[].class)
. map(Arrays: :asList) . orElse(Collections. emptyList());
}
String[] excludes = getEnvironment( )
. getProperty( PROPERTY_ NAME_ AUTOCONFIGURE_ EXCLUDE, String[].class);
return (excludes != null) ? Arrays.aslist(excludes) : Collections . emptyLi
st();
}

AutoConfigurationlmportSelector 中通过调用 getExclusions 方法来获取被排除类的集合。

它会收集@EnableAutoConfiguration 注解中配置的 exclude 属性值 excludeName 属性值 并 通 过 方 法
getExcludeAutoConfigurationsProperty 获 取 在 配 置 文 件 中 key 为spring.autoconfigure.exclude 的配置值。

以排除自动配置
DataSourceAutoConfiguration 为例,配置文件中的配置形式如下。

spring. autoconfigure . exc lude=org. spr ingframework . boot . autoconfigure .jdbc .DataSource-AutoConfiguration

获取到被排除组件的集合之后,首先是对待排除类进行检查操作,代码如下。

private void checkExcludedClasses(List<String> configurat ions,
Set<String> exclusions) {
List<String> invalidExcludes = new ArrayL ist<>(exclusions. size());
//遍历并判断是否存在对应的配置类
for (String exclusion : exclusions) {
if (ClassUtils. isPresent(exclusion, getClass(). getClassLoader())
&& !configurat ions . contains(exclusion)) {
inval idExcludes . add(exclusion);
//如果不为空,就进行处理
if (!invalidExcludes . isEmpty()) {
handleInvalidExcludes( invalidExcludes);
}
//抛出指定异常
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
message . append("\t- "). append(exclude) . append(String . format( "%n"));
throw new IllegalStateException(String
. format("The following classes could not be excluded because they are"
not auto-configuration classe
s:%n%s", message));
}

以上代码中,checkExcludedClasses 方 法用来确保被排除的类存在于当前的 ClassLoader中 , 并 且 包 含 在 spring.factories 注 册 的 集 合 中 。 如 果 不 满 足 以 上 条 件 , 调 用handleInvalidExcludes 方法抛出异常。

如果被排除类都符合条件,调用 configurations.removeAll(exclusions)方法从自动配置集合中移除被排除集合的类,至此完成初步的自动配置组件排除。

AutoConfiguration 过滤自动配置组件

当完成初步的自动配置组件排除工作之后
AutoConfigurationlmportSelector 会结合在此之前获取的 AutoConfigurationMetadata 对象,对组件进行再次过滤。

private List<String> filter(List<String> configurations,
AutoConf igurationMetadata autoConfigurationMeta
data)
long startTime = System. nanoTime();
String[] candidates = StringUtils. toStringArray( configurations);
boolean[] skip = new boolean[ candidates . length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConf igurationImportFil
te-
rs()) {
invokeAwareMethods(filter);
boolean[] match = filter . match(candidates, autoConfigurat ionMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
if (!skipped) {
return configurations;
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i])
result . add(candidates [i]);
return new ArrayL ist<>(result);
}
protected List<AutoConfigurat ionImportFilter> getAutoConfigurat ionImportFil
ters() {
return SpringFactoriesLoader . loadFactories (AutoConfigurat ionImportFilter.
class,
this . beanClassLoader);}

下面,我们先来明确一一下都有哪些数据参与 了以上两种方法,然后再进行业务逻辑的梳理。

-configurations: List, 经过初次过滤之后的自动配置组件列表。

-autoConfigurationMetadata: AutoConfigurationMetadata ,元数据文件META-INF/spring-autoconfigure-metadata.properties 中配置对应实体类。

List :META-INF/springfactories 中配置 key 为
Auto-ConfigurationImportFilter 的 Filters 列表。

getAutoConfigurationImportFilters 方法是通过 SpringFactoriesLoader 的 loadFactories 方法将 ME TA-INF/spring.factories 中配置 key 为 AutoConfigurationlmportFilter 的值进行加载。下面为 META-INF/spring.factories 中相关配置的具体内容。

# Auto Configuration Import Filters
org. springframework. boot . autoconfigure . AutoConfigurationImportFilter=\
org. springframework . boot . autoconfigure. condit ion . OnBeanCondition, \
org . spr ingframework . boot . autoconfigure . condition . OnClassCondition,\
org. springframework. boot . autoconfigure . condit ion . OnWebApplicat ionCondition

在 spring-boot-autoconfigure 中 默 认 配 置 了 3 个 筛 选 条 件 , OnBeanCondition 、OnClassCondition和OnWebApplicationCondition,它们均实现了
AutoConfigurationlmportFilter 接口。

在明确了以上信息之后,该 filter 方法的过滤功能就很简单了。用一句话来概述就是:对自动配 置 组 件 列 表 进 行 再 次 过 滤 , 过 滤 条 件 为 该 列 表 中 自 动 配 置 类 的 注 解 得 包 含 在
OnBeanConditionOnClassCondition 和 OnWebApplicationCondition 中指定的注解依次包含@ConditionalOnBean@ConditionalOnClass 和@ConditionalOnWebApplication.那 么 在 这 个 实 现 过 程 中 , AutoConfigurationMetadata 对应 的 元 数 据 和AutoConfiguration-lmportFilter 接口及其实现类是如何进行具体筛选的呢?我们先来看一下AutoConfiguration-lmportFilter 接口相关类的结构及功能, 如图 2-4 所示。

下面进行相关的源代码及步骤的分解。我们已经知道
AutoConfigurationlmportFilter 接口可以在 spring.factories 中注册过滤器,用来过滤自动配置类,在实例化之前快速排除不需要的自动配置,代码如下。

@FunctionalInterface
public interface AutoConfigurationImportFilter {
boolean[] match(String[ ] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurat ionMetadata);
}

match 方法接收两个参数,一个是待过滤的自动配置类数组,另一个是自动配置的元数据信息 。 match 返 回 的 结 果 为 匹 配 过 滤 后 的 结 果 布 尔 数 组 , 数 组 的 大 小 与 String[ ]autoConfigurationClasses-致, 如果需排除,设置对应值为 false。

图 2-4 中已经显示
AutoConfigurationlmportFilter 接口的 match 方法主要在其抽象子类中实现,而抽象子类 FilteringSpringBootCondition 在实现 match 方法的同时又定义了新的抽象方法 getOutcomes,继承该抽象类的其他 3 个子类均实现了 getOutcomes 方法,代码如下。

abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanF actoryAware, BeanClassLoad
er
Aware {
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurat ionMetadata autoConfigurat ionMetad
ata) {
ConditionOutcome[] outcomes = getOutcomes( autoConfigurationClasses,
autoConfigurat ionMetadata);
boolean[] match = new boolean[outcomes. length];
for (int i = 0; i < outcomes. length; i++) {
match[i] = (outcomes[i] == nu1l|| outcomes[i]. isMatch());
return match;
//过滤核心功能,该方法由子类实现
protected abstract ConditionOutcome[] get0utcomes(String[ ] autoConfigur
ationClasses,
AutoConfigurat ionMetada
ta autoConfigurationMetadata);
}

通过上面的源码可以看出,match 方法在抽象类
FilteringSpringBootCondition 中主要的功能就是调用 getOutcomes 方法,并将其返回的结果转换成布尔数组。而相关的过滤核心功能由子类实现的 getOutcomes 方法来实现。

下面以实现类 OnClassCondition 来具体说明执行过程。首先看一下入口方法 getOutcomes的源代码。

@Order (Ordered . HIGHEST_ PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
@Override
protected final ConditionOutcome[] get0utcomes (String[] autoConfiguration
Classes ,
AutoConfigurationMetadata
autoConfigurationMetadata) {
//如果有多个处理器,采用后台线程处理
if (Runtime . getRuntime(). availableProcessors() > 1) {
return resolveOutcomes Threaded( autoConfigurationClasses, autoConfigu-
rationMetadata);
} else {
OutcomesResolver outcomesResolver = new
StandardoutcomesResolver ( autoConfigurationClasses, 0,
autoConfigurationClasses.length, autoConfigu
rationMetadata,
getBeanClassLoader());
return outcomesResolver . resolve0utcomes( );
}
}
}

Spring Boot 当前版本对 getOutcomes 方法进行了性能优化,根据处理器的情况不同采用了不同的方式进行操作。如果使用多个处理器,采用后台线程处理(之前版本的实现方法)。否则,getOutcomes 直接创建 StandardOutcomesResolver 来处理。

在 resolveOutcomesThreaded 方法中主要采用了分半处理的方法来提升处理效率,而核心功能都是在内部类 StandardOutcomesResolver 的 resolveOutcomes 方法中实现。

resolveOutcomes Threaded 的分半处理实现代码如下。

@Order(Ordered . HIGHEST_ PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
private ConditionOutcome[ ] resolveOutcomesThreaded(String[ ] autoConfigura
tionClasses,
AutoConfigurationMetad
ata autoConfigurationMetadata) {
int split = autoConfigurationClasses. length / 2;
OutcomesResolver firstHalfResolver = create0utcomesResolver( autoConfigu
rationClass
es, 0,split, autoConfigurat ionMetadata) ;
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(au-
toCo
nfigurationClasses, split,
auto
ConfigurationClasses. length, autoConfigurat ionMetadata, getBean-Clas
sLoader());
ConditionOutcome[ ] secondHalf = secondHalfResolver . resolveOutcomes();
ConditionOutcome[] firstHalf = firstHalfResolver . resolve0utcomes( );
ConditionOutcome[] outcomes = new ConditionOutcome[ autoConfigurationCla
ses. length];
System. arraycopy(firstHalf, 0,outcomes, 0,firstHalf .length);
System. arraycopy(secondHalf, 0,outcomes, split, secondHalf . length);
return outcomes ;
}
}

内部类 StandardOutcomesResolver 的源代码重点关注 getOutcomes 方法的实现,它实现了获取元数据中的指定配置,间接调用 getOutcome(StringclassName,ClassL oader classLoader)方法来判断该类是否符合条件,部分源代码如下。

@Order(Ordered. HIGHEST_ PRECEDENCE)
class OnClassCondition extends FilteringSpr ingBootCondition {
private final class StandardOutcomesResolver implements OutcomesResolver
private ConditionOutcome[] getOutcomes(String[ ] autoConfigurationClasse
int start, int end, AutoConfigur
ationMetadata autoConfiguration-
Metadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for(inti=start;i<end;i++){
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
String candidates = autoConfigurationMetadata
. get( autoConfigurat ionClass, "Conditional0nClass");
if (candidates != null) {
outcomes[i - start] = getOutcome ( candidates);
return outcomes ;
//判断该类是否符合条件
private ConditionOutcome getOutcome(String className, ClassLoader class
Loader) {
if (ClassNameFilter .MISSING . matches(className, classLoader)) {
return ConditionOutcome 。noMatch( ConditionMessage
. forCondition(ConditionalOnClass .class)
.didNotFind("required class").items
(Style.QUOTE, className));
return null;
}
}
}

在获取元数据指定配置的功能时用到了 AutoConfigurationMetadata 接口的 get(StringclassName,String key) 方法,而该方法由类
AutoConfigurationMetadataL oader 来实现。

该类在前面的章节已经提过了,它会加载
META-INF/spring-autoconfigure-metadata.properties 中的配置。

final class AutoConfigurat ionMetadataLoader {
protected static final String PATH = "META-INF/"
+ " spring- autoconfigure - metadata. properties";
private static class PropertiesAutoConfigurat ionMetadata
implements AutoConfigurat ionMetadata {
@Override
public String get(String className, String key) {
return get(className, key, null);
@Override
public String get(String className, String key, String defaultValue) {
String value = this. properties. getProperty(className + "." + key);
return (value != null) ? value : defaultValue;
}
}
}

AutoConfigurationMetadataL oader 的内部类 PropertiesAutoConfigurationMetadata 实现了 AutoConfigurationMetadata 接 口 的 具 体 方 法 , 其 中 包 含 我 们 用 到 的 get(StringclassName,String key)方法。

根据 get 方法实现过程,我们不难发现,在 getOutcomes 方 法中获取到的 candidates,其实就是
META-INF/spring-autoconfigure-metadata.properties 文件中配置的 key 为自动加载注解类+"."+"ConditionalOnClass"的字符串,而 value 为其获得的值。以数据源的自动配置为例,寻找到的对应元数据配置如 org. springframework . boot . autoconf igure . jdbc .DataSourceAutoConfiguration. ConditionalOnClass=javax . sql. DataSource , org.springframework. jdbc . datasource. embedded.EmbeddedDatabaseTypekey为自动0载组件org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration加上"."”,再加上当前过滤条件中指定的 ConditionalOnClass。

然后,根据此 key 获得的 value 直为 javax.sql.DataSource,
org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType.当 获 取 到 对 应 的candidates值 之 后 , 最 终 会 调 用getOutcome(StringclassName,ClassLoader classLoader)方法 ,并在其中使用枚举类ClassNameFilter.MISSING 的 matches 方法来判断 candidates 值是否匹配。而枚举类ClassNameFilter 位于 OnClassCondition 继承的 抽象类 FilteringSpringBootCondition 中。

abstract class FilteringSpringBootCondition extends SpringBootConditionD
implements AutoConfigurationImportFilter, BeanF actoryAware, BeanClassLoad
erAware {
protected enum ClassNameFilter {
PRESENT
@Override
public boolean matches (String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
},
MISSING {
@Override
public boolean matches (String className, ClassLoader classLoader) {
return lisPresent(className, classLoader);
};
public abstract boolean matches (String className, ClassLoader classLoad
er);
//通过类加载是否抛出异常来判断该类是否存在
public static boolean isPresent(String className, ClassLoader classLoad
er) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}try{
forName (className, classLoader);
return true;
} catch (Throwable ex) {
return false;
}
//进行类加载操作private static Class<?> forName ( String className, ClassLoader classLoad
er)
throws Clas sNotFoundException {
if (classLoader != null) {
return classLoader . loadClass(className);
return Class . forName( className);
}
}

ClassNameFilter 的匹配原则很简单,就是通过类加载器去加载指定的类。如果指定的类加载成功,即没有抛出异常,说明 ClassNameFilter 匹配成功。如果抛出异常,说明ClassNameFilter 匹配失败。

至此,整个过滤过程的核心部分已经完成了。我们再用一-张简单的流程图来回顾整个过滤的过程,如图2-5所示。

本文给大家讲解的内容是AutoConfiguration排除指定组件和过滤自动配置组件

  1. 下篇文章给大家讲解的是AutoConfiguration事件注册和@Conditional 条件注解、实例解析;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

AutoConfiguration排除指定和过滤自动配置组件相关推荐

  1. springboot继承组件_SpringBoot如何扩展引入的组件,以及如何自动配置组件原理

    大家都知道,当我们创建SpringBoot项目之后,我们可以在pom文件下,引入我们想要启动的组件,当我们引入之后,SpringBoot会自动帮我们配置属性! 下面我们以SpringBoot引入Spr ...

  2. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  3. ecshop 属性自动组合_SpringBoot单元测试:MockMvc的自动配置

    MockMvc 的自动配置 上面我们提到@AutoConfigureMockMvc 提供了自动配置 MockMvc 的功能,实例化MockMvc 的 具 体 代 码 在 spring-boot-tes ...

  4. SpringBoot单元测试:MockMvc的自动配置

    MockMvc 的自动配置 上面我们提到@AutoConfigureMockMvc 提供了自动配置 MockMvc 的功能,实例化MockMvc 的 具 体 代 码 在 spring-boot-tes ...

  5. SpringBoot自定义Starter(自动配置类)

    前言 SpringBoot其实从诞生以来围绕的核心就是快速构建项目,快速构建的前提是有人帮你做好轮子,开发者只要拿来即用就好了,而造好轮子的人就是SpringBoot的开发者,引入自动配置的形式帮助开 ...

  6. Spring Boot概述与入门特点配置方式注入方式yim配置文件与多文件配置Spring Boot自动配置原理lombok应用

    1. Spring Boot概述 Spring Boot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品: 首页Spring Boot简介可以 ...

  7. autobahn-java-master,禁用自动配置

    Spring Boot大量使用自动配置和默认配置,极大地减少了代码,通常只需要加上几个注解,并按照默认规则设定一下必要的配置即可.例如,配置JDBC,默认情况下,只需要配置一个spring.datas ...

  8. [Spring Boot] 4. Spring Boot实现自动配置的原理

    入口注解类@EnableAutoConfiguration @SpringBootApplication注解中包含了自动配置的入口注解: @Target(ElementType.TYPE) @Rete ...

  9. 2、SpringBoot2组件添加注解与自动配置原理

    1.组件添加 (1)@Configuration + @Bean 通过配置类往容器中添加组件,@Bean用于配置类的方法上 /** * 1.配置类里面使用@Bean标注在方法上给容器注册组件,默认也是 ...

最新文章

  1. nginx rewrite 指令last break区别最详细的解释
  2. QQ亿级日活跃业务后台核心技术揭秘
  3. RocketMQ之Pull消费者客户端启动
  4. Java集合篇:Stack
  5. OpenCV使用F变换过滤
  6. 宝马奥迪工厂模式_宝马的完整形式是什么?
  7. String和QString之间的转化----可避免出现中文乱码的现象
  8. 知道 | 同学,你都了解关系型数据库,确定不了解一下这种数据库吗?
  9. 动手动脑第二波方法的重载
  10. Greenplum单机版部署
  11. QQ游戏-大家来找茬 外挂
  12. 红烧鸡翅(简单又好吃)
  13. 分享第三方支付申请流程政策
  14. Pygame游戏编程
  15. python本科毕设_关于本科毕设选题请教问题
  16. Redis入门完整教程:复制原理
  17. springboot+mybatis-plus+vue完成微信支付(前后端分离)
  18. 转:090906爆笑COD4山寨剧情
  19. JAVA----百度推广接口返回结果集统一处理
  20. java项目需要画什么图_项目管理流程图如何画?这样操作5分钟解决

热门文章

  1. 计算机系23班趣味口号,23班口号怎么写
  2. VS2017无法登录:我们无法刷新此账户的凭证、我们无法添加此账户发送请求时出错、评估期已结束,请登录以解除产品锁定
  3. (转)(异常分析) org.hibernate.MappingException: entity class not found
  4. 2021CUDA编程:加速计算基础 —— CUDA C/C++
  5. ANSYS有限元仿真分析:边界非线性 (接触Contact)
  6. AOE网与关键路径、关键路径算法
  7. Sutherland-Hodgeman 多边形裁剪算法
  8. Android Kotlin仿iOS底部选择框
  9. excel手机版_这些Excel恢复神器,据说个个都很强,你用过吗?网友:很实用
  10. nvidia驱动,cuda与cudnn的关系