springboot启动流程、日志分析

您所在的位置:网站首页 spring启动失败没有日志 springboot启动流程、日志分析

springboot启动流程、日志分析

2024-07-15 21:57| 来源: 网络整理| 查看: 265

springboot启动流程、日志分析

只关注info级别日志

1. 主类启动,通过调用SpringApplication#run(Class primarySource, String... args)方法启动程序 public static void main(String[] args) { SpringApplication.run(AppConfig.class, args); } 2. SpringApplication实例化、并调用run(Class[] primarySources, String[] args)方法启动 public static ConfigurableApplicationContext run(Class primarySource, String... args) { return run(new Class[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { // 创建创建 SpringApplication 对象并启动 return new SpringApplication(primarySources).run(args); } 2.1. 创建SpringApplication对象

主要设置应用类型、初始化器、监听器

public SpringApplication(Class... primarySources) { this(null, primarySources); } @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } resourceLoader:资源加载器,无默认值,此处即为null,用途参考文章:IoC 之 Spring 统一资源加载策略primarySources:主要的Java Config 类的数组,此处即为服务启动的主类cn.javadog.demo.springboot.AppConfigwebApplicationType:Web 应用类型,调用WebApplicationType#deduceFromClasspath()方法,通过classpath推断,此处即为SERVLET 具体类型取决于引入的maven依赖,如果引入的为spring-boot-starter-webflux则类型为REACTIVE,如果引入的为spring-boot-starter-web则类型为SERVLET,都为引入则为null这个属性,在下面的 createApplicationContext() 方法,将根据它的值(类型),创建不同类型的 ApplicationContext 对象,即 Spring 容器的类型不同。 private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext"; private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext"; static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } initializers: ApplicationContextInitializer 数组 通过 getSpringFactoriesInstances(Class type) 方法,进行获得 ApplicationContextInitializer 类型的对象数组,详细的解析,见「2.1.1 getSpringFactoriesInstances」 方法。当前只在 Spring MVC 的环境下,initializers 属性的结果如下图: listeners:ApplicationListener 数组 也是通过 #getSpringFactoriesInstances(Class type) 方法,进行获得 ApplicationListener 类型的对象数组当前只在 Spring MVC 的环境下,initializers 属性的结果如下图: mainApplicationClass:程序启动main方法所在的类,此处即为服务启动的主类cn.javadog.demo.springboot.AppConfig 这个 mainApplicationClass 属性,没有什么逻辑上的用途,主要就是用来打印下日志,说明是通过这个类启动 Spring 应用的 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; } 2.1.1 从META-INF/spring.factories中指定类对应的实例对象 private Collection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class[] {}); } private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 加载指定类型对应的实现类,去重,在 `META-INF/spring.factories` 里的类名的数组 Set names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 创建对象,具体实现不深究 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 将对象进行排序,主要是针对类上有`@Order`注解的 AnnotationAwareOrderComparator.sort(instances); return instances; } 2.2 运行run(String... args)方法启动spring程序,注意是spring程序! public ConfigurableApplicationContext run(String... args) { // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList(); // 配置 headless 属性 configureHeadlessProperty(); // 获得 SpringApplicationRunListener 的数组,并启动监听 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { // 创建ApplicationArguments对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 打印 Spring Banner Banner printedBanner = printBanner(environment); // 创建 Spring 容器。 context = createApplicationContext(); // 异常报告器 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 主要是调用所有初始化类的 initialize 方法 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 初始化 Spring 容器。 refreshContext(context); // 执行 Spring 容器的初始化的后置逻辑。默认实现为空。 afterRefresh(context, applicationArguments); // 停止 StopWatch 统计时长 stopWatch.stop(); // 打印 Spring Boot 启动的时长日志。 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。 listeners.started(context); // 调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。 callRunners(context, applicationArguments); } catch (Throwable ex) { // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常 handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } // 通知 SpringApplicationRunListener 的数组,Spring 容器运行中。 try { listeners.running(context); } catch (Throwable ex) { // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } 2.2.1 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长

