目录
- springboot主函数
- main方法
- 初始化SpringApplication实例
- SpringApplication.run方法
springboot主函数
springboot项目一般都是打包成jar包直接运行main方法启动,当然也可以跟传统的项目一样打包war包放在tomcat里面启动.那么springboot怎么直接通过main方法启动呢?
举个栗子,这是一个简单的main方法启动类:
@EnableAsync
@EnableScheduling
@EnableTransactionManagement
@EnableConfigurationProperties
@EnableCaching
@MapperScan(value = {"com.study.springbootplus.**.mapper"})
@SpringBootApplication
public class SpringBootPlusApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
}
}
main方法
main方法主要就是看SpringApplication的run方法,这个方法大概就是创建个spring容器,然后创建个web容器(tomcat,jetty等)启动.
run方法点进去,这里一个新建springApplication实例,一个run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
初始化SpringApplication实例
源码:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// main方法中的args参数,可接收命令行启动时添加的参数
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 确认spring容器类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载ApplicationContextInitializer类,ApplicationContext初始化类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载ApplicationList编程ener类,监听类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取main方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication.run方法
源码:
public ConfigurableApplicationContext run(String... args) {
//计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//spring容器
ConfigurableApplicationContext context = jsnull;
//错误回调
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置一些系统属性
configureHeadlessProperty();
//获取启动时监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听器
listeners.starting();
try {
//获取一些启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建运行环境environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//设置一些系统参数
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建spring容器
context = createApplicationContext();
//获取异常报告,回调
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器
refreshContext(context);
//spring容器后置处理
afterRefresh(context, applicationArguments);
//计时终止
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//结束通知
listeners.started(context);
//执行runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//spring容器就绪通知
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
getRunListeners方法,starting方法,获取启动监听,和启动
源码:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.lpythonoadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
前面讲过启动的时候会先加载spring会加载所有jar包下的META-INF/spring.factories,然后缓存起来,这里就获取

返回spirngboot唯一实现SpringApplicationRunListener的接口:EventPublishingRunListener,然后执行starting方法;starting方法就是构建ApplicationStartingEvent,然后获取listener执行,这一块其实还比较复杂.用到了适配器模式:

prepareEnvironment准备环境
//准备启动参数就不说了
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//创建一个environment对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境 PropertySources Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//PropertySources
ConfigurationPropertySources.attach(environment);
//环境准备
listeners.environmentPrepared(environment);
//将环境绑定到oSpringApplication(
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//配置PropertySources-如果有attach到environment
ConfigurationPropertySources.attach(environment);
return environment;
}
createApplicationContext创建容器
protected ConfigurableApplicationContext createApplicationContext() {
//获取上下文的类
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
//判断Web应用类型
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

prepareContext,准备容器
private void preparepythonContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境
context.setEnvironment(environment);
//处理上下文
postProcessApplicationContext(context);
//获取所有ApplicationContextInitializer,执行ApplicationContextInitializer的init方法
applyInitializers(context);
//调用SpringApplicationRunListener的contextPrepared,表示容器已经准备
listeners.contextPrepared(context);
//日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//获取beanfactory,注册相关bean
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(sourceDxuVds, "Sources must not be empty");
//加载启动类
load(context, sources.toArray(new Object[0]));
//调用SpringApplicationRunListener的contextPrepared,表示容器已经loaded事件
listeners.contextLoaded(context);
}
refreshContext,afterRefresh刷新容器,刷新容器之后执行方法
就是spring容器的刷新方法
到此这篇关于Springboot的启动原理详细解读的文章就介绍到这了,更多相关Springboot启动原理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论