Spring Boot启动源码分析一万字
Posted 刘Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot启动源码分析一万字相关的知识,希望对你有一定的参考价值。
文章目录
- 1 new SpringApplication构建Sring应用实例
- 2 run运行spring应用实例
- 3 总结
本次的spring-boot-starter-parent版本为2.3.0。
Spring Boot项目的启动入口是一个main方法,因此我们从该方法入手即可。
关键类就是SpringApplication,他的run方法将当前启动类的class和main方法参数传递进去并执行初始化操作,这个参数就是启动参数。
/**
* SpringApplication的方法
*
* @param primarySource 启动类的class
* @param args 启动参数
* @return 可配置的应用程序上下文
*/
public static ConfigurableApplicationContext run( Class<?> primarySource, String... args )
//调用另一个重载方法
return run( new Class<?>[] primarySource , args );
/**
* SpringApplication的方法
*
* @param primarySources 启动类的class数组
* @param args 启动参数
* @return 可配置的应用程序上下文
*/
public static ConfigurableApplicationContext run( Class<?>[] primarySources, String[] args )
//构造一个SpringApplication实例,然后执行run方法
return new SpringApplication( primarySources ).run( args );
可以看到,静态run方法最终会构造一个SpringApplication实例,然后执行run方法进行启动初始化。
1 new SpringApplication构建Sring应用实例
该构造器将创建一个SpringApplication实例并且执行一些初始化操作。比如设置主要bean来源集合,设置应用程序类型,设置ApplicationContextInitializer初始化器集合,设置Listener监听器集合等。
/**
* 创建一个新的SpringApplication实例,并从指定的来源加载bean的信息
*
* @param primarySources 主要bean来源
*/
public SpringApplication( Class<?>... primarySources )
//空的资源加载器
this( null, primarySources );
/**
* 主要bean来源集合
*/
private Set<Class<?>> primarySources;
/**
* 应用程序初始化器集合
*/
private List<ApplicationContextInitializer<?>> initializers;
/**
* 应用程序监听器集合
*/
private List<ApplicationListener<?>> listeners;
/**
* 主应用程序类
*/
private Class<?> mainApplicationClass;
/**
* 应用程序类型
*/
private WebApplicationType webApplicationType;
/**
* 创建一个新的SpringApplication实例,并从指定的来源加载bean的信息
*
* @param resourceLoader 要使用的资源加载器
* @param primarySources 主要bean来源
*/
@SuppressWarnings( "unchecked", "rawtypes" )
public SpringApplication( ResourceLoader resourceLoader, Class<?>... primarySources )
this.resourceLoader = resourceLoader;
Assert.notNull( primarySources, "PrimarySources must not be null" );
//将主要来源设置到一个集合里面,默认来源就只有一个启动类的class
this.primarySources = new LinkedHashSet<>( Arrays.asList( primarySources ) );
//设置Web应用程序类型,可能是SERVLET程序,也可能是REACTIVE响应式程序,还有可能是NONE,即非web应用程序
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置ApplicationContextInitializer,应用程序初始化器。从META-INF/spring.factories文件中获取
setInitializers( ( Collection )getSpringFactoriesInstances( ApplicationContextInitializer.class ) );
//设置ApplicationListener,应用程序监听器。从META-INF/spring.factories文件中获取
setListeners( ( Collection )getSpringFactoriesInstances( ApplicationListener.class ) );
//设置主应用程序类,一般就是Spring boot项目的启动类
this.mainApplicationClass = deduceMainApplicationClass();
1.1 deduceFromClasspath推断应用程序类型
该方法根据是否存在指定路径的类来推断应用程序类型。有NONE、REACTIVE、SERVLET三种,一般都是SERVLET类型。
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_INDICATOR_CLASSES = "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" ;
static WebApplicationType deduceFromClasspath()
//如果存在org.springframework.web.reactive.DispatcherHandler类型,并且不存在org.springframework.web.servlet.DispatcherServlet类型
//并且不存在org.glassfish.jersey.servlet.ServletContainer类型
//那么设置为REACTIVE,即响应式web应用程序
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null))
return WebApplicationType.REACTIVE;
//如果不存在javax.servlet.Servlet类型,或者不存在org.springframework.web.context.ConfigurableWebApplicationContext类型
//那么设置为NONE,即非web应用程序
for (String className : SERVLET_INDICATOR_CLASSES)
if (!ClassUtils.isPresent(className, null))
return WebApplicationType.NONE;
//否则设置为SERVLET,即给予servlet的web应用程序,一般都是SERVLET
return WebApplicationType.SERVLET;
1.2 setInitializers设置初始化器
设置ApplicationContextInitializer初始化器,后面初始化的时候会使用到,在Spring上下文被刷新之前进行初始化的操作,例如注入Property Sources属性源或者是激活Profiles环境。
这里会借助SpringFactoriesLoader工具类获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例,也就是ApplicationContextInitializer类型的实例。
spring.factories 是Spirng boot提供的一种扩展机制,实际上spring.factories就是仿照Java中的SPI扩展机制来实现的Spring Boot自己的SPI机制,它是实现Spribf Boot的自动配置的基础。
spring.factories该文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
public void setInitializers( Collection<? extends
ApplicationContextInitializer<?>> initializers )
this.initializers = new ArrayList<>( initializers );
/**
* 借助SpringFactoriesLoader获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例
* spring.factories 文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
*
* @param type 指定类型
*/
private <T> Collection<T> getSpringFactoriesInstances( Class<T> type )
return getSpringFactoriesInstances( type, new Class<?>[] );
private <T> Collection<T> getSpringFactoriesInstances( Class<T> type, Class<?>[] parameterTypes, Object... args )
ClassLoader classLoader = getClassLoader();
//借助SpringFactoriesLoader获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例名称
//spring.factories 文件必须是 Properties 格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。
//使用名称Set集合保存数据,确保唯一以防止重复
Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames( type, classLoader ) );
//根据获取的类型全路径名反射创建实例
List<T> instances = createSpringFactoriesInstances( type, parameterTypes, classLoader, args, names );
//对实例进行排序
AnnotationAwareOrderComparator.sort( instances );
return instances;
1.2.1 loadFactoryNames加载给定类型的全路径名
通过SpringFactoriesLoader从全部“META-INF/spring.factories”文件中根据给定的type类型获取所有对应的是实现类的全路径类名集合。这里的type就是ApplicationContextInitializer。
public static List<String> loadFactoryNames(Class<?> factoryType,
@Nullable ClassLoader classLoader)
//获取工厂类型名,这里就是org.springframework.context.ApplicationContextInitializer
String factoryTypeName = factoryType.getName();
//根据上面的名字作为key从/META-INF/spring.factories文件中获取指定的value并转换为集合
//value就是实现类的全路径名,并通过','拆分多个实现类
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
例如,与常见的自动配置类相关的是位于spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件。其文件中,名为org.springframework.context.ApplicationContextInitializer的key对应的value为包括三个初始化器:
在另一个spring-boot.jar包中同样有该初始化器的配置信息,同样会被加载:
1.2.2 createSpringFactoriesInstances实例化
该方法将会对上面方法加载的全部ApplicationContextInitializer的实现进行实例化,实际上就是一系列反射创建对象的过程。
/**
* SpringApplication的方法
*
* 反射创建实例
*/
private <T> List<T> createSpringFactoriesInstances( Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names )
//最终返回的对象集合
List<T> instances = new ArrayList<>( names.size() );
//遍历全路径名数组
for( String name : names )
try
//根据全路径名获取对应的class对象
Class<?> instanceClass = ClassUtils.forName( name, classLoader );
Assert.isAssignable( type, instanceClass );
//获取指定的构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor( parameterTypes );
//通过构造器反射创建对象
T instance = ( T )BeanUtils.instantiateClass( constructor, args );
instances.add( instance );
catch( Throwable ex )
throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex );
return instances;
1.3 setListeners设置监听器
设置ApplicationListener监听器。该方法和上面的setInitializers方法的逻辑是一样的。
这里是从META-INF/spring.factories文件中获取配置的ApplicationListener类型的实例。
spring-boot-autoconfigure.jar下的/META-INF/spring.factories文件中,名为org.springframework.context.ApplicationListener的key对应的value为包括一个监听器。
在另一个spring-boot.jar包中同样有该初始化器的配置信息,同样会被加载:
1.4 deduceMainApplicationClass推断应用程序主类
该方法推断应用程序主类,一般就是Spring boot项目的启动类,也就是调用main方法的类。
/**
* SpringApplication的方法
* 推断应用程序主类,一般就是Spring boot项目的启动类
*
* @return 主类class
*/
private Class<?> deduceMainApplicationClass()
try
//获取堆栈跟踪记录
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for( StackTraceElement stackTraceElement : stackTrace )
//找到调用main方法的类,返回该类的class
if( "main".equals( stackTraceElement.getMethodName() ) )
return Class.forName( stackTraceElement.getClassName() );
catch( ClassNotFoundException ex )
// Swallow and continue
return null;
2 run运行spring应用实例
该方法将会运行SpringApplication实例,并且创建并刷新一个新的ApplicationContext。该方法就是Spring boot项目启动的关键方法。
该方法主要有以下步骤:
- 获取全部SpringApplicationRunListener运行监听器并封装到SpringApplicationRunListeners中。SpringApplicationRunListener的实现也是从spring.factories文件中加载的,其中一个实现就是EventPublishingRunListener。
- 执行所有SpringApplicationRunListener监听器的starting的方法,可以用于非常早期的初始化操作。EventPublishingRunListener的starting方法会向之前初始化的所有ApplicationListener发送一个ApplicationStartingEvent事件,标志着SpringApplication的启动。
- 根据SpringApplicationRunListeners和启动参数准备环境。这一步会查找项目的配置文件以及激活的profile等信息。
- 打印启动banner图。
- 创建spring上下文容器实例,核心方法。基于servlet的web项目容器是AnnotationConfigServletWebServerApplicationContext类型。
- 准备上下文容器,核心方法。该方法会将我们的启动类注入到springboot的容器上下文内部的beanfactory中,有了这一步,后面就可以解析启动类的注解和各种配置,进而执行springboot的自动配置(启动类上有@SpringBootApplication注解,还有其他各种注解)。
- 刷新上下文容器,核心方法。该方法就是spring容器的启动方法,将会加载和解析容器中的bean以及各种配置。这个方法的源码非常多,我们在之前的spring启动源码部分花了大量时间已经讲过了,在此不再赘述。之前的文章链接Spring IoC容器初始化源码。
- 刷新后处理。该方法是一个扩展方法,没有提供任何默认的实现,我们自定义的子类可以进行扩展。
- 应用SpringApplicationRunListener监听器的started方法。EventPublishingRunListener将会发出ApplicationStartedEvent事件,表明容器已启动
- 调用runner,即执行容器中的ApplicationRunner和CommandLineRunner类型bean的run方法。这也是一个扩展点,用于执行spring容器完全启动后需要做的逻辑。
- 应用SpringApplicationRunListener监听器的running方法。EventPublishingRunListener将会发出ApplicationReadyEvent事件,表明容器已就绪,可以被使用了。
/**
* SpringApplication的方法
* 运行容器
*
* @param args 启动参数,也就是main方法的参数
* @return 一个上下文环境对象,代表着spring容器
*/
public ConfigurableApplicationContext run( String... args )
//创建并启动一个计时器,用于统计run方法执行时常,即应用启动时常
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//spring的应用程序上下文,代表着spring容器
ConfigurableApplicationContext context = null;
//SpringBootExceptionReporter异常报告者集合,用于记录项目启动过程中的异常信息
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置headless系统属性,Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
configureHeadlessProperty();
/*
* 1、获取全部SpringApplicationRunListener运行监听器并封装到SpringApplicationRunListeners中
* SpringApplicationRunListener的实现也是从spring.factories文件中加载的,其中一个实现就是EventPublishingRunListener
*/
SpringApplicationRunListeners listeners = getRunListeners( args );
/*
* 2、执行所有SpringApplicationRunListener监听器的starting的方法,可以用于非常早期的初始化操作
*/
//EventPublishingRunListener的starting方法会向之前初始化的所有ApplicationListener发送一个ApplicationStartingEvent事件
//ApplicationStartingEvent事件标志着SpringApplication的启动,并且此时ApplicationContext还没有初始化,这是一个早期事件
listeners.starting();
try
//参数对象,封装了传递进来的启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments( args );
/*
* 3、根据SpringApplicationRunListeners和启动参数准备环境
*/
ConfigurableEnvironment environment = prepareEnvironment( listeners, applicationArguments );
/*
* 配置spring.beaninfo.ignore属性
* 这个配置用来忽略所有自定义的BeanInfo类的搜索,优化启动速度
*/
configureIgnoreBeanInfo( environment );
/*
* 4、打印启动banner图
*/
Banner printedBanner = printBanner( environment );
/*
* 5、创建spring上下文容器实例,核心方法
*/
context = createApplicationContext();
/*
* 获取SpringBootExceptionReporter异常报告者集合,用于记录项目启动过程中的异常信息
* SpringBootExceptionReporter的实现也是从spring.factories文件中加载的
*/
exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] ConfigurableApplicationContext.class ,
context );
/*
* 6、准备上下文容器,核心方法
* 该方法会将我们的启动类注入到容器中,后续通过该类启动自动配置
*/
prepareContext( context, environment, listeners, applicationArguments, printedBanner );
/*
* 7、刷新上下文容器,核心方法
* 该方法就是spring容器的启动方法,将会加载bean以及各种配置
* 这个方法的源码非常多,我们在之前的spring启动源码部分花了大量时间已经讲过了,在此不再赘述
*/
refreshContext( context );
/*
* 8、刷新后处理
* 该方法是一个扩展方法,没有提供任何默认的实现,我们自定义的子类可以进行扩展。
*/
afterRefresh( context, applicationArguments );
/*
* springboot项目启动完毕,停止stopWatch计时
*/
stopWatch.stop();
/*
* 打印容器启动耗时日志
* 例如:Started SpringBootLearnApplication in 13.611 seconds (JVM running for 20.626)
*/
if( this.logStartupInfo )
new StartupInfoLogger( this.mainApplicationClass ).logStarted( getApplicationLog(), stopWatch );
/*
* 9、应用SpringApplicationRunListener监听器的started方法
* EventPublishingRunListener将会发出ApplicationStartedEvent事件,表明容器已启动
*/
listeners.started( context );
/*
* 10、执行容器中的ApplicationRunner和CommandLineRunner类型的bean的run方法
* 这也是一个扩展点,用于实现spring容器启动后需要做的事
*/
callRunners( context, applicationArguments );
catch( Throwable ex )
/*
* 如果启动过程中出现了异常,那么会将异常信息加入到exceptionReporters中并抛出IllegalStateException
*/
handleRunFailure( context, ex, exceptionReporters, listeners );
throw new IllegalStateException( ex );
try
/*
* 11、应用SpringApplicationRunListener监听器的running方法
* EventPublishingRunListener将会发出ApplicationReadyEvent事件,表明容器已就绪,可以被使用了
*/
listeners.running( context );
catch( Throwable ex )
/*
* 如果发布事件的过程中出现了异常,那么会将异常信息加入到exceptionReporters中并抛出IllegalStateException
*/
handleRunFailure( context, ex, exceptionReporters, null );
throw new IllegalStateException( ex );
//返回容器
return context;
2.1 configureHeadlessProperty配置headless属性
配置headless系统属性,Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。默认为true。
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS =
"java.awt.headless";
private boolean headless = true;
/**
* SpringApplication的方法
*/
private void configureHeadlessProperty()
System.setProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString( this.headless ) ) );
2.2 getRunListeners获取运行监听器
该方法用于获取运行监听器SpringApplicationRunListener,其也是从spring.factories文件中加载的,key为org.springframework.boot.SpringApplicationRunListener。
/**
* SpringApplication的方法
*
* 获取运行监听器SpringApplicationRunListener并封装到SpringApplicationRunListeners中
*/
private SpringApplicationRunListeners getRunListeners( String[] args )
Class<?>[] types = new Class<?>[] SpringApplication.class, String[].class ;
//借助SpringFactoriesLoader获取所有引入的jar包中和当前类路径下的META-INF/spring.factories文件中指定类型的实例
//这里指定类型为SpringApplicationRunListener,默认情况下spring.factories文件中有一个实现,即EventPublishingRunListener
//最终所有的SpringApplicationRunListener集合会被封装到一个SpringApplicationRunListeners对象中。
return new SpringApplicationRunListeners( logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args ) );
//SpringApplicationRunListeners的属性
private final Log log;
private final List<SpringApplicationRunListener> listeners;
/**
* SpringApplicationRunListeners的构造器
*/
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners)
this.log = log;
this.listeners = new ArrayList<>(listeners);
这个key对应的属性仅在spring-boot.jar的spring.factories文件中被设置,且只有一个实现类EventPublishingRunListener。
2.2.1 EventPublishingRunListener事件发布运行监听器
EventPublishingRunListener是一个很有趣的监听器,在上一步被找到之后会对监听器进行实例化,他的构造器如下:
//EventPublishingRunListener的属性
private final SpringApplication application;
private final String[] args;
/**
* 事件广播器,用于广播事件
*/
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener( SpringApplication application, String[] args )
this.application RocketMQ源码—Broker启动流程源码解析一万字
RocketMQ源码—Broker启动加载消息文件以及恢复数据源码一万字