您现在的位置是:首页 > 文章详情

深入理解SpringBoot的过滤条件--AutoConfigure

日期:2018-07-22点击:358

     我们知道在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,那么我们启动可以发现:
415409F1_B9A5_4BAA_89EB_59931DCC4A75
     在这里,我们可以在控制台看到构造函数输出的值,这就说明我们的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. 在代码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。OnClassConditionAutoConfigurationImportFilter接口的实现类,我这里贴一下与主题相关的代码:

@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方法来获取的值

原文链接:https://yq.aliyun.com/articles/617718
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章