SpringBoot应用启动流程源码解析

网友投稿 240 2022-12-09


SpringBoot应用启动流程源码解析

前言

Springboot应用在启动的时候分为两步:首先生成 SpringApplication 对象 ,运行 SpringApplication 的 run 方法,下面一一看一下每一步具体都干了什么

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));

     //判断当前是否一个web应用

this.webApplicationType = WebApplicationType.deduceFromClasspath();

     //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来

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

     //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener 

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

     //从多个配置类中找到有main方法的主配置类 

this.mainApplicationClass = deduceMainApplicationClass();

}

其中从类路径下获取到META-INF/spring.factories配置的所有ApplicationContextInitializer和ApplicationListener的具体代码如下

public final class SpringFactoriesLoader {

/**spring.factories的位置*/

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

/**

* 缓存扫描后的结果, 注意这个cache是static修饰的,说明是多个实例共享的

* 其中MultiValueMap的key就是spring.factories中的key(比如org.springframework.boot.autoconfigure.EnableAutoConfiguration),

* 其值就是key对应的value以逗号分隔后得到的List集合(这里用到了MultiValueMap,他是guava的一键多值map, 类似Map>)

*/

private static final Map> cache = new ConcurrentReferenceHashMap<>();

private SpringFactoriesLoader() {

}

/**

* AutoConfigurationImportSelector及应用的初始化器和监听器里最终调用的就是这个方法,

* 这里的factoryType是EnableAutoConfiguration.class、ApplicationContextInitializer.class、或ApplicationListener.class

* classLoader是AutoConfigurationImportSelector、ApplicationContextInitializer、或ApplicationListener里的beanClassLoader

*/

public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {

String factoryTypeName = factoryType.getName();

return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());

}

/**

* 加载 spring.factories文件的核心实现

*/

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {

// 先从缓存获取,如果获取到了说明之前已经被加载过

MultiValueMap result = cache.get(classLoader);

if (result != null) {

return result;

}

try {

// 找到所有jar中的spring.factories文件的地址

Enumeration urls = (classLoader != null ?

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

result = new LinkedMultiValueMap<>();

// 循环处理每一个spring.factories文件

while (urls.hasMoreElements()) {

URL url = urls.nextElement();

UrlResource resource = new UrlResource(url);

// 加载spring.factories文件中的内容到Properties对象中

Properties properties = PropertiesLoaderUtils.loadProperties(resource);

// 遍历spring.factories内容中的所有的键值对

for (Map.Entry, ?> entry : properties.entrySet()) {

// 获得http://spring.factories内容中的key(比如org.springframework.boot.autoconfigure.EnableAutoConfiguratio)

String factoryTypeName = ((String) entry.getKey()).trim();

// 获取value, 然后按英文逗号(,)分割得到value数组并遍历

for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {

// 存储结果到上面的多值Map中(MultiValueMap)

result.add(factoryTypeName, factoryImplementationName.trim());

}

}

}

cache.put(classLoader, result);

return result;

}

catch (IOException ex) {

throw new IllegalArgumentException("Unable to load factories from location [" +

FACTORIES_RESOURCE_LOCATION + "]", ex);

}

}

}

运行run方法

public ConfigurableApplicationContext run(String... args) {

  //开始停止的监听

StopWatch stopWatch = new StopWatch();

stopWatch.start();

  //声明一个可配置的ioc容器

ConfigurableApplicationContext context = null;

FailureAnalyzers analhttp://yzers = null;

//配置awt相关的东西

configureHeadlessProperty();

//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories

SpringApplicationRunListeners listeners = getRunListeners(args);

//回调所有的获取SpringApplicationRunListener.starting()方法

listeners.starting();

try {

//封装命令行参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(

args);

//准备环境

ConfigurableEnvironment environment = prepareEnvironment(listeners,

applicationArguments);

//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成

Banner printedBanner = printBanner(environment);

//创建ApplicationContext;决定创建web的ioc还是普通的ioc,

    //通过反射创建ioc容器((ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);)

context = createApplicationContext();

//出现异常之后做异常分析报告

analyzers = new FailureAnalyzers(context);

//准备上下文环境;将environment保存到ioc中;而且applyInitializers();

//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法

//回调所有的SpringApplicationRunListener的contextPrepared();

//

prepareContext(context, environment, listeners, applicationArguments,

printedBanner);

//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();

//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版

//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)

refreshContext(context);

//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调

//ApplicationRunner先回调,CommandLineRunner再回调

afterRefresh(context, applicationArguments);

//所有的SpringApplicationRunListener回调finished方法

listeners.finished(context, null);

stopWatch.stop();

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass)

.logStarted(getApplicationLog(), stopWatch);

}

//整个SpringBoot应用启动完成以后返回启动的ioc容器;

return context;

}

catch (Throwable ex) {

handleRunFailure(context, listeners, analyzers, ex);

throw new IllegalStateException(ex);

}

}

几个重要的事件回调机制

配置在META-INF/spring.factories

ApplicationContextInitializer

    SpringApplicationRunListener

只需要放在ioc容器中

ApplicationRunner

    CommandLineRunner


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java full gc触发情况实例解析
下一篇:Java util concurrent及基本线程原理简介
相关文章

 发表评论

暂时没有评论,来抢沙发吧~