jframe移除指定组件_AutoConfiguration排除指定和过滤自动配置组件

AutoConfiguration排除指定组件

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

protected Set getExclusions (AnnotationMetadata metadata,AnnotationAttributes attributes) {//创建 Set 集合并把待排除的内容存于集合内,LinkedHashSet 具有不可重复性Set 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 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 . emptyList();}

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 configurat ions,Set exclusions) {List 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 invalidExcludes) {StringBuilder message = new StringBuilder();for (String exclude : invalidExcludes) {message . append("- "). append(exclude) . append(String . format( "%n"));throw new IllegalStateException(String. format("The following classes could not be excluded because they are"not auto-configuration classes:%n%s", message));}

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

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

AutoConfiguration 过滤自动配置组件

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

private List filter(List configurations,AutoConf igurationMetadata autoConfigurationMetadata)long startTime = System. nanoTime();String[] candidates = StringUtils. toStringArray( configurations);boolean[] skip = new boolean[ candidates . length];boolean skipped = false;for (AutoConfigurationImportFilter filter : getAutoConf igurationImportFilte-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 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 getAutoConfigurat ionImportFilters() {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 Filtersorg. 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 所示。

bb4c75bed22320709ced8a455bc07d09.png

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

@FunctionalInterfacepublic 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 SpringBootConditionimplements AutoConfigurationImportFilter, BeanF actoryAware, BeanClassLoaderAware {@Overridepublic boolean[] match(String[] autoConfigurationClasses,AutoConfigurat ionMetadata autoConfigurat ionMetadata) {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[ ] autoConfigurationClasses,AutoConfigurat ionMetadata autoConfigurationMetadata);}

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

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

@Order (Ordered . HIGHEST_ PRECEDENCE)class OnClassCondition extends FilteringSpringBootCondition {@Overrideprotected final ConditionOutcome[] get0utcomes (String[] autoConfigurationClasses ,AutoConfigurationMetadataautoConfigurationMetadata) {//如果有多个处理器,采用后台线程处理if (Runtime . getRuntime(). availableProcessors() > 1) {return resolveOutcomes Threaded( autoConfigurationClasses, autoConfigu-rationMetadata);} else {OutcomesResolver outcomesResolver = newStandardoutcomesResolver ( autoConfigurationClasses, 0,autoConfigurationClasses.length, autoConfigurationMetadata,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[ ] autoConfigurationClasses,AutoConfigurationMetadata autoConfigurationMetadata) {int split = autoConfigurationClasses. length / 2;OutcomesResolver firstHalfResolver = create0utcomesResolver( autoConfigurationClasses, 0,split, autoConfigurat ionMetadata) ;OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(au-toConfigurationClasses, split,autoConfigurationClasses. length, autoConfigurat ionMetadata, getBean-ClassLoader());ConditionOutcome[ ] secondHalf = secondHalfResolver . resolveOutcomes();ConditionOutcome[] firstHalf = firstHalfResolver . resolve0utcomes( );ConditionOutcome[] outcomes = new ConditionOutcome[ autoConfigurationClases. 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 OutcomesResolverprivate ConditionOutcome[] getOutcomes(String[ ] autoConfigurationClasseint start, int end, AutoConfigurationMetadata autoConfiguration-Metadata) {ConditionOutcome[] outcomes = new ConditionOutcome[end - start];for(inti=start;i

在获取元数据指定配置的功能时用到了 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 ionMetadataimplements AutoConfigurat ionMetadata {@Overridepublic String get(String className, String key) {return get(className, key, null);@Overridepublic 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 SpringBootConditionDimplements AutoConfigurationImportFilter, BeanF actoryAware, BeanClassLoaderAware {protected enum ClassNameFilter {PRESENT@Overridepublic boolean matches (String className, ClassLoader classLoader) {return isPresent(className, classLoader);},MISSING {@Overridepublic boolean matches (String className, ClassLoader classLoader) {return lisPresent(className, classLoader);};public abstract boolean matches (String className, ClassLoader classLoader);//通过类加载是否抛出异常来判断该类是否存在public static boolean isPresent(String className, ClassLoader classLoader) {if (classLoader == null) {classLoader = ClassUtils.getDefaultClassLoader();}try{forName (className, classLoader);return true;} catch (Throwable ex) {return false;}//进行类加载操作private static Class> forName ( String className, ClassLoader classLoader)throws Clas sNotFoundException {if (classLoader != null) {return classLoader . loadClass(className);return Class . forName( className);}}

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

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

d4e1a2ef0ebe017c3e25bdf7ff28ba0b.png

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值