Springboot 使得我们日常开发变得方便,下面一起来看下他的启动原理(往往是跟读取自动配置类联系在一起的)
一、创建SpringApplication
1.调用SpringApplication.run 启动springboot
public static void main(String[] args) {
SpringApplication.run(DeomoApplication.class, args);
}
调用springApplication 的run 方法
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
然后调用 run 实例化了一个自定义 springApplication
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");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//将启动类放到primarySources 里面
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}//推断Main方法所在的类 就是获取Springboot项目的启动类路径
getSpringFactoriesInstances 跟进这个方法
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.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这个方法主要就是从类路径jar 包下读取spring.factories 中读取key=ApplicationContextInitializer 的相关类
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;
}
总结:
1.获取启动类
2.获取web 应用类型(servlet)
3.读取了对外扩展的ApplicationContextInitializer ApplicationListener
4.根据Main 推算出所在的类
二、启动 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;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();//开启Hander 模式
SpringApplicationRunListeners listeners = getRunListeners(args);//获取监听器,springboot 在启动会对外扩展很多监听器
listeners.starting();//发布ApplicationStartingEvent 事件
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//读取命令行参数,封装成一个ApplicationArguments 对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//预初始化环境
configureIgnoreBeanInfo(environment);//实现的BeanInfo springboot在扫描容器的时候会忽略
Banner printedBanner = printBanner(environment);//打印springboot 启动图标
context = createApplicationContext();//根据webApplication创建上下文
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);//从spring.factories 中读取相关类
prepareContext(context, environment, listeners, applicationArguments, printedBanner);//预初始化容器上下文
//调用springIoc 加载 该方法为Spring 中非常重要的方法
//spring boot 是利用ServletWebserverApplication 对spring 做的扩展,所以这里使用AnnotationConfigServletWebserverApplicationContext 启动Spring 容器
//做了哪些扩展:finishBeanFactoryInitialization加载启动配置类,onRefresh创建servlet 容器 这里不太理解的可以去看下Spring Ioc 加载流程源码,这里不做过多介绍了
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();//根据webApplicationType 创建environment 读取java 环境变量和系统环境变量
configureEnvironment(environment, applicationArguments.getSourceArgs());//将命令行参数读取到环境变量中
ConfigurationPropertySources.attach(environment);//将@PropertiesSources 的配置信息放到第一行,因为他的优先级是最低的
listeners.environmentPrepared(environment);//发布监听器,读取springboot 的全局配置文件
bindToSpringApplication(environment);//将spring.main 开头的配置信息绑定到springApplication
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//获取spring山下问Bean 工厂,负责创建Bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
//在spring 如果出现了两个相同的Bean ,则后面的会覆盖前面的
//而在springboot中,设置了不允许覆盖,如果出现了,直接抛异常
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//设置当前spring容器是否将所有的Bean 设置为懒加载
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]));
//读取完整配置类后发送SpringApplicationPreparedEvent
listeners.contextLoaded(context);
}
总结:
1.初始化springApplication 从spring.factories 中读取listener springapplicationContextinitializers
2.运行run
3.读取环境变量,配置信息
4.创建SpringApplication 上下文 :ServletWebServerApplication 内嵌tomcat
5.初始化上下文,读取启动类
6.调用ioc refresh
加载所有的自动配置类,创建servlet容器
7.对外提供监听器提供扩展