Spring源码剖析-SpringBoot启动流程
在Spring源码剖析的前三篇⽂章,我们介绍了ApplicationContext、Bean相关内容、BeanPostProcessor的内容;但从普遍反馈和⾃⼰事后阅读的体验来看,⽂章过长,没有重点,条理并不是特别清楚。想必是写作⽅式出了问题,最突出的莫过于流⽔账式写法,虽然写作的⽬的并不⼀定是写出好的⽂章,⽽是主要服务⾃⼰,但时间⼀长,⾃⼰也是个普通的读者,同样会看不⼤懂。
因此,写作⽅法是需要变更了:要突出条理和重点,如需⼤段源码讲解,可在⽂章最后增加源码解析⼀节,读者可选读。也就是说,长度还是那么长,但可读性增强了很多。
本⽂我们关注SpringBoot启动时做了什么,这是理解⾃动注解的基础,下⼀篇⽂章我们将探索⾃动注解的实现⽅式。
⼀个最简单的SpringBoot应⽤,可以是这样
@SpringBootApplication
class MyApplication
fun main(args: Array<String>){
SpringApplication(MyApplication::class.java).run(*args)
}
那么重点落在SpringApplication类和其run⽅法上。
SpringApplication
SpringApplication是⼀个⼤类,包含了所有应⽤启动流程。包含的主要步骤如下
创建ApplicationContext
注册CommandLinePropertySource到Environment,⽤于将命令⾏参数暴露成容器中的属性值
刷新容器,即调⽤容器的refresh()⽅法
创建CommandLineRunner并调⽤其run()⽅法
关键点
SpringApplication能够启动三种类型的应⽤
普通的Servlet应⽤
响应式的Reactive应⽤
普通的⾮Web应⽤
不同的应⽤,会创建对应的ApplciationContext实现类,在ApplicationContext源码分析那⼀节有所描述
SpringApplication引⼊了⼀个新的临时容器,Bootstrapper、BootstrapRegistry,⽤于在创建出ApplicationContext实例之前管理启动阶段的Bean
SpringApplication提供ApplicationContextInitializer接⼝,⽤于在ApplicationContext刷新前修改它,这为我们提供了⼀个⾃定
义ApplicationContext的扩展点。
向SpringApplication注册它的⽅式有两个
调⽤SpringApplication.addInitializers()⽅法⼿动注册,⼿写代码时候可⽤
放在META-INF/spring.factories中,⾃定义starter时可⽤
SpringApplication的核⼼ —— META-INF/spring.factories,启动时,会从所有jar包的该⽂件中搜寻指定factory类型的实
现,SpringBoot的诸多特性,⽐如⾃动配置,都是依赖于该机制实现。加载该⽂件有专门的类——SpringFactoriesLoader。在SpringApplication中,加载的类有四种
Bootstrapper:SpringBoot启动阶段⽤
ApplicationContextInitializer:Spring上下⽂初始化器
SpringApplicationRunListener:SpringApplication启动时候的
ApplicationListener:Spring事件
这其中最重要的当属ApplicationContextInitializer,考虑到其能⼒,SpringBoot的诸多功能应该都是由其⼦类实现。
如果我们要查看哪些初始化器⽣效了,可以去对应jar包的spring.factories下查看。⽐如要看⾃动配置,去spring-boot-
autoconfigure包下的spring.factories⽂件下查看(我们将在下⼀篇⽂章研究这个)
SpringApplication提供的⾃定义点
SpringApplication启动时也⽀持⾼级写法,可⾃定义更多内容,再run,⽐如
fun main(args: Array<String>){
// 先创建SpringApplication实例,再设置该实例,⾃定义⼀些内容
springApplicationContext =SpringApplication(MylogApplication::class.java).run{
addInitializers(MylogApplication())
run(*args)
}
}
这是除了spring.factories外,Spring为我们提供了的额外定义⽅法,主要可⾃定义如下内容:
主类
WebApplicationType
是否允许Bean覆盖
springboot是啥Bean是否延迟加载
Banner的模式:不显⽰、显⽰在控制台、显⽰在⽇志中
命令⾏参数否写⼊Spring环境
是否将ApplicationConversionService添加到ApplicationContext的转换服务中
添加Bootstrapper
添加BootstrapRegistryInitializer
设置默认属性,会被添加到环境变量中
设置额外的profile
设置BeanNameGenerator
设置ConfigurableEnvironment
添加其它需要被添加到容器的资源,主要是指Configuration的资源
设置ResourceLoader
设置环境变量前缀,当从系统中获取环境变量时,将会应⽤该前缀
设置ConfigurableApplicationContext,相当于⼿动指定容器的类型了
设置ApplicationContextFactory
设置ApplicationContextInitializer,即添加⾃定义的初始化器
添加ApplicationListener,即事件
设置ApplicationStartup,该类并没有什么⽤,仅⽇志输出⽤
还缺啥
如上,加上后⾯的源码分析,我们只看到了SpringApplication的启动流程,貌似并没有看到关键之所在
application.properties配置⽂件的加载原理
⾃动配置加载的原理
这些其实在spring-boot.jar和spring-boot-autoconfigure.jar中的META-INF/spring.factories有指定,出于篇幅,我们下篇⽂章再讨论。源码分析
这次源码分析我们单独放在⼀边
构造⽅法
public SpringApplication(Class<?>... primarySources){
// 传⼊的类叫做主要源,这也说明了它的重要性
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources){
this.primarySources =new LinkedHashSet<>(Arrays.asList(primarySources));
// 检测应⽤类型”响应式、Servlet、普通应⽤
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置启动器
this.bootstrappers =new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 设置初始化器
setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置
setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));
// 检测主类
this.mainApplicationClass =deduceMainApplicationClass();
}
新出现的类
WebApplicationType:⼀个枚举,包含三个值:SERVLET、REACTIVE、NONE。⽤于决定应⽤是否需要启动特定类型的web服务器Bootstrapper:⽤于初始化BootstrapRegistry
public interface Bootstrapper {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void intitialize(BootstrapRegistry registry);
}
BootstrapRegistry:启动注册器,在服务启动阶段到ApplicationContext准备好这段时间有效;作⽤有两个:创建创建过程⽐较复杂的Bean、创建需要在ApplicationContext准备好之前共享的Bean。可以理解为预加载容器,即SpringBoot启动阶段使⽤的容器。
ApplicationContextInitializer:⽤于初始化ApplicationContext,调⽤时机在fresh()之前。
也就是说,在SpringBoot中,可以添加⾃⼰的ApplicationContextInitializer在容器初始化时修改内容。
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>{
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
获取⼯⼚对象 - getSpringFactoriesInstances
听这个⽅法,像是抽象⼯⼚模式,但总体看下来不⼤像,如果我们只是把它们当成⼀种插件机制,会更加⽅便理解。
getSpringFactoriesInstances()是贯穿SpringBoot启动的静态⽅法,⽤于加载指定类型的实例,来源是所有包下的META-INF/spring.factories⽂件中指定的类型。其作⽤就是获取指定类型的所有实现类的实例。
我们⾸先温习⼀下spring.factories⽂件长啥样
org.v.EnvironmentPostProcessor=\
batisplus.autoconfigure.SafetyEncryptProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
batisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
batisplus.autoconfigure.MybatisPlusAutoConfiguration
然后再来看这个⽅法
private<T> Collection<T>getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, args){
ClassLoader classLoader =getClassLoader();
// 获取指定类型的完整类名
Set<String> names =new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建它们
List<T> instances =createSpringFactoriesInstances(type, parameterTypes, classLoader, args, name
s);
// 按照@Order注解排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 直接调⽤了反射创建实例
private<T> List<T>createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> nam es){
List<T> instances =new ArrayList<>(names.size());
for(String name : names){
try{
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = DeclaredConstructor(parameterTypes);
T instance =(T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}catch(Throwable ex){
throw new IllegalArgumentException("Cannot instantiate "+ type +" : "+ name, ex);
}
}
return instances;
}
理解上⾯那段代码的重点,⼜落在了SpringFactoriesLoader.loadFactoryNames(type, classLoader)上。
// 注意这个spring.factories的位置
public static final String FACTORIES_RESOURCE_LOCATION ="META-INF/spring.factories";
public static<T> List<T>loadFactories(Class<T> factoryType,@Nullable ClassLoader classLoader){
ClassLoader classLoaderToUse = classLoader;
if(classLoaderToUse ==null){
classLoaderToUse = ClassLoader();
}
// 关键⽅法,根据指定的⼯⼚类型,获取其实现类们的名字
List<String> factoryImplementationNames =loadFactoryNames(factoryType, classLoaderToUse);
if(logger.isTraceEnabled()){
}
List<T> result =new ArrayList<>(factoryImplementationNames.size());
for(String factoryImplementationName : factoryImplementationNames){
// 反射实例化这些⼯⼚实现类
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
// 按照Order和Priority注解排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
/
**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论