14 Spring Boot 体系原理
14 Spring Boot 体系原理
前述
Spring Boot 设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。其特点如下。
- 创建独立的 Spring 应用程序。
- 嵌入的 Tomcat,无须部署 WAR 文件。
- 简化 Maven 配置。
- 自动配置 Spring。
- 提供生成就绪型功能,如指标、健康检查和外部配置。
- 绝对没有代码生成,以及对 XML 没有配置要求。
Spring Boot 使用代码示例见 https://gitee.com/shj.com/sample-projects/tree/master/back/sample-src-code/sample-spring-boot-demo
。
14.1 Spring Boot 源码安装
还是得按照书籍版本对应源码整以下。
Spring Boot v2.0.1.RELEASE 下载。
环境我们还是不折腾,源码只为阅读,检索代码,正常使用项目更方便。
14.2 第一个 Starter
使用自动配置功能封装一个 spring-jar,然后在客户端中引入该 jar 包。具体示例略。
14.3 探索 SpringApplication 启动 Spring
SpringApplication main
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 创建 context
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 初始化 context
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新 context
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
}
14.3.1 SpringContext 创建
SpringContext 创建
public class SpringApplication {
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
}
这里是 Spring 如何自动化启动 Tomcat 的关键,后续再详细介绍。
14.3.2 bean 的加载
bean 的加载
public class SpringApplication {
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载核心逻辑
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// bean 的加载,继续跟,里面调用的加载逻辑 2.5.1 配置文件封装就开始讲了
loader.load();
}
}
class BeanDefinitionLoader {
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
private int load(Resource source) {
if (source.getFilename().endsWith(".groovy")) {
if (this.groovyReader == null) {
throw new BeanDefinitionStoreException(
"Cannot load Groovy beans without Groovy on classpath");
}
return this.groovyReader.loadBeanDefinitions(source);
}
// 2.5.1 配置文件封装就开始讲了
return this.xmlReader.loadBeanDefinitions(source);
}
}
14.3.3 Spring 扩展属性的加载
Spring 扩展属性的加载
public class SpringApplication {
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
}
这个也是 Spring 核心逻辑,前面已经分析过了。
14.4 总结
Spring Boot 是按照约定大于配置的原则,内置了 Spring 原有的启动类,并在启动的时候启动和刷新。具体类参考 org.springframework.context.annotation.AnnotationConfigApplicationContext。
14.4 Starter 自动化配置原理
Starter 自动化配置原理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 注解如果去掉,一切的 Starter 都会失效,我们再看这个注解结构,来了解它是如何生效的。
我们找到 AutoConfigurationImportSelector 的 isEnabled 方法,后面我们逆向推看 isEnabled 方法是怎么被调用的。
14.4.1 spring.factories 的加载
spring.factories 的加载
我们直接通过 AutoConfigurationImportSelector 的 isEnabled 方法跟踪,找到调用的类。
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 使用 isEnabled
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 关注 getCandidateConfigurations
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
public abstract class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
}
我们可以看到硬编码读的固定配置文件 FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
14.4.2 factories 调用时序图
factories 调用时序图
META-INF/spring.factories 中的配置文件是如何与 Spring 整合的呢?其路径还是比较深的,我们下面流程来理清它的逻辑(逆向跟踪代码)。
流程仅供参考,各个版本源码不一样。
- AbstractApplicationContext
- refresh
- invokeBeanFactoryPostProcessors
- PostProcessorRegistrationDelegate
- invokeBeanDefinitionRegistryPostProcessors
- ConfigurationClassPostProcessor
- postProcessBeanDefinitionRegistry
- processConfigBeanDefinitions
- ConfigurationClassParser
- parse
- doProcessConfigurationClass
- AutoConfigurationImportSelector
- selectImports
这个调用链中最核心的就是 SpringBoot 使用了 Spring 提供的 BeanDefinitionRegistryPostProcessor 拓展点并实现了 ConfigurationClassPostProcessor 类,从而实现了 spring 之上的一系列逻辑扩展。
ConfigurationClassPostProcessor 类层次结构
ConfigurationClassPostProcessor
Object
BeanDefinitionRegistryPostProcessor
BeanFactoryPostProcessor
PriorityOrdered
Ordered
ResourceLoaderAware
Aware
ApplicationStartupAware
Aware
BeanClassLoaderAware
Aware
EnvironmentAware
Aware
14.4.3 配置类的解析
配置类的解析
AutoConfigurationImportSelector.selectImports 方法返回后的配置类又是如何进一步处理的呢?
class ConfigurationClassParser {
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
if (deferredImports == null) {
return;
}
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent(
(group == null ? deferredImport : group),
(key) -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
// 打个断点,调试下,可以看到有很多 XXAutoConfiguration,包括默认的和自定义的,如 MessageSourceAutoConfiguration
grouping.getImports().forEach((entry) -> {
ConfigurationClass configurationClass = configurationClasses.get(
entry.getMetadata());
try {
// 核心业务逻辑:配置文件的处理逻辑
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
}
上面对应 Spring 启动的时候会扫描所有 JAR 中的 spring.factories 定义的类,而这些对于用户来说如果不是通过调试信息可能根本就感知不到。
继续看 processImports 的处理逻辑。
Parser 解析流程
META-INF/spring.factories 中的配置文件是如何与 Spring 整合的呢?其路径还是比较深的,我们下面流程来理清它的逻辑(逆向跟踪代码)。
流程仅供参考,各个版本源码不一样。
其是 factories 调用时序图
更细粒度的突出解析过程的时序图
- ConfigurationClassPostProcessor
- postProcessBeanDefinitionRegistry
- processConfigBeanDefinitions
- ConfigurationClassParser
- parse
- processDeferredImportSelectors
- selectImports
- imports
- processImports
- processConfigurationClass
- doProcessConfigurationClass
- configurationClasses.put
- doProcessConfigurationClass
- processConfigurationClass
- ConfigurationClassBeanDefinitionReader
- loadBeanDefinitions(ConfigurationClassPostProcessor.processConfigBeanDefinitions 里面调用)
从流程中我们大致可以看到 Spring 的全局处理流程。
- ConfigurationClassPostProcessor 作为 Spring 扩展点是 Spring Boot 一系列功能的基础入口。
- ConfigurationClassParser 作为解析职责的基本处理类,涵盖了各种解析处理的逻辑,如 @Import、@Bean、@ImportResource、@PropertySource、@ComponentScan 等注解都是在这个注解类中完成的,而这个类对外开发的函数入口就是 parse 方法。
- 在 processImports 中最后,所有的解析结构放在了 configurationClasses 中,然后委托给 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitions 注册。
- 其中,在 parse 中的处理是最复杂的,parse 中首先会处理自己本身能扫描到的 bean 注册逻辑,然后才会处理 spring.factories 定义的配置。处理 spring.factories 定义的配置首先就是要加载配置类,这个时候 EnableAutoConfigurationImportSelector 提供的 selectImports 就被派上用场了,它返回的配置类需要进行进一步的解析,因为这些配置类中可能对应不同的类型,如 @Import、@Bean、@ImprtResource、@PropertySource、@ComponentScan,而这些类型又是又有不同的处理逻辑,例如 ComponentScan,我们就能猜到这里面除了解析外一定还会有递归解析的处理逻辑,而且很有可能通过 ComponentScan 又扫描除了另一个 ComponentScan 配置。
14.4.4 ComponentScan 的切入点
ComponentScan 的切入点
class ConfigurationClassParser {
// configClass 就是 spring.factories 中定义的配置类
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 获取对应的注解配置信息,也就是对应的 @ComponentScan 中最主要的扫描路径信息,然后委托给 parse 进一步扫描。
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 对扫描出来的类进行过滤
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
// 将所有扫描出来的类委托到 parse 方法中递归处理
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
}
public class AnnotationConfigUtils {
static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata,
Class<?> containerClass, Class<?> annotationClass) {
return attributesForRepeatable(metadata, containerClass.getName(), annotationClass.getName());
}
static Set<AnnotationAttributes> attributesForRepeatable(
AnnotationMetadata metadata, String containerClassName, String annotationClassName) {
Set<AnnotationAttributes> result = new LinkedHashSet<>();
// Direct annotation present?
addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false));
// Container annotation present?
Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false);
if (container != null && container.containsKey("value")) {
for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
addAttributesIfNotNull(result, containedAttributes);
}
}
// Return merged result
return Collections.unmodifiableSet(result);
}
}
ComponentScanAnnotationParser.parse
继续跟踪 parse 方法,找到 ConfigurationClassParser 的 doProcessConfigurationClass,里面会调用 ComponentScanAnnotationParser.parse 方法。
class ComponentScanAnnotationParser {
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
// scopeProxy 属性构造
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
// resourcePattern 属性构造
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// includeFilters 设置
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// excludeFilters 属性设置
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// basePackages 设置
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
它的 Spring 原生的解析类,这是 Spring 核心解析类,它通过字节码扫描的方式,效率要比通常我们用的反射机制要高很多。
14.5 Conditional 机制的实现
14.5.1 Conditional 使用
ConditionalOnProperty 使用示例见 com.spring.boot.demo.controller.ConditionController。
14.5.2 Conditional 原理
Conditional 原理
ctrl+shift+f
搜索 ConditionalOnProperty.class,在 scope 中可以看到,所有的调用都出现在一个类 OnPropertyCondition 中,其中仅有一个 public 方法。
class OnPropertyCondition extends SpringBootCondition {
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// 扫描配置的 ConditionalOnProperty 信息,可以 DEBUG 看看
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(
ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
// 核心的验证逻辑
ConditionOutcome outcome = determineOutcome(annotationAttributes,
context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
PropertyResolver resolver) {
Spec spec = new Spec(annotationAttributes);
List<String> missingProperties = new ArrayList<>();
List<String> nonMatchingProperties = new ArrayList<>();
// 初始化 missingProperties、nonMatchingProperties
spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
// 不匹配情况 1,缺失 missingProperties
if (!missingProperties.isEmpty()) {
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property", "properties")
.items(Style.QUOTE, missingProperties));
}
// 不匹配情况 2,对应不匹配逻辑
if (!nonMatchingProperties.isEmpty()) {
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.found("different value in property",
"different value in properties")
.items(Style.QUOTE, nonMatchingProperties));
}
return ConditionOutcome.match(ConditionMessage
.forCondition(ConditionalOnProperty.class, spec).because("matched"));
}
private void collectProperties(PropertyResolver resolver, List<String> missing,
List<String> nonMatching) {
for (String name : this.names) {
String key = this.prefix + name;
if (resolver.containsProperty(key)) {
// 使用 PropertyResolver 验证对应的属性是否存在
if (!isMatch(resolver.getProperty(key), this.havingValue)) {
nonMatching.add(name);
}
}
else {
if (!this.matchIfMissing) {
missing.add(name);
}
}
}
}
}
public class ConditionOutcome {
private final boolean match;
private final ConditionMessage message;
public ConditionOutcome(boolean match, ConditionMessage message) {
Assert.notNull(message, "ConditionMessage must not be null");
this.match = match;
this.message = message;
}
public static ConditionOutcome noMatch(ConditionMessage message) {
return new ConditionOutcome(false, message);
}
public static ConditionOutcome match(ConditionMessage message) {
return new ConditionOutcome(true, message);
}
}
14.5.3 调用切入点
14.5.3 调用切入点
对照前述 Parser 解析流程
解析链路,切入点在 ConfigurationClassParser.processConfigurationClass 中。
class ConfigurationClassParser {
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 整个 condition 逻辑生效的切入点,如果验证不通过则会直接忽略掉后面的解析逻辑
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
}
shouldSkip
class ConfigurationClassParser {
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
List<Condition> conditions = new ArrayList<>();
// condition 的获取,获取某一个特定类的解析,metadata 中包含了完整的配置类信息
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// condition 的匹配,condition 对应的运行态类为 OnPropertyCondition
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
}
class ConditionEvaluator {
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
Object values = (attributes != null ? attributes.get("value") : null);
return (List<String[]>) (values != null ? values : Collections.emptyList());
}
}
14.6 属性自动化配置实现
14.6.1 示例
使用示例还是它,见 com.spring.boot.demo.controller.ConditionController,我们添加了 @Value
注解读取配置信息。
14.6.2 原理
原理
ctrl+shift+f
搜索 Value.class,找到一个调用点 QualifierAnnotationAutowireCandidateResolver.class。
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
private Class<? extends Annotation> valueAnnotationType = Value.class;
protected Object findValue(Annotation[] annotationsToSearch) {
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
if (attr != null) {
// 打个断点,DEBUG 下,可以看到在此停留
return extractValue(attr);
}
return null;
}
protected Object extractValue(AnnotationAttributes attr) {
Object value = attr.get(AnnotationUtils.VALUE);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
}
找到 extractValue,我们继续跟踪,找到以下两个问题答案。
- 表达式对应的值是在哪里被替换的?
- 表达式替换后的值又是如何与原有的 bean 整合的?
resolveEmbeddedValue
继续 extractValue 方法 DEBUG 向下走,进入 DefaultListableBeanFactory.resolveEmbeddedValue 方法,里面会将属性值替换。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ...
// 里面对应上面 QualifierAnnotationAutowireCandidateResolver.findValue 解析逻辑
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
// ...
String strVal = resolveEmbeddedValue((String) value);
}
}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
public String resolveEmbeddedValue(@Nullable String value) {
if (value == null) {
return null;
}
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
// 属性的解析委托给了 StringValueResolver 类处理
result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
}
return result;
}
}
PropertySourcesPlaceholderConfigurer 结构
StringValueResolver 功能实现依赖 Spring 的切入点 PropertySourcesPlaceholderConfigurer。
- PropertySourcesPlaceholderConfigurer
- PlaceholderConfigurerSupport
- PropertyResourceConfigurer
- PropertiesLoaderSupport
- BeanFactoryPostProcessor
- PriorityOrdered
- Ordered
- BeanNameAware
- Aware
- BeanFactoryAware
- Aware
- PropertyResourceConfigurer
- EnvironmentAware
- Aware
- PlaceholderConfigurerSupport
StringValueResolver 初始化过程
- PropertySourcesPlaceholderConfigurer
- postProcessBeanFactory
- 实例化(依赖 this.environment)。MutablePropertySources
- 实例化(依赖 propertySources)。PropertySourcesPropertyResolver
- processProperties
- 初始化属性。PropertySourcesPropertyResolver
- 返回 PropertyResolver。PropertySourcesPropertyResolver
- 实例化(依赖于 PropertyResolver)。StringValueResolver
- doProcessProperties
- addEmbeddedValueResolver
- postProcessBeanFactory
1. 初始化 MutablePropertySources
首先会通过 this.environment 来初始化 MutablePropertySources。这里面有几点需要说明。
- environment 是 Spring 属性加载的基础,里面包含了 Spring 已经加载的各个属性,而之所以使用 MutablePropertySources 封装,是因为 MutablePropertySources 还能实现单独加载自定义的额外属性的功能。
2. 初始化 PropertySourcesPropertyResolver
使用 PropertySourcesPropertyResolver 对 MutablePropertySources 的操作进行进一步封装,使得操作多个文件属性对外部不感知。当然 PropertySourcesPropertyResolver 还提供一个重要的功能就是对变量的解析,如下代码。
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
propertyResolver.setValueSeparator(this.valueSeparator);
}
}
/**
* 对应上面解析逻辑
*/
public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer
implements BeanNameAware, BeanFactoryAware {
/** Default placeholder prefix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/** Default placeholder suffix: {@value}. */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/** Default value separator: {@value}. */
public static final String DEFAULT_VALUE_SEPARATOR = ":";
}
3. StringValueResolver 初始化
StringValueResolver 存在的目的主要是对解析逻辑的进一步封装,例如通过变量 ignoreUnresolvablePlaceholders 来控制是否对变量做解析,代码如下。
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
StringValueResolver valueResolver = strVal -> {
// resolvePlaceholders 表示如果变量无法解析则忽略
// resolveRequiredPlaceholders 表示如果变量无法解析则抛异常
String resolved = (this.ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (this.trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(this.nullValue) ? null : resolved);
};
}
}
4. StringValueResolver 注册
最后将 StringValueResolver 示例注册到单例 ConfigurableListableBeanFactory 中,也就是在真正解析变量时使用的 StringValueResolver 实例。
这里有一个关键点,就是在初始化 MutablePropertySources 的时候依赖的一个变量是 environment。Environment 是 Spring 所有配置文件转换为 KV 的基础,而后续的一系列操作都是在 environment 基础上做的进一步封装,我们再来探索下 environment 的实现路径。
4.1 environment 初始化过程
初始化过程如下。
- ConfigFileApplicationListener
- onApplicationEnvironmentPreparedEvent
- postProcessEnvironment
- ConfigFileApplicationListener
- postProcessEnvironment
- addPropertySources
- load(重点)
- ConfigFileApplicationListener
- load
- addConfigurationProperties
- addLast
- ConfigurableEnvironment
- addLast
4.2 load 方法
public class ConfigFileApplicationListener
implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
public void load() {
this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
// 通过 profile 标记不同的环境
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
private void initializeProfiles() {
Set<Profile> initialActiveProfiles = initializeActiveProfiles();
this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles));
if (this.profiles.isEmpty()) {
// 未配置环境,则取默认的环境
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
if (!this.profiles.contains(defaultProfile)) {
this.profiles.add(defaultProfile);
}
}
}
// 支持不添加任何 profile 注解的 bean 的加载
this.profiles.add(null);
}
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// Spring Boot 默认从 4 个位置查找 application.properties 文件就是从 getSearchLocations() 方法返回。
// 1. 当前目录下的 /config 目录
// 2. 当前目录
// 3. 类路径下的 /config 目录
// 4. 类路径根目录
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
// 如果没有配置则默认从 application.properties 中加载,约定大于配置
Set<String> names = (isFolder ? getSearchNames() : NO_SEARCH_NAMES);
names.forEach(
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
}
8 Tomcat 启动
Tomcat 启动入口
启动 Spring Boot 程序,可以看到如下日志。
2025-03-30 16:24:23.026 INFO 13180 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path ''
14.3.1 SpringContext 创建
中我们曾经提到过以下代码。
public class SpringApplication {
// 默认配置
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
}
默认配置如上代码。在 6.2 拓展功能
我们讲解了 AbstractApplicationContext.refresh()。
refresh()
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 初始化前的准备工作,例如对系统属性或者环境变量进行准备及验证。
// 准备刷新的上下文环境
prepareRefresh();
// 2. 初始化 BeanFactory,并进行 XML 文件读取。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 对 BeanFactory 进行各种功能填充。
prepareBeanFactory(beanFactory);
try {
// 4. 子类覆盖方法做额外的处理。
postProcessBeanFactory(beanFactory);
// 5. 激活各种 BeanFactory 处理器。
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册拦截 bean 创建的 bean 处理器,这里只是注册,真正的调用是在 getBean 时候。
registerBeanPostProcessors(beanFactory);
// 7. 为上下文初始化 Message 源,即对不同语言的消息体进行国际化处理。
initMessageSource();
// 8. 初始化应用消息广播器,并放入 “applicationEventMulticaster” bean 中。
initApplicationEventMulticaster();
// 9. 留给子类来初始化其他的 bean。
onRefresh();
// 10. 在所有注册的 bean 中查找 listener bean,注册到消息广播器中。
registerListeners();
// 11. 初始化剩下的单实例(非惰性的)。
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新过程。通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exc to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
}
AnnotationConfigServletWebServerApplicationContext 类层次结构
AnnotationConfigServletWebServerApplicationContext 正式拓展了这个类,如下是其类层次结构(类层级有点多,省略部分)。
- AnnotationConfigServletWebServerApplicationContext
- ServletWebServerApplicationContext
- GenericWebApplicationContext
- GenericApplicationContext
- ConfigurableWebApplicationContext
- ThemeSource
- ConfigurableWebServerApplicationContext
- ConfigurableApplicationContext
- WebServerApplicationContext
- ApplicationContext
- GenericWebApplicationContext
- AnnotationConfigRegistry
- ServletWebServerApplicationContext
onRefresh()
关键函数。
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 核心方法,DEBUG 看下,实例为 TomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
}
ServletWebServerFactory 是服务器启动的上层抽象,无论是 Tomcat 还是 Jetty 都要通过这个类实现对 Spring 服务器的注册。我们打个断点看下它的返回结果为 TomcatServletWebServerFactory。
TomcatServletWebServerFactory 的调用
ctrl+shift+f
搜索 TomcatServletWebServerFactory,在 scope 中可以找到如下代码。
class ServletWebServerFactoryConfiguration {
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory() {
return new JettyServletWebServerFactory();
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
return new UndertowServletWebServerFactory();
}
}
}
这个类包含了 Tomcat、Jetty、Undertow 3 种类型的服务器自动注册逻辑,而选择条件则是通过 @ConditionalOnClass 注解控制。ConditionalOnClass 逻辑:对应的类在 classpath 目录下存在时,才会去解析对应的配置文件。
Tomcat 异步启动
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 异步启动 tomcat
return getTomcatWebServer(tomcat);
}
}