SpringBoot:Spring容器的启动过程

一、简述

Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用BeanFactory来实例化、配置和管理Bean。

二、SpringBoot的启动过程

在SpringBoot中,SpringApplication封装了一套Spring应用的启动流程,对用户完全是透明的,这个类原本在Spring中是没有的。

一般来说,默认的SpringApplication执行流程可以满足大部分需求,若是想要干预这过程,可以通过SpringApplication在流程的某些地方开启扩展点来对流程进行扩展,典型的扩展方案就是setXXX方法.

1 @SpringBootApplication
2 public class CodeSheepApplication {
3     public static void main( String[] args ) {
4         // SpringApplication.run( DdsApplication.class, args );
5         SpringApplication app = new SpringApplication( DdsApplication.class );
6         app.setXXX( ... ); // 用户自定的扩展在此 !!!
7         app.run( args );
8     }
9 }

SpringBoot应用中,首先要了解的就是SpringApplication这个类了。

SpringApplication的实例化,在上面这段代码中,使用了自定义SpringApplication,通过一行代码启动SpringBoot应用,也可以自定义SpringApplication的一些扩展。

 1   @SuppressWarnings({ "unchecked", "rawtypes" })
 2     public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 3         this.resourceLoader = resourceLoader;
 4         Assert.notNull(primarySources, "PrimarySources must not be null");
 5         this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
 6         this.webApplicationType = WebApplicationType.deduceFromClasspath();
 7         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 8         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 9         this.mainApplicationClass = deduceMainApplicationClass();
10     }

1.WebApplicationType是一个枚举Web Application的类型,其类型定义有三种:NONE(不应作为Web应用程序运行,也不应启动嵌入式Web服务器)、SERVLET(基于servlet的Web应用程序)、REACTIVE(响应式Web应用程序)。

NONE:org.springframework.context.annotation.AnnotationConfigApplicationContext

SERVLET:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

REACTIVE:org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext

WebApplicationType#deduceFromClasspath()的意思是根据Classpath的内容推断WebApplication类型。

 1   static WebApplicationType deduceFromClasspath() {
 2         if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
 3                 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
 4             return WebApplicationType.REACTIVE;
 5         }
 6         for (String className : SERVLET_INDICATOR_CLASSES) {
 7             if (!ClassUtils.isPresent(className, null)) {
 8                 return WebApplicationType.NONE;
 9             }
10         }
11         return WebApplicationType.SERVLET;
12     }

ClassUtils是Spring框架所提供关于类级别的工具类,主要供框架内部使用。ClassUtils#isPresent是判断当前class loader中是否存在对应的类型。代码里面关于判断中的方法,为什么所提供的classLoader参数都为空,再进去方法里面看看,发现它是调用了ClassUtils#forName

 1 public static boolean isPresent(String className, @Nullable ClassLoader classLoader)    {
 2         try {
 3             forName(className, classLoader);
 4             return true;
 5         }
 6         catch (IllegalAccessError err) {
 7             throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
 8                     className + "]: " + err.getMessage(), err);
 9         }
10         catch (Throwable ex) {
11             // Typically ClassNotFoundException or NoClassDefFoundError...
12             return false;
13         }
14     }

再看看ClassUtils#forName,方法的源码很长,它替换Class.forName(),还返回原始类型(例如“int”)和数组类名称(例如“String []”)的Class实例。 此外,它还能够以Java源代码样式解析内部类名(例如“java.lang.Thread.State”而不是“java.lang.Thread $ State”)。

里面调用了ClassUtils#resolvePrimitiveClassName来把给定的类作为基本类(如果适用的话);如果不是基本类型则在缓存找,如果是基本类型则返回;然后接着判断给出的类名是不是一维或多维的整型或字符串数组;接着判断方法传入的classLoader,为空则ClassUtils#getDefaultClassLoader来从当前System中获取默认的classLoader,再使用给定的类加载器返回与具有给定字符串名称的类或接口关联的Class对象→Class.forName(类名, false, 当前默认的classLoader),当找不到Class就会报异常。

 1 public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
 2             throws ClassNotFoundException, LinkageError {
 3 ?
 4         Assert.notNull(name, "Name must not be null");
 5 ?
 6         Class<?> clazz = resolvePrimitiveClassName(name);
 7         if (clazz == null) {
 8             clazz = commonClassCache.get(name);
 9         }
10         if (clazz != null) {
11             return clazz;
12         }
13 ?
14         // "java.lang.String[]" style arrays
15         if (name.endsWith(ARRAY_SUFFIX)) {
16             String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
17             Class<?> elementClass = forName(elementClassName, classLoader);
18             return Array.newInstance(elementClass, 0).getClass();
19         }
20 ?
21         // "[Ljava.lang.String;" style arrays
22         if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
23             String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
24             Class<?> elementClass = forName(elementName, classLoader);
25             return Array.newInstance(elementClass, 0).getClass();
26         }
27 ?
28         // "[[I" or "[[Ljava.lang.String;" style arrays
29         if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
30             String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
31             Class<?> elementClass = forName(elementName, classLoader);
32             return Array.newInstance(elementClass, 0).getClass();
33         }
34 ?
35         ClassLoader clToUse = classLoader;
36         if (clToUse == null) {
37             clToUse = getDefaultClassLoader();
38         }
39         try {
40             return Class.forName(name, false, clToUse);
41         }
42         catch (ClassNotFoundException ex) {
43             int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
44             if (lastDotIndex != -1) {
45                 String innerClassName =
46                         name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
47                 try {
48                     return Class.forName(innerClassName, false, clToUse);
49                 }
50                 catch (ClassNotFoundException ex2) {
51                     // Swallow - let original exception get through
52                 }
53             }
54             throw ex;
55         }
56     }

