SpringBoot启动流程及⾃动装配原理分析
SpringBoot项⽬的main函数
常规的这个主类如下图所⽰,我们⼀般会这样去写。
在这个类中需要关注的是:
@SpringBootApplication
SpringApplication.run()
关于 @SpringBootApplication 注解,在后⾯分析SpringBoot⾃动装配的章节会展开去分析。
本章节中我们需要关注的就是 SpringApplication.run() ⽅法。
查看run()⽅法的实现,如下⾯代码所⽰,我们发现其⾸先是创建了 SpringApplication 的实例,然后调⽤了 SpringApplication 的run()⽅法,那本章我们关注的就是 SpringApplication 创建实例的过程。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
SpringApplication() 构造⽅法
继续查看源码, SpringApplication 实例化过程,⾸先是进⼊构造⽅法,最终回来到两个参数的构造⽅法。
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//推断应⽤类型,后⾯会根据类型初始化对应的环境。常⽤的⼀般都是servlet环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer
this.SpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化classpath下所有已配置的 ApplicationListener
this.SpringFactoriesInstances(ApplicationListener.class));
//根据调⽤栈,推断出 main ⽅法的类名
this.mainApplicationClass = this.deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath();该⽅法推断应⽤的类型。 SERVLET REACTIVE NONE
public enum WebApplicationType {
NONE,
NONE,
SERVLET,
REACTIVE;
// 常量值
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.t.Configu private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.active.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.t.WebApplicationContext" private static final String REACTIVE_APPL
ICATION_CONTEXT_CLASS = "org.springframework.t.ReactiveWebApp
private WebApplicationType() {
}
/**
* 判断应⽤的类型
* NONE: 应⽤程序不是web应⽤,也不应该⽤web服务器去启动
* SERVLET: 应⽤程序应作为基于servlet的web应⽤程序运⾏,并应启动嵌⼊式servlet web(tomcat)服务器。
* REACTIVE: 应⽤程序应作为 reactive web应⽤程序运⾏,并应启动嵌⼊式 reactive web服务器。
* @return
*/
static WebApplicationType deduceFromClasspath() {
//classpath下必须存在org.active.DispatcherHandler
if (ClassUtils.isPresent("org.active.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springfram return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
//classpath环境下存在javax.servlet.Servlet或者org.t.ConfigurableWebApplicationContext
return SERVLET;
}
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable("org.t.WebApplicationContext", applicationContextClass)) {
return SERVLET;
} else {
return isAssignable("org.springframework.t.ReactiveWebApplicationContext", applicationContextClass) ?
}
}
private static boolean isAssignable(String target, Class<?> type) {
try {
solveClassName(target, (ClassLoader)null).isAssignableFrom(type);
} catch (Throwable var3) {
return false;
}
}
}
返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三个枚举的解释如下:
WebApplicationType.REACTIVE classpath下存在org.active.DispatcherHandler
WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者org.t.ConfigurableWebApplicationContext
WebApplicationType.NONE 不满⾜以上条件。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
SpringFactoriesInstances(type, new Class[0]);
}
/**
* 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的⼯⼚实例
* @param type
* @param parameterTypes
* @param args
* @param <T>
* @return
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, args) {
ClassLoader classLoader = ClassLoader();
//通过指定的classLoader从 META-INF/spring.factories 的资源⽂件中,
//读取 key 为 Name() 的 value
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建Spring⼯⼚实例
List<T> instances = ateSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对Spring⼯⼚实例排序(annotation.Order注解指定的顺序)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
看看 getSpringFactoriesInstances 都⼲了什么,看源码,有⼀个⽅法很重要 loadFactoryNames() 这个⽅法很重要,它是spring-core中提供的从META-INF/spring.factories中获取指定的类(key)的同⼀⼊⼝⽅法。
在这⾥,获取的是key为 t.ApplicationContextInitializer 的类。
ApplicationContextInitializer 是Spring框架的类, 这个类的主要⽬的就是在ConfigurableApplicationContext调⽤refresh()⽅法之前,回调这个类的initialize⽅法。通过ConfigurableApplicationContext 的实例获取容器的环境Environment,从⽽实现对配置⽂件的修改完善等⼯作。SpringFactoriesInstances(ApplicationListener.class));
初始化classpath下 META-INF/spring.factories中已配置的 ApplicationListener。
ApplicationListener 的加载过程和上⾯的ApplicationContextInitializer 类的加载过程是⼀样的。不多说了,⾄于ApplicationListener 是spring的事件,典型的观察者模式,通过ApplicationEvent 类和ApplicationListener 接⼝,可以实现对spring容器全⽣命周期的监听,当然也可以⾃定义监听事件。
run⽅法的源码
/**
* 运⾏spring应⽤,并刷新⼀个新的 ApplicationContext(Spring的上下⽂)
* ConfigurableApplicationContext 是 ApplicationContext 接⼝的⼦接⼝。在 ApplicationContext
* 基础上增加了配置上下⽂的⼯具。 ConfigurableApplicationContext是容器的⾼级接⼝
*/
public ConfigurableApplicationContext args) {
//记录程序运⾏时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext Spring 的上下⽂
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//从META-INF/spring.factories中获取
//1、获取并启动
SpringApplicationRunListeners listeners = RunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2、构造应⽤上下⽂环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//处理需要忽略的Bean
//打印banner
Banner printedBanner = this.printBanner(environment);
//3、初始化应⽤上下⽂
context = ateApplicationContext();
spring framework组件//实例化SpringBootExceptionReporter.class,⽤来⽀持报告关于启动的错误
exceptionReporters = SpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.
//4、刷新应⽤上下⽂前的准备阶段
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新应⽤上下⽂
//刷新应⽤上下⽂后的扩展接⼝
this.afterRefresh(context, applicationArguments);
//时间记录停⽌
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).ApplicationLog(), stopWatch);
}
//发布容器启动完成事件
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
具体的每⼀⾏代码的含义请看注释,我们在这先总结⼀下启动过程中的重要步骤:
第⼀步:获取并启动
第⼆步:构造应⽤上下⽂环境
第三步:初始化应⽤上下⽂
第四步:刷新应⽤上下⽂前的准备阶段
第五步:刷新应⽤上下⽂
第六步:刷新应⽤上下⽂后的扩展接⼝
OK,下⾯SpringBoot的启动流程分析,我们就根据这6⼤步骤进⾏详细解读。最重要的是第四,五步。我们会着重的分析。
第⼀步:获取并启动
事件机制在Spring是很重要的⼀部分内容,通过事件机制我们可以监听Spring容器中正在发⽣的⼀些事件,同样也可以⾃定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供⽀持。当⼀个对象处理完某种任务后,通知另外的对象进⾏某些处理,常⽤的场景有进⾏某些操作后发送通知,消息、邮件等情况。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
在这⾥⾯是不是看到⼀个熟悉的⽅法:getSpringFactoriesInstances(),可以看下下⾯的注释,前⾯我们已经详细介绍过该⽅法是怎么⼀步步的获取到META-INF/spring.factories中的指定的key的value,获取到以后怎么实例化类的。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, args) {
ClassLoader classLoader = ClassLoader();
//通过指定的classLoader从 META-INF/spring.factories 的资源⽂件中,
//读取 key 为 Name() 的 value
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建Spring⼯⼚实例
List<T> instances = ateSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对Spring⼯⼚实例排序(annotation.Order注解指定的顺序)
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
回到run⽅法,debug这个代码 SpringApplicationRunListeners listeners = getRunListeners(args); 获取的是EventPublishingRunListener:
EventPublishingRunListener是Spring容器的启动。
listeners.starting(); 开启了监听事件。
第⼆步:构造应⽤上下⽂环境
应⽤上下⽂环境包括什么呢?包括计算机的环境,Java环境,Spring的运⾏环境,Spring项⽬的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。
⾸先看⼀下prepareEnvironment()⽅法。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论