深入理解SpringBoot的过滤条件--AutoConfigure
我们知道在Spring及SpringBoot里按条件创建Bean的核心是Condition
接口与Conditional
注解,其实在SpringBoot里还有一种AutoConfigure也可以来过滤配置,只不过使用这种技术,能够让SpringBoot更快速的启动,那么下面我们就来看一下具体怎么实现的。
autoconfigure Module
SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties
进行配置。SpringBoot会将收集好的@Configuration
进行一次过滤进而剔除不满足条件的配置类。
演示示例
在我们创建好的SpringBoot项目里添加一个AutoConfiguration
:
package com.ys.zhshop.member.config;
import com.ys.zhshop.member.service.MemberRegisterService;
import org.springframework.context.annotation.Bean;
public class MemberAutoConfiguration {
@Bean
public MemberRegisterService registerService() {
return new MemberRegisterService();
}
}
在MemberRegisterService里的构造函数输出一段内容看看Spring是否帮我们初始化
紧接着在META-INF/spring.factories
里配置对应的引导:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ys.zhshop.member.config.MemberAutoConfiguration
随后我们需要在META-INF目录下创建一个spring-autoconfigure-metadata.properties
文件,内容如下:
com.ys.zhshop.member.config.MemberAutoConfiguration.ConditionalOnClass=java.lang.Strings
格式:自动配置的类全名.条件=值
在这里我们先指定一个类路径下不存在的Java类,启动后并没有相关信息的输出,那么把其值改成java.land.String
,那么我们启动可以发现:
在这里,我们可以在控制台看到构造函数输出的值,这就说明我们的Bean的的确确被创建了
下面我贴出一个spring-cloud-netflix-core下的配置,主要来看看这些条件该怎么写,大家如果想使用可以参考人家的来配置:
#Mon Jun 18 19:13:37 UTC 2018
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.ConditionalOnClass=com.netflix.hystrix.Hystrix,org.springframework.boot.actuate.health.HealthIndicator
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration=
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration=
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration.AutoConfigureAfter=org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration.Configuration=
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration.ConditionalOnClass=com.netflix.hystrix.Hystrix,org.springframework.security.core.context.SecurityContext
根据官网说法,使用这种配置方式可以有效的降低SpringBoot的启动时间,因为通过这种过滤方式能减少@Configuration
类的数量,从而降低初始化Bean时的耗时,官网原话描述如下:
Spring Boot uses an annotation processor to collect the conditions on auto-configurations in a metadata file (META-INF/spring-autoconfigure-metadata.properties). If that file is present, it is used to eagerly filter auto-configurations that do not match, which will improve startup time.
源码追踪
我们再次打开自动化配置的核心类AutoConfigurationImportSelector
,看看它的selectImport
方法:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);//1
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);//2
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);//3
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
- 在代码1处创建用于加载spring-autoconfigure-metadata.properties里的元数据,在这里我们可以看到其指定文件的位置:
protected static final String PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties";
2.在代码2处主要通过SpringFactoriesLoader加载spring.factories
里的EnableAutoConfiguration
3.根据AutoConfigurationMetaData进行一次过滤:
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);//代码1
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
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]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
return new ArrayList<>(result);
}
我们在这里主要关注代码1。OnClassCondition
是AutoConfigurationImportFilter
接口的实现类,我这里贴一下与主题相关的代码:
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = getConditionEvaluationReport();
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this,
outcomes[i]);
}
}
}
return match;
}
//.....
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
Set<String> candidates = autoConfigurationMetadata
.getSet(autoConfigurationClass, "ConditionalOnClass");
if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
}
return outcomes;
}
}
上述代码虽然多,但最终还是要调用AutoConfigurationMetadata
的getSet方法,我们继续追踪一下这个接口的实现类,它是位于AutoConfigurationMetadataLoader
的内部类:
/**
* {@link AutoConfigurationMetadata} implementation backed by a properties file.
*/
private static class PropertiesAutoConfigurationMetadata
implements AutoConfigurationMetadata {
//....省略其他代码
@Override
public Set<String> getSet(String className, String key) {
return getSet(className, key, null);
}
@Override
public Set<String> getSet(String className, String key,
Set<String> defaultValue) {
String value = get(className, key);
return (value != null ? StringUtils.commaDelimitedListToSet(value)
: defaultValue);
}
@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);
}
}
最后我们可以发现其get方法还是通过java.util.Properties
的getProperty方法来获取的值

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
不学无数——SpringBoot入门IV
SpringBoot 1.Profiles Spring Profiles能够在不同的环境中使不同的应用配置生效。@Component和@Configuration两个注解都能够通过@Profiles来标记。下面是例子: @Configuration @Profile("buxuewushu") public class ProductionConfiguration { // ... } 在配置文件中可以通过spring.profiles.active这个变量来控制哪个Profiles生效。例如,可以在application.properties配置文件中配置如下: spring.profiles.active=buxuewushu1,buxuewushu --即通过@Profiles注解标记的名为buxuewushu和buxuewushu1的文件生效 当然也可以通过命令行的形式进行配置:--spring.profiles.active= buxuewushu1, buxuewushu 1.1在代码中配置 在启动文件运行之前可以通过SpringApplication设置需要使哪一个配...
-
下一篇
剥开比原看代码06:比原是如何把请求区块数据的信息发出去的
作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 在前一篇中,我们说到,当比原向其它节点请求区块数据时,BlockKeeper会发送一个BlockRequestMessage把需要的区块height告诉对方,并把该信息对应的二进制数据放入ProtocolReactor对应的sendQueue通道中,等待发送。而具体的发送细节,由于逻辑比较复杂,所以在前一篇中并未详解,放到本篇中。 由于sendQueue是一个通道,数据放进去后,到底是由谁在什么情况下取走并发送,BlockKeeper这边是不知道的。经过我们在代码中搜索,发现只有一个类型会直接监视sendQueue中的数据,它就是前文出现的MConnection。MConnection的对象在它的OnStart方法中,会监视sendQueue中的数据,然后,等发现数据时,会将之取走并放入一个叫sending的通道里。 事情变得有点复杂了: 由前篇我们知道,一个MConnec...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 面试大杂烩
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS关闭SELinux安全模块
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2全家桶,快速入门学习开发网站教程
- MySQL数据库在高并发下的优化方案
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程