逻辑比较简单,就是创建StopWatch对象,其内部有个TaskInfo内部类,记录一次任务的持续时间和任务名称,结果放在StopWatch的taskList属性里;本项目持续时间即为Spring容器初始化的时间,任务名称为空字符串

2.2.2 配置headless属性

这个逻辑,可以无视,和AWT(硬件)相关

2.2.3 获得 SpringApplicationRunListener 的数组,并启动监听

实际类型为SpringApplicationRunListener的实现类EventPublishingRunListener,实现 SpringApplicationRunListener、Ordered 接口,将 SpringApplicationRunListener 监听到的事件,转换成对应的 SpringApplicationEvent 事件,发布到监听器。具体作用暂时不深究

2.2.4 创建ApplicationArguments对象

此处的args目前为空数组!可以通过命令行等方式设置参数

2.2.5 加载属性配置

调用prepareEnvironment(...) 方法,加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置,具体原理不深究,参见:《【死磕 Spring】—— 环境 & 属性:PropertySource、Environment、Profile》

根据 webApplicationType 类型,会创建不同类型的 ConfigurableEnvironment 对象主要配置environment有两大属性:propertySources和activeProfiles propertySources:根据配置的 defaultProperties、或者 JVM 启动参数,作为附加的 PropertySource 属性源,本项目都没有做配置activeProfiles:激活的profile,未指定即为空数组,可以同时指定多个 通知 SpringApplicationRunListener 的数组,环境变量已经准备完成,相关监听器会产生一些 debug 级别日志的日志,不去深究 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment // 创建 ConfigurableEnvironment 对象,并进行配置 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); // 通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。 listeners.environmentPrepared(environment); // 绑定 environment 到 SpringApplication 上,暂时不太知道用途。 bindToSpringApplication(environment); // 如果非自定义 environment ,则根据条件转换。认情况下,isCustomEnvironment 为 false ,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment 自身,所以可以无视这块逻辑先。 if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } // 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。 ConfigurationPropertySources.attach(environment); return environment; } 2.2.6 打印 Spring Banner

打印的第一个日志,样式如下 banner

默认的是org.springframework.boot.SpringBootBanner打印的spring的启动banner核心逻辑如下,没有自定义的banner就取默认的,可以通过在路径下添加banner.txt或者指定spring.banner.location属性进行更换 private Banner getBanner(Environment environment) { Banners banners = new Banners(); banners.addIfNotNull(getImageBanner(environment)); banners.addIfNotNull(getTextBanner(environment)); if (banners.hasAtLeastOneBanner()) { return banners; } if (this.fallbackBanner != null) { return this.fallbackBanner; } return DEFAULT_BANNER; } 2.2.7 调用createApplicationContext()创建 Spring 容器

具体解析见下方

2.2.8 创建异常报告器

此处exceptionReporters属性如下,不做深究:

2.2.9 调用 prepareContext(...) 方法,主要是调用所有初始化类的 initialize(...) 方法 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置 context 的 environment 属性 context.setEnvironment(environment); // 设置 context 的一些属性 postProcessApplicationContext(context); // 初始化 ApplicationContextInitializer applyInitializers(context); // 通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。 listeners.contextPrepared(context); // 打印日志 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans // 设置 beanFactory 的属性 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources // 加载 BeanDefinition 们 Set sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); // 通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。 listeners.contextLoaded(context); } 2.2.10 调用 refreshContext(ConfigurableApplicationContext context) 方法,启动(刷新) Spring 容器

具体解析见下方

2.2.11 调用afterRefresh(...) 方法,执行 Spring容器的初始化的后置逻辑。 默认实现为空。代码如下: protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { } 2.2.12 停止 StopWatch 统计时长 2.2.13 打印 Spring Boot 启动的时长日志。效果如下: 2.2.14 调用 SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器启动完成 2.2.15 调用 SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3