SpringBoot启动流程-创新互联
使用的SpringBoot版本为2.3.12,配套Spring版本为5.2.15
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名注册、网络空间、营销软件、网站建设、内乡网站维护、网站推广。一 SpringBoot启动流程 1.程序入口// 程序入口
// 运行SpringApplication.run方法
// 主启动类方法
// args是启动参数,我在环境配置处配置程序参数了--server.port=8079端口号信息,则args不为空
public static void main(String[] args) {
// 加载完成后 执行run方法
SpringApplication.run(SpringbootQuartzApplication.class, args);
}
// 上面代码实际上是调用spring-boot包里的SpringApplication.class里的方法
// 这里可以看出是先创建SpringApplication对象,再调用run方法完成容器的初始化操作
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
2.主体分为两个步骤a.构造方法创建SpringApplication实例
b.执行SpringApplication实例的run方法
3.图示 二 new SpringAoolication()构造函数随版本不同,代码会有较大差异,具体根据自己所用项目来。下面截取构造方法核心部分代码。
// new SpringApplication()构造函数
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
其中new SpringApplication(primarySources)构造函数环节调用的也是SpringApplication.class里的方法
public SpringApplication(Class>... primarySources) {
this((ResourceLoader)null, primarySources);
}
// 下面会逐一展开run方法执行时,是如何使用构造方法里定义的属性的
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
// 1.初始化启动时必要参数
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
// 打印JVM的启动和运行信息,启动类、java版本等
this.logStartupInfo = true;
// 通过命令行参数向application.properties中添加属性配置
this.addCommandLineProperties = true;
// 加载默认的类型转换和格式化类
this.addConversionService = true;
// 开启java的headless模式,此模式允许java在没有显示器、鼠标等设备的情况下启动服务
this.headless = true;
// 创建线程,该线程用来在java程序关闭后释放资源
this.registerShutdownHook = true;
// 默认为空,即使用springboot默认配置文件。开发时切dev/sit/prod等分支时会使用
this.additionalProfiles = new HashSet();
// 是否加载环境配置
this.isCustomEnvironment = false;
// 是否懒加载,可以通过在application.properties设置spring.main.lazy-initialization=true修改
this.lazyInitialization = false;
//
this.resourceLoader = resourceLoader;
// 主配置为空则抛出异常
Assert.notNull(primarySources, "PrimarySources must not be null");
// 保存主配置类
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 2 确认web应用类型,本项目为Servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3.加载所有初始化器,获取spring工厂文件中的实例。加载了当前项目下全部的spring.factries文件,将文件中的k-v放入缓存中,然后将读取到的ApplicationContextInitializer类型的类实例化
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4.加载所有监听器,这里与上面类似也调用this.getSpringFactoriesInstances(),但是由于之前已经把spring.factries文件读取到缓存中了,因此不用再次读取文件而是读缓存
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 5.设置程序运行的主类MainApplication,以便后面执行run方法
this.mainApplicationClass = this.deduceMainApplicationClass();
}
2.确定应用程序类型// 提供三种类型
// 1.NONE 表示当前的应用既不是一个web应用也不是一个reactive应用,是一个纯后台的应用。不启动内嵌的服务
// 2.SERVLET 当前应用是一个基于servlet API的标准的web应用,需启动内嵌servlet web服务
// 3.REACTIVE 是spring5当中的新特性,表示是一个响应式的web应用,需启动内嵌的响应式web服务
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 这里实际上是根据类加载器名字具体是哪一种,本项目中SERVLET_INDICATOR_CLASSES为['javax.servlet.Servlet', 'org.springframework.web.context.ConfigurableWebApplicationContext']
// ClassUtils.isPresent()方法,判断指定类名的类是否存在,是否可以进行加载(如果我们导入了,相对应的jar包,就可以加载,如果没有导入就不能加载)
// 如果能够加载DispatcherHandler类,但是无法加载DispatcherServlet和ServletContainer类,就证明是一个响应式web应用
static WebApplicationType deduceFromClasspath() {
// 判断能否加载响应式web应用
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2< var1; ++var2) {
String className = var0[var2];
// 判断能否加载SERVLET_INDICATOR_CLASSES里的两个类名
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
// 补充ClassUtils.isPresent()方法 实际上是调用forName来获取类加载器
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
} catch (IllegalAccessError var3) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
} catch (Throwable var4) {
return false;
}
}
3.加载所有初始化器,获取spring工厂文件中的实例补充说明,4.加载监听器与下面流程基本一致,不同的是3.加载所有初始化器时读取的一些文件配置会放入缓存中,加载监听器时从缓存读取数据即可。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
privateCollectiongetSpringFactoriesInstances(Classtype) {
// type为ApplicationContextInitializer.class
return this.getSpringFactoriesInstances(type, new Class[0]);
}
privateCollectiongetSpringFactoriesInstances(Classtype, Class>[] parameterTypes, Object... args) {
// 获取SpringApplication的类加载器,这里是一个系统类加载器AppClassLoader
ClassLoader classLoader = this.getClassLoader();
// 加载spring.factories文件中的类的全类名集合
Setnames = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过反射获取到集合中全类名对应的Class,并创建其实例对象,返回实例对象
Listinstances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 根据优先级排序
AnnotationAwareOrderComparator.sort(instances);
// 返回实例
return instances;
}
public static ListloadFactoryNames(Class>factoryType, @Nullable ClassLoader classLoader) {
// 加载初始化器时factoryTypeName="org.springframework.context.ApplicationContextInitializer",对应之前设置的ApplicationContextInitializer.class
// 加载监听器时factoryTypeName="org.springframework.context.ApplicationListener",对应ApplicationListener.calss
String factoryTypeName = factoryType.getName();
// 正式加载spring-boot包里META-INF目录下的spring.factories文件
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// loadSpringFactories()才正式去加载spring-boot包里META-INF目录下的spring.factories文件
private static Map>loadSpringFactories(@Nullable ClassLoader classLoader) {
// 这里是读缓存,加载初始化器时还没有缓存(在这一环节添加了缓存),到加载监听器时就能从缓存读取数据了
MultiValueMapresult = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 获取当前项目中所有META-INF/spring.factories文件的绝对路径,对于目前使用的项目则是读取下面的路径,因为是测试项目,因此引入的包和版本比较杂乱
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/boot/spring-boot/2.3.12.RELEASE/spring-boot-2.3.12.RELEASE.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/spring-beans/5.2.15.RELEASE/spring-beans-5.2.15.RELEASE.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.0.1/mybatis-spring-boot-autoconfigure-2.0.1.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.12.RELEASE/spring-boot-autoconfigure-2.3.12.RELEASE.jar!/META-INF/spring.factories
// 这里贴一下pom.xml文件的部分引用Enumerationurls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
// 遍历上面获取到的绝对路径
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
// 把文件资源加载到Properties中
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry, ?>entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11< var10; ++var11) {
String factoryImplementationName = var9[var11];
// K-V形式,把spring.factories中的的类型,下面图例中会贴上result的内容
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 将类加载器加载的spring.factories文件结果集放入缓存
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
并且贴一下对应图片,下图为result.add(factoryTypeName, factoryImplementationName.trim());后的result。大多是与项目启动、异常处理、自动配置等基础配置相关的内容。
下面则是读取了哪些spring.factories文件(实际项目中会很多,这里只是测试项目)
三 执行run方法下面贴一下主要代码部分
public ConfigurableApplicationContext run(String... args) {
// 开启计时器。计算程序启动时间用的
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 6.开启java的headless模式 是SpringApplication的 this.headless = true;控制的
this.headless = true;
// 保证headless一定有值
this.configureHeadlessProperty();
// 7.获取并启用监听器 这里获取到的是SpringApplicationRunListeners对象,里面有之前初始化SpringApplication时的监听器
// args默认为空,我填写了--server.port=8079属性
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
// 8.设置应用程序参数
// 初始化ApplicationArguments对象,提供访问运行SpringApplication时的参数
// 由于之前的配置 args 里带有--server.port=8079参数,而这个参数就会被封装进Source对象,底层是Spring 框架的 SimpleCommandLinePropertySource
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 9.准备环境变量 参数是监听器和应用程序参数
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 忽略掉部分bean信息
this.configureIgnoreBeanInfo(environment);
// 打印banner信息 这里实际上是打印了Spring的标志
Banner printedBanner = this.printBanner(environment);
// 10.创建应用程序的上下文
context = this.createApplicationContext();
// 10.准备上下文环境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11.刷新上下文,属于Spring的范畴(自动装配+启动tomcat)
this.refreshContext(context);
// 11.刷新上下文后置处理(目前是空的)
this.afterRefresh(context, applicationArguments);
// 停止计时器
stopWatch.stop();
// 打印JVM的启动和运行信息,启动类、java版本等 this.logStartupInfo = true;
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 创建应用上下文启动完成事件对象ApplicationStartedEvent
// 发布应用上下文启动完成事件,广播事件,调用监听器相对应的监听方法
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var9) {
this.handleRunFailure(context, var9, listeners);
throw new IllegalStateException(var9);
}
try {
// 12.发布上下文准备就绪事件
listeners.running(context);
return context;
} catch (Throwable var8) {
this.handleRunFailure(context, var8, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var8);
}
}
7.获取并启用监听器
9.准备环境变量
9.1ConfigurableEnvironmentprivate ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 创建或者返回环境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
// 监听器的环境准备,这部分.class文件断点匹配不上,但是从输出上看多了initialMulticaster
listeners.environmentPrepared((ConfigurableEnvironment)environment);
// 把环境绑定到SpringApplication
this.bindToSpringApplication((ConfigurableEnvironment)environment);
// 之前配置的 this.isCustomEnvironment = false;
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
// 返回的environment环境信息不仅仅是SpringBoot项目本身的,虚拟机、项目运行系统信息(Linux/Windows等)、JDK、MAVEN等信息也包含其中
return (ConfigurableEnvironment)environment;
}
// 由于没有设置environment,因此根据this.webApplicationType = WebApplicationType.deduceFromClasspath();得知为SERVLET
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 之前设置的this.addConversionService = true;加载默认的类型转换和格式化类
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
// 配置environment的PropertySources
this.configurePropertySources(environment, args);
// 配置environment的ActiveProfiles
this.configureProfiles(environment, args);
}
9.2忽略Bean的BeanInfo信息// spring.beaninfo.ignore默认为true表示跳过对BeanInfo类的搜索
// 忽略所有自定义的BeanInfo信息的搜索
// 通过Java的Introspector.getBeanInfo方法,可以获取到JavaBean的BeanInfo信息(一般有Bean的方法、属性、类相关信息等)
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty("spring.beaninfo.ignore") == null) {
Boolean ignore = (Boolean)environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty("spring.beaninfo.ignore", ignore.toString());
}
}
9.3打印banner信息这里实际上是打印了Spring的标志
private Banner printBanner(ConfigurableEnvironment environment) {
// 之前设置的this.bannerMode = Mode.CONSOLE;
if (this.bannerMode == Mode.OFF) {
return null;
} else {
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader((ClassLoader)null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
console控制台会打印出下面标识(默认输出,可以通过配置修改)
10.创建应用程序的上下文 10.1ConfigurableApplicationContextprotected ConfigurableApplicationContext createApplicationContext() {
Class>contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据Web应用类型创建对应的上下文,目前项目这里根据之前的配置 是SERVLET
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
// 实例化应用上下文
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
10.2准备上下文环境private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 把之前的配置的环境加载到应用上下文中
context.setEnvironment(environment);
// 实例化单例的beanName生成器
this.postProcessApplicationContext(context);
// 遍历之前在SpringApplication中的初始化器,进行初始化
this.applyInitializers(context);
// 监听器初始化
listeners.contextPrepared(context);
// 打印JVM的启动和运行信息,启动类、java版本等 this.logStartupInfo = true;
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.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());
}
Set
11.刷新上下文(这部分会调用Spring里的代码,包括自动装配Tomcat)private void refreshContext(ConfigurableApplicationContext context) {
// 之前设置的this.registerShutdownHook = true;创建线程,该线程用来在java程序关闭后释放资源
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
this.refresh((ApplicationContext)context);
}
上面的this.refresh((ApplicationContext)context);其实就在调用Spring相关包了
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 刷新前准备工作,设置了容器的启动时间、加载environment对象、准备监听器等
this.prepareRefresh();
// 获取BeanFactory,类型是DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 把配置和属性信息填充到beanFactory中
this.prepareBeanFactory(beanFactory);
try {
//
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor(初始化时才会调用)
this.registerBeanPostProcessors(beanFactory);
// 初始化MessageSource组件,消息处理相关
this.initMessageSource();
// 事件广播相关
this.initApplicationEventMulticaster();
// 具体由子类实现,根据Spring容器的种类进行不同的事情处理.
this.onRefresh();
// 注册引用的监听器
this.registerListeners();
// 实例非懒加载的单例
this.finishBeanFactoryInitialization(beanFactory);
// 完成刷新并进行通知广播
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
11.1beanFactoryConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();这部分
11.2this.onRefresh();根据不同Spring容器来的创建不同容器
// 项目时web项目,用的ServletWebServerApplicationContext
// 对应代码如下,由this.createWebServer();创建具体的Servlet容器
protected void onRefresh() {
super.onRefresh();
try {
// 由于SpringBoot内置Tomcat容器,所以这里实际上会创建一个TomcatServletWebServerFactory生成TomcatWebServer
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
文章题目:SpringBoot启动流程-创新互联
文章源于:http://ybzwz.com/article/csepig.html