SpringBoot项目通过SpringApplication.run(App.class, args)来启动:
@Configuration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
接下来,通过源码来看看SpringApplication.run()方法的执行过程。如果对源码不感兴趣,直接下拉到文章末尾,看启动框图。
1、调用SpringApplication类的静态方法
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[] { source }, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
2、SpringApplication对象初始化
public SpringApplication(Object... sources) {
initialize(sources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 判断是否为WEB环境
this.webEnvironment = deduceWebEnvironment();
// 找到META-INF/spring.factories中ApplicationContextInitializer所有实现类,并将其实例化
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 找到META-INF/spring.factories中ApplicationListener所有实现类,并将其实例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 获取当前main方法类对象,即测试类中的App实例
this.mainApplicationClass = deduceMainApplicationClass();
}
对象初始化过程中,使用到了getSpringFactoriesInstances方法:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
// 读取META-INF/spring.factories指定接口的实现类
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
T instance = (T) constructor.newInstance(args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
// 读取META-INF/spring.factories文件
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
META-INF/spring.factories文件内容,spring boot版本1.3.6.RELEASE # PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor
ApplicationListener接口是Spring框架的事件监听器,其作用可理解为SpringApplicationRunListener发布通知事件时,由ApplicationListener负责接收。SpringApplicationRunListener接口的实现类就是EventPublishingRunListener,其在SpringBoot启动过程中,负责注册ApplicationListener监听器,在不同时间节点发布不同事件类型,如果有ApplicationListener实现类监听了该事件,则接收处理。
public interface SpringApplicationRunListener {
/**
* 通知监听器,SpringBoot开始启动
*/
void started();
/**
* 通知监听器,环境配置完成
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* 通知监听器,ApplicationContext已创建并初始化完成
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* 通知监听器,ApplicationContext已完成IOC配置
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* 通知监听器,SpringBoot开始完毕
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
}
附图为ApplicationListener监听接口实现类,每个类对应了一种事件。
3、SpringApplication核心run方法
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 任务执行时间监听,记录起止时间差
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 启动SpringApplicationRunListener监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 创建并刷新ApplicationContext
context = createAndRefreshContext(listeners, applicationArguments);
afterRefresh(context, applicationArguments);
// 通知监听器,应用启动完毕
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
}
这里,需要看看createAndRefreshContext()方法是如何创建并刷新ApplicationContext。
private ConfigurableApplicationContext createAndRefreshContext(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableApplicationContext context;
// Create and configure the environment
// 创建并配置运行环境,WebEnvironment与StandardEnvironment选其一
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
// 是否打印Banner,就是启动程序时出现的图形
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}
// Create, load, refresh and run the ApplicationContext
// 创建、装置、刷新、运行ApplicationContext
context = createApplicationContext();
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
// 通知监听器,ApplicationContext创建完毕
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
// Load the sources
// 将beans载入到ApplicationContext容器中
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 通知监听器,beans载入ApplicationContext完毕
listeners.contextLoaded(context);
// Refresh the context
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
return context;
}
其中利用createApplicationContext()来实例化ApplicationContext对象,即DEFAULT_WEB_CONTEXT_CLASS 、DEFAULT_CONTEXT_CLASS两个对象其中一个。
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
postProcessApplicationContext(context)、applyInitializers(context)均为初始化ApplicationContext工作。
SpringBoot启动过程分析就先到这里,过程中关注几个对象:
ApplicationContext:Spring高级容器,与BeanFactory类似。
SpringApplicationRunListener:SprintBoot启动监听器,负责向ApplicationListener注册各类事件。
Environment:运行环境。
4、启动过程框图
5、接口文档
http://docs.spring.io/spring-framework/docs/current/javadoc-api/
总结
以上所述是小编给大家介绍的SpringBoot应用启动过程分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# springboot启动过程
# spring容器初始化遇到的死锁问题解决
# Spring Boot DevTools使用教程
# Spring之ShutDown Hook死锁现象解读
# 运行环境
# 过程中
# 小编
# 几个
# 在此
# 均为
# 执行时间
# 并将其
# 给大家
# 来看看
# 其中一个
# 图为
# 拉到
# 先到
# 所述
# 给我留言
# 感谢大家
# 类中
# 判断是否
# 疑问请
相关文章:
制作网站外包平台,自动化接单网站有哪些?
如何快速打造个性化非模板自助建站?
常州自助建站费用包含哪些项目?
婚礼视频制作网站,学习*后期制作的网站有哪些?
建站主机解析:虚拟主机配置与服务器选择指南
建站主机与虚拟主机有何区别?如何选择最优方案?
Swift中swift中的switch 语句
小米网站链接制作教程,请问miui新增网页链接调用服务有什么用啊?
如何在香港免费服务器上快速搭建网站?
如何在Windows虚拟主机上快速搭建网站?
攀枝花网站建设,攀枝花营业执照网上怎么年审?
建站之星如何快速解决建站难题?
定制建站模板如何实现SEO优化与智能系统配置?18字教程
小建面朝正北,A点实际方位是否存在偏差?
如何用y主机助手快速搭建网站?
免费ppt制作网站,有没有值得推荐的免费PPT网站?
网站代码制作软件有哪些,如何生成自己网站的代码?
已有域名能否直接搭建网站?
如何在宝塔面板中修改默认建站目录?
武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?
建站之星下载版如何获取与安装?
公众号网站制作网页,微信公众号怎么制作?
如何通过万网虚拟主机快速搭建网站?
,柠檬视频怎样兑换vip?
建站之星如何防范黑客攻击与数据泄露?
如何基于云服务器快速搭建个人网站?
如何通过WDCP绑定主域名及创建子域名站点?
宝塔建站助手安装配置与建站模板使用全流程解析
建站之星后台管理如何实现高效配置?
黑客入侵网站服务器的常见手法有哪些?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
定制建站流程解析:需求评估与SEO优化功能开发指南
自助网站制作软件,个人如何自助建网站?
如何快速生成橙子建站落地页链接?
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
如何通过商城免费建站系统源码自定义网站主题?
音响网站制作视频教程,隆霸音响官方网站?
公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?
长春网站建设制作公司,长春的网络公司怎么样主要是能做网站的?
道歉网站制作流程,世纪佳缘致歉小吴事件,相亲网站身份信息伪造该如何稽查?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
建站之星后台密码遗忘或太弱?如何重置与强化?
再谈Python中的字符串与字符编码(推荐)
如何通过商城自助建站源码实现零基础高效建站?
如何在云指建站中生成FTP站点?
定制建站方案优化指南:企业官网开发与建站费用解析
北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?
制作证书网站有哪些,全国城建培训中心证书查询官网?
存储型VPS适合搭建中小型网站吗?
建站ABC备案流程中有哪些关键注意事项?
*请认真填写需求信息,我们会在24小时内与您取得联系。