Springboot Code的启动源码是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
项目启动的流程:
(一)new SpringApplication
配置source和web环境;
创建初始化构造器和应用监听器;
配置应用的主方法所在类;
(二)run 第一二部分
1.获取并启动监听器
初始化计时stopWatch、启动上下文bootstrapContext、设置系统参数headless;
初始化监听器列表SpringApplicationRunListeners;
发布springboot开始启动事件(从applicationListeners中过滤出4个能监听ApplicationStartingEvent事件的,并启动它们)
2.准备环境
装配命令行参数applicationArguments(对象中装载4个propertySource);
准备应用程序运行的环境ConfigurableEnvironment(从applicationListeners中过滤出6个能监听ApplicationEnvironmentPreparedEvent事件的,并启动它们。监听器中关联启动了一些后置处理器处理数据,最终目的是为应用环境做准备)
3.打印banner
public ConfigurableApplicationContext run(String... args) {
//1、StopWatch简单的看成一个stop watch的机制,保存stop的记录信息。
//初始化一个计时器,并开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化启动上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
ConfigurableApplicationContext context = null;
//2、configureHeadlessProperty即配置headless模式,这种模式是一种系统缺少显示设备、键盘和鼠标外设的情况模式。
this.configureHeadlessProperty();
//3、SpringApplicationListeners为SpringApplicationRunListener接口实现集合(创建SpringApplicationRunListener初始化构造器)初始化监听器列表
//可以理解这个接口就是在spring启动整个过程都需要回调这些listener
//debug能发现,拿到了一个名为EventPublishingRunListener(RunListener构造方法中关联上了全部applicationListener),这个就是用来进行触发publishEvent的被观察者
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//启动EventPublishingRunListener,从而过滤并启动相关Listener
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//4、ConfigurableEnvironment为配置环境对象,简单理解所有的配置信息汇总在这个对象中
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//5、Banner就是我们常在控制台输出的画面横幅,可以使用图片或者文本进行替换
Banner printedBanner = this.printBanner(environment);
=====> //6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。
this.refreshContext(context);
//9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。
this.afterRefresh(context, applicationArguments);
//10、然后根据stopwatch打印出启动时间
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
//11、这里调用ApplicationRunner和CommandLineRunner的实现类
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
下面开始对context的创建与准备
context = this.createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
public interface ApplicationContextFactory {
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch(webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
} catch (Exception var2) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
}
};
ConfigurableApplicationContext create(WebApplicationType webApplicationType);
....
}
上述代码可以通过webApplicationTyep(即SERVLET)创建 AnnotationConfigServletWebServerApplicationContext 对象,对象创建过程中,初始化了父类的属性值,其中有三个比较关键的值,reader、scanner和父类中的beanFactory,下面这个对象的层级结构需要了解一下
/**
* reader 和 scanner 都是在构造方法中进行了赋值
**/
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
private final Set<Class<?>> annotatedClasses;
private String[] basePackages;
public AnnotationConfigServletWebServerApplicationContext() {
this.annotatedClasses = new LinkedHashSet();
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
...
}
我们可以在 GenericApplicationContext 类的构造方法中看到其构造方法中,对beanFactory属性的赋值
public GenericApplicationContext() {
this.customClassLoader = false;
this.refreshed = new AtomicBoolean();
this.beanFactory = new DefaultListableBeanFactory();
}
//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
=====> //7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。
this.refreshContext(context);
//9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。
this.afterRefresh(context, applicationArguments);
//10、然后根据stopwatch打印出启动时间
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
//11、这里调用ApplicationRunner和CommandLineRunner的实现类
this.callRunners(context, applicationArguments);
/**
* Spring容器准备
*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置上下文环境
context.setEnvironment(environment);
//
postProcessApplicationContext(context);
// 执行所有ApplicationContextInitializer对象的initialize方法(这些对象是通过读取spring.factories加载)
applyInitializers(context);
// 发布上下文准备完成事件到所有监听器
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
//
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载bean到上下文
load(context, sources.toArray(new Object[0]));
// 发送上下文加载完成事件
listeners.contextLoaded(context);
}
//为context设置beanFactoryPostProcess
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
//调用全部的构造器
protected void applyInitializers(ConfigurableApplicationContext context) {
Iterator var2 = this.getInitializers().iterator();
while(var2.hasNext()) {
ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
//加载class com.learning.demo.DemoApplication 启动类
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.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);
}
loader.load();
}
debug,此处load方法可以跟进多层到 AnnotatedBeanDefinitionReader 的 registerBean(启动类)
public void registerBean(Class<?> beanClass) {
this.doRegisterBean(beanClass, (String)null, (Class[])null, (Supplier)null, (BeanDefinitionCustomizer[])null);
}
这里同样有两个listeners.*的两个方法,和前面listeners.starting()是一样的,提供下最近整理的类图(了解Runlistener和listener类之间的关系)
//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
=====> //8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。
this.refreshContext(context);
//9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。
this.afterRefresh(context, applicationArguments);
//10、然后根据stopwatch打印出启动时间
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
//11、这里调用ApplicationRunner和CommandLineRunner的实现类
this.callRunners(context, applicationArguments);
refreshContext() 是整个Run方法的核心部分
/**
* 刷新应用程序上下文
*
* @param context
*/
private void refreshContext(ConfigurableApplicationContext context) {
// 注册一个关闭钩子,在jvm停止时会触发,然后退出时执行一定的退出逻辑
if (this.registerShutdownHook) {
try {
// 添加:Runtime.getRuntime().addShutdownHook()
// 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook)
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
// ApplicationContext真正开始初始化容器和创建bean的阶段
refresh((ApplicationContext) context);
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
调用应用上下文对象的refresh()方法,接下来我i门到ConfigurableApplicationContext类中去看下这个方法
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
void refresh() throws BeansException, IllegalStateException;
}
这是一个接口,且这个类是在spring框架中,非springboot,它的实现类共有三个
AbstractApplicationContext是一个抽象类,其余两个类都继承了它,我们来看看这个抽象类的代码:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 第一步:准备更新上下时的预备工作
prepareRefresh();
// 第二步:获取上下文内部BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第三步:对BeanFactory做预备工作
prepareBeanFactory(beanFactory);
try {
// 第四步:允许在上下文子类中对bean工厂进行post-processing
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 第五步:调用上下文中注册为bean的工厂 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册拦截bean创建的拦截器
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 第七步:初始化MessageSource(国际化相关)
initMessageSource();
// 第八步:初始化容器事件广播器(用来发布事件)
initApplicationEventMulticaster();
// 第九步:初始化一些特殊的bean
onRefresh();
// 第十步:将所有监听器注册到前两步创建的事件广播器中
registerListeners();
// 第十一步:结束bean的初始化工作(主要将所有单例BeanDefinition实例化)
finishBeanFactoryInitialization(beanFactory);
// 第十二步:afterRefresh(上下文刷新完毕,发布相应事件)
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 exception 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();
contextRefresh.end();
}
}
}
这里有非常多的步骤,上下文对象主要的bean也是在这里进行处理的,具体的说明可以看注释, fresh方法就是SpringFrameWork的那部分(不再细化)
//6、ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象
context = this.createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//7、接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//8、refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。
this.refreshContext(context);
=====> //9、afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。
this.afterRefresh(context, applicationArguments);
=====> //10、然后根据stopwatch打印出启动时间
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
//11、这里调用ApplicationRunner和CommandLineRunner的实现类
this.callRunners(context, applicationArguments);
afterRefresh() 是对方法 refresh() 的扩展,暂时空方法。
stopWatch.stop() 根据stopwatch打印出启动时间,至此项目已经启动完成。
* run方法主要做如下几件事情:
发出启动结束事件
执行实现ApplicationRunner、CommandLineRunner的run方法
发布应用程序已启动(ApplicationStartedEvent)事件
结合网上共享的两张图可以清晰回顾下整体流程:
关于Springboot Code的启动源码是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/xiaohai945/blog/5026834