Spring Boot 源码分析(一)
sschrodinger
2019/05/28
Spring boot 简介
Spring boot 采用约定大于配置的思想对应用程序进行默认配置,减少了大量的配置时间。
Spring Boot 包含的特性如下:
- 创建可以独立运行的 Spring 应用
- 直接嵌入 Tomcat 或 Jetty 服务器,不需要部署 WAR 文件
- 提供推荐的基础 POM 文件来简化 Apache Maven 配置
- 尽可能的根据项目依赖来自动配置 Spring 框架
- 提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查
- 没有代码生成,也没有 XML 配置文件
通过 Spring Boot,创建新的 Spring 应用变得非常容易,而且创建出的 Spring 应用符合通用的最佳实践。只需要简单的几个步骤就可以创建出一个 Web 应用。对于 spring boot 的创建,参见 spring 官方启动器。
Spring boot 加载
因为 Spring boot 内置了 Tomcat 或 Jetty 服务器,不需要直接部署 War 文件,所以 spring boot 的程序起点为一个普通的主函数,主函数类如下:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
看似和普通的程序没有什么区别,其实他最主要的步骤都通过注解 @SpringBootApplication
和方法 SpringApplication.run()
完成了。
其实所有的依赖在这一步就可以完成注入,主要的步骤是 spring 读取所有依赖中 META-INF/spring.factories
文件,该文件指明了哪些依赖可以被自动加载,然后根据 ImportSelector
类选择加载哪些依赖,加载到 IoC 容器中。
META-INF/spring.factories
文件的读取
主函数中,只有一个方法,即 SpringApplication.run()
,如下:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
run
方法只是一个静态方法,调用的是 run(new Class<?>[] { primarySource }, args)
方法。如下:
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
其中,读取配置文件的操作就发生在 SpringApplication
类的实例化过程中。实例化的代码如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在 getSpringFactoriesInstances(ApplicationContextInitializer.class))
中 spring 读取了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 ?
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" 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()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
// output
// like:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration -> {LinkedList@1446} size = 118
// |
// |- org.springframework.boot.autoconfigure.EnableAutoConfiguration
}
该方法会将所有的 META-INF/spring.factories
缓存到 SpringFactoriesLoader
类中,在需要读取的时候进行读取。
总结
SpringFactoriesLoader
类的静态方法实现了依赖文件的读取,读取所有的配置类的全限定名名称
@SpringBootApplication 注解
@SpringBootApplication
是一个复合注解,如下:
@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
@Configuration
public @interface SpringBootConfiguration {}
有了这个注解,我们就可以把由 @SpringBootApplication
注解修饰的类作为配置类来使用,即在类 DemoApplication
中由 @Bean
注解修饰的代码都会被注入到 IoC 容器中由 Spring 统一管理。
注解三会自动扫描该类所在包,将各种组件注入到 IoC 容器中让其管理,所以我们也可以类 DemoApplication
上增加诸如 @Controller
的注解,将其作为一个 Controller。
最重要的是 @EnableAutoConfiguration
注解。
在 Spring 中,由 Enable 开头的注解的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。
@EnableAutoConfiguration 注解
@EnableAutoConfiguration
的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 注解一
@Import(AutoConfigurationImportSelector.class) // 注解二
public @interface EnableAutoConfiguration {
// ...
}
@Import
@Import
注解可以导入三种类:
-
@Configuration
注解修饰的类 -
ImportSelector
或ImportBeanDefinitionRegistrar
的实现类 - 普通的组件类,即由
@Componet
注解修饰的类。
ImportSelector
接口定义如下:
public interface ImportSelector {
// 返回哪些类需要被创建
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
该接口的主要作用是根据给定的选择条件(通常是一个或多个注解属性)导入哪个 @Configuration
类。
note
- 如果该接口的子类实现了如下四个接口,会先执行如下四个接口的函数:
- EnvironmentAware
- BeanFactoryAware
- BeanClassLoaderAware
- ResourceLoaderAware
- 如果希望在所有的
@Configuration
类都导入后再导入该类,则使用其子接口DeferredImportSelector
ImportSelector
类在 BeanFactory.refresh()
方法中调用,具体到应该是在 BeanFactory.refresh()
函数的 invokeBeanFactoryPostProcessors
中,具体参见 [Spring 源码分析(二)Core],invokeBeanFactoryPostProcessors
的主要作用是注册所有的 PostProcessors
,即 bean 的后处理器,需要在 sigleton 实例化之前调用。
主要方法如下:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ...
}
下面是 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
的方法代码,部分代码如下:
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 记录是否是定义类的 Processor 或者普通的 Processor
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// ...
// 应用 Bean 定义类的后置处理器
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// ...
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
invokeBeanDefinitionRegistryPostProcessors
函数对每一个定义类的后置处理器分别进行应用, @Configure
的解析就在这个函数中。如下:
// 从注册表中的配置类派生更多的bean定义
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// ...
this.registriesPostProcessed.add(registryId);
// Build and validate a configuration model based on the registry of Configuration classes.
processConfigBeanDefinitions(registry);
}
进入最关键的类 ConfigurationClassPostProcessor
,这个类用户来注册所有的 @Configure
和 @Bean
。他的 processConfigBeanDefinitions
函数如下:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 记录所有候选的未加载的配置
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 按照 Ordered 对配置进行排序
// 加载自定义 bean 名命策略
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 解译候选集
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
// ...
} while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
注意 process
中的 this.deferredImportSelectorHandler.process()
方法:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
这个方法就是处理实现了 deferredImportSelector
接口的方法。该方法会读取所有满足要求的类名并将其加入到 IoC 框架中。
该方法会读取静态类变量 SpringFactoriesLoader.cache
获得所有需要加载的类的全限定名。方法栈如下:
selectImports:10, RoleImportSelector (com.example.demo.config)
/^\
process:889, ConfigurationClassParser$DefaultDeferredImportSelectorGroup (org.springframework.context.annotation)
/^\
getImports:875, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
/^\
processGroupImports:801, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
/^\
process:771, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
/^\
parse:185, ConfigurationClassParser (org.springframework.context.annotation)
/^\
processConfigBeanDefinitions:315, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
postProcessBeanDefinitionRegistry:232, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:705, AbstractApplicationContext (org.springframework.context.support)
/^\
根据 @EnableAutoConfiguration
的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration
作为查找的 Key,获取对应的一组 @Configuration
类。
即从 classpath 中搜寻所有的 META-INF/spring.factories
配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration
对应的配置项通过反射(Java Refletion)实例化为对应的标注了 @Configuration
的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。
转载于:https://www.jianshu.com/p/e7a33e9eec0e