总结WebApplicationType#deduceFromClasspath(),它通过ClassPath来推断WebApplication的类型,从当前系统默认的ClassLoader获取WebApplication类型相对应类的映射,从而判断WebApplication的类型。

1 setInitializers((Collection)
2 getSpringFactoriesInstances(ApplicationContextInitializer.class));

2.setInitializers(...)使用 SpringFactoriesLoader 查找并加载 classpath下 META-INF/spring.factories文件中所有可用的 ApplicationContextInitializer ---- 用于在ConfigurableApplicationContext#refresh() 之前初始化Spring ConfigurableApplicationContext的回调接口。

 1 # PropertySource Loaders
 2 org.springframework.boot.env.PropertySourceLoader= 3 org.springframework.boot.env.PropertiesPropertySourceLoader, 4 org.springframework.boot.env.YamlPropertySourceLoader
 5 ?
 6 # Run Listeners
 7 org.springframework.boot.SpringApplicationRunListener= 8 org.springframework.boot.context.event.EventPublishingRunListener
 9 ?
10 # Error Reporters
11 org.springframework.boot.SpringBootExceptionReporter=12 org.springframework.boot.diagnostics.FailureAnalyzers
13 ?
14 # Application Context Initializers
15 org.springframework.context.ApplicationContextInitializer=16 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,17 org.springframework.boot.context.ContextIdApplicationContextInitializer,18 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,19 org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
20 ?
21 # Application Listeners
22 org.springframework.context.ApplicationListener=23 org.springframework.boot.ClearCachesApplicationListener,24 org.springframework.boot.builder.ParentContextCloserApplicationListener,25 org.springframework.boot.context.FileEncodingApplicationListener,26 org.springframework.boot.context.config.AnsiOutputApplicationListener,27 org.springframework.boot.context.config.ConfigFileApplicationListener,28 org.springframework.boot.context.config.DelegatingApplicationListener,29 org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,30 org.springframework.boot.context.logging.LoggingApplicationListener,31 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
32 ?
33 # Environment Post Processors
34 org.springframework.boot.env.EnvironmentPostProcessor=35 org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,36 org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,37 org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
38 ?
39 # Failure Analyzers
40 org.springframework.boot.diagnostics.FailureAnalyzer=41 org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,42 org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,43 org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,44 org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,45 org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,46 org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,47 org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,48 org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,49 org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,50 org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,51 org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,52 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,53 org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
54 ?
55 # FailureAnalysisReporters
56 org.springframework.boot.diagnostics.FailureAnalysisReporter=57 org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

3.setListeners(...) 使用SpringFactoriesLoader查找并加载 classpath下 META-INF/spring.factories文件中的所有可用的 ApplicationListener ---- 应用程序事件侦听器实现的接口, 基于Observer设计模式的标准java.util.EventListener 接口。

4.deduceMainApplicationClass()推断并设置main方法的定义类,通过Throwable#getStackTrace()方法返回堆栈中的元素,找出方法名为main的堆栈元素,再根据类名返回对应 的反射类

 1 private Class<?> deduceMainApplicationClass() {
 2         try {
 3             StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
 4             for (StackTraceElement stackTraceElement : stackTrace) {
 5                 if ("main".equals(stackTraceElement.getMethodName())) {
 6                     return Class.forName(stackTraceElement.getClassName());
 7                 }
 8             }
 9         }
10         catch (ClassNotFoundException ex) {
11             // Swallow and continue
12         }
13         return null;
14     }

再来看看SpringBoot应用运行方法 SpringApplication#run

 1 public ConfigurableApplicationContext run(String... args) {
 2         StopWatch stopWatch = new StopWatch();
 3         stopWatch.start();
 4         ConfigurableApplicationContext context = null;
 5         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 6         configureHeadlessProperty();
 7         SpringApplicationRunListeners listeners = getRunListeners(args);
 8         listeners.starting();
 9         try {
10             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
11             ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
12             configureIgnoreBeanInfo(environment);
13             Banner printedBanner = printBanner(environment);
14             context = createApplicationContext();
15             exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
16                     new Class[] { ConfigurableApplicationContext.class }, context);
17             prepareContext(context, environment, listeners, applicationArguments, printedBanner);
18             refreshContext(context);
19             afterRefresh(context, applicationArguments);
20             stopWatch.stop();
21             if (this.logStartupInfo) {
22                 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
23             }
24             listeners.started(context);
25             callRunners(context, applicationArguments);
26         }
27         catch (Throwable ex) {
28             handleRunFailure(context, ex, exceptionReporters, listeners);
29             throw new IllegalStateException(ex);
30         }
31 ?
32         try {
33             listeners.running(context);
34         }
35         catch (Throwable ex) {
36             handleRunFailure(context, ex, exceptionReporters, null);
37             throw new IllegalStateException(ex);
38         }
39         return context;
40     }

三、总结

获取SpringApplicationListener → 通知Listeners start → 创建参数,配置Environmen → 根据WebApplicationType创建ApplicationContext → 初始化ApplicationContext,设置Environment加载相关配置等 → 通知EnvironmentPrepared,contextLoaded → refresh ApplicationContext → 通知Listeners start context → 完成启动→ 通知runner → 结束

  1. 通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件,获取并创建 SpringApplicationRunListener 对象
  2. 然后由 SpringApplicationRunListener 来发出 starting 消息
  3. 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
  4. 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息
  5. 根据 WebApplicationType 创建 ApplicationContext
  6. 初始化 ApplicationContext,并设置 Environment,加载相关配置等
  7. SpringApplicationRunListener 来发出 contextPrepared 消息,告知SpringBoot 应用使用的 ApplicationContext 已准备OK
  8. 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 SpringBoot 应用使用的 ApplicationContext 已装填OK
  9. refresh ApplicationContext,完成IoC容器可用的最后一步
  10. SpringApplicationRunListener 来发出 started 消息
  11. 完成最终的程序的启动
  12. SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了

原文地址:https://www.cnblogs.com/magic-sea/p/11333383.html

时间: 2024-11-07 00:49:21

SpringBoot:Spring容器的启动过程的相关文章

spring容器的启动过程(1)

容器启动过程总体流程 public void refresh() throws BeansException, IllegalStateException { //容器在启动之前要获得对象锁,保证容器只有一个启动synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the inter

Java Web容器的启动过程

Java web容器的启动与处理请求的过程 1.启动一个web项目的时候,web容器回去读取它的配置文件web.xml,读取<Context-param>结点. 2.容器创建一个servletContext(Servlet上下文),这个web项目的所有部分都将共享这个上下文. 3.容器将<context-param>转换为键值对,并交个ServletContext.因为listener,filter等组件在初始化时会用到这些上下文的信息,所以要先加载. 4.容器创建<list

IoC容器的启动过程

 <context-param>   <param-name>contextConfigLocation</param-name>   <param-value>applicationContext.xml</param-value>  </context-param>    <listener>   <listener-class>org.springframework.web.context.Context

spring注解配置启动过程

最近看起spring源码,突然想知道没有web.xml的配置,spring是怎么通过一个继承于AbstractAnnotationConfigDispatcherServletInitializer的类来启动自己的.鉴于能力有限以及第一次看源码和发博客,不到之处请望谅~ 我用的IDE是IntelliJ IDEA,这个比myEclipse看源码方便一点,而且黑色背景挺喜欢.然后项目是在maven下的tomcat7插件运行.spring版本是4.3.2.RELEASE. 如果写过纯注解配置的spri

Spring容器启动过程

搞了一年多的Java了,每个项目都在用Spring,这几天没事看了看Spring源码,总结了下Spring容器的启动过程,想把它记录下来,免得忘了 spring容器的启动方式有两种: 1.自己提供ApplicationContext自己创建Spring容器 2.Web项目中在web.xml中配置监听启动 org.springframework.web.context.ContextLoaderListener 先介绍第一种(自创建) ClassPathXmlApplicationContext 

Spring容器-ApplicationContext的启动过程

转载自: http://blog.163.com/[email protected]/blog/static/118777042009410248557/ 这片博客信息量很大,言简意赅.简明扼要地说清楚了Spring容器的启动过程,前面红色的“打比方”可以忽略... Spring容器像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式进行工作.如果我们将Spring容器比喻为一辆汽车,可以将BeanFactory看成汽车的发动机,而ApplicationContex

spring的启动过程就是创建ioc容器的过程

1. spring简介 spring的最基本的功能就是创建对象及管理这些对象之间的依赖关系,实现低耦合.高内聚.还提供像通用日志记录.性能统计.安全控制.异常处理等面向切面的能力,还能帮我们管理最头疼的数据库事务,本身提供了一套简单的JDBC访问实现,提供与 第三方数据访问框架集成(如hibernate.JPA),与各种Java EE技术整合(如JavaMail.任务调度等等),提供一套自己的web层框架Spring MVC.而且还能非常简单的与第三方web框架集成.从这里我们可以认为Sprin

Spring 源码学习(二) IOC容器启动过程

这一节主要是记录一下Spring Ioc 容器的启动过程. Spring 的 Ioc 容器是怎么被加载和使用的? web容器为它提供了宿主环境 ServlectContext,  Tomcat 启动时会读取web.xml. 并且实例化web.xml中配置的ContextLoaderListener ,下面看一下ContextLoaderListener的创建过程: 在实例化ContextLoaderListener 之后,通过接口回调执行ContextLoaderListener 类中的cont

Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServