SpringBoot运行源代码分析

简介: SpringBoot运行源代码分析

我们知道SpringBoot是基于“约定优于配置”,也知道可以根据starters自动加载和配置相应的服务,那么SpringBoot底层是怎么实现这些操作呢?这篇文章带大家通过源码分析了解相关知识。


SpringApplication的拆解


通常创建SpringBoot项目之后,默认的启动代码只有一行,通过默认的配置基本上可以完成大多数的功能,但如果需要对启动流程的扩展,就需要对SpringBoot的启动方法进行拆解。其实这个操作在《SpringBoot基础之banner玩法解析》这篇文章中已经实践过。通过扩展来设置banner相关操作。我们还可以通过其他set操作来设置其他扩展。










@SpringBootApplicationpublic class SpringLearnApplication {
  public static void main(String[] args) {    SpringApplication app = new SpringApplication(SpringLearnApplication.class);    app.setXXX(...);// 自定义扩展    app.run(args);  }}


SpringBoot实例化


先来看看SpringBoot实例化时都做了些什么:















public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {    this.resourceLoader = resourceLoader;    Assert.notNull(primarySources, "PrimarySources must not be null");    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));    // 1.判断应用的类型    this.webApplicationType = WebApplicationType.deduceFromClasspath();    // 2.加载扫描到的Initializer    setInitializers((Collection) getSpringFactoriesInstances(        ApplicationContextInitializer.class));    // 3.设置**ApplicationListener    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));    // 4.推断并设置main方法的定义类    this.mainApplicationClass = deduceMainApplicationClass();  }


通过代码中的注释,可以了解到SpringApplication初始化可分为四部:

  • 判断应用的类型;
  • 加载扫描到的Initializer;
  • 加载扫描到的Listener;
  • 推断并设置main方法的定义类。


下面针对四个步骤再看一下相应的源代码实现。


判断应用的类型


此功能在枚举类WebApplicationType中实现,根据INDICATOR_CLASSES的类型来判断当前web应用是什么类型的,分别有:REACTIVE(reactive web应用)、SERVLET(基于servlet的web应用)、NONE(不启动web应用)。














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


加载扫描到的Initializer


使用SpringFactoriesLoader扫描加载classpath下的META-INF/spring.factories文件中所有可用的Initializer。



































private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);        if (result != null) {            return result;        } else {            try {                Enumeration<URL> urls = 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();                    UrlResource resource = new UrlResource(url);                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);                    Iterator var6 = properties.entrySet().iterator();
                    while(var6.hasNext()) {                        Entry<?, ?> entry = (Entry)var6.next();                        String factoryClassName = ((String)entry.getKey()).trim();                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());                        int var10 = var9.length;
                        for(int var11 = 0; var11 < var10; ++var11) {                            String factoryName = var9[var11];                            result.add(factoryClassName, factoryName.trim());                        }                    }                }                cache.put(classLoader, result);                return result;            } catch (IOException var13) {                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);            }        }    }

其中spring.factories默认在spring-boot-autoconfigure包下,文件的内容格式为:









# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\# 省略其他的


加载扫描到的ApplicationListener


同样是使用SpringFactoriesLoader扫描加载classpath下的META-INF/spring.factories文件中所有可用的Listener。对应文件类的Listener为:












# Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listenersorg.springframework.boot.autoconfigure.AutoConfigurationImportListener=\org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener


推断并设置main方法的定义类
















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


run方法的基本操作


当实例化对象之后,会调用run方法。下面通过源代码来了解一下run方法都做了些什么。



























































public ConfigurableApplicationContext run(String... args) {    StopWatch stopWatch = new StopWatch();    stopWatch.start();    ConfigurableApplicationContext context = null;    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();    configureHeadlessProperty();    // 创建SpringApplicationRunListener    SpringApplicationRunListeners listeners = getRunListeners(args);    // 遍历并调用SpringApplicationRunListener的starting方法    listeners.starting();    try {        // 创建参数配置      ApplicationArguments applicationArguments = new DefaultApplicationArguments(          args);      // 环境参数准备      ConfigurableEnvironment environment = prepareEnvironment(listeners,          applicationArguments);      configureIgnoreBeanInfo(environment);      // 打印banner      Banner printedBanner = printBanner(environment);      // 创建ApplicationContext      context = createApplicationContext();      // 获得SpringFactories实例      exceptionReporters = getSpringFactoriesInstances(          SpringBootExceptionReporter.class,          new Class[] { ConfigurableApplicationContext.class }, context);      // 设置Environment,加载相关配置      prepareContext(context, environment, listeners, applicationArguments,          printedBanner);      // 刷新ApplicationContext      refreshContext(context);      // 刷新context之后调用此方法      afterRefresh(context, applicationArguments);      stopWatch.stop();      if (this.logStartupInfo) {        new StartupInfoLogger(this.mainApplicationClass)            .logStarted(getApplicationLog(), stopWatch);      }      // 刷新context,启动application      listeners.started(context);      // 启动程序      callRunners(context, applicationArguments);    }    catch (Throwable ex) {      handleRunFailure(context, ex, exceptionReporters, listeners);      throw new IllegalStateException(ex);    }
    try {        // 发出running消息,告知程序已启动      listeners.running(context);    }    catch (Throwable ex) {      handleRunFailure(context, ex, exceptionReporters, null);      throw new IllegalStateException(ex);    }    return context;  }


如果查看当前自动配置


如果想查看当前启动了哪些自动配置有以下几种方式。


(1)运行jar时增加--debug参数。


java -jar xx.jar --debug

(2)在application.properties进行配置。


debug=true

(3)在IDE运行程序中添加VM参数。


-Ddebug

控制台会输出对应日志。已启动的自动配置日志如下:









Positive matches:-----------------
   CodecsAutoConfiguration matched:      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)
   CodecsAutoConfiguration.JacksonCodecConfiguration matched:      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)


未启动的自动配置日志为:











Negative matches:-----------------
   ActiveMQAutoConfiguration:      Did not match:         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
   AopAutoConfiguration:      Did not match:         - @ConditionalOnClass did not find required class 'org.aspectj.lang.annotation.Aspect' (OnClassCondition)


小结

本篇文章我们简单介绍了springboot启动时都做了些什么,后面我们会逐步讲到整个的运作原理。

目录
相关文章
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的民谣网站附带文章和源代码设计说明文档ppt
基于springboot+vue.js的民谣网站附带文章和源代码设计说明文档ppt
31 4
|
11天前
|
Java Linux Spring
在 Linux 系统中将 Spring Boot 应用作为系统服务运行
【6月更文挑战第11天】最近由于一些原因,服务器经常会重启,每次重启后需要手动启动 Spring Boot 的工程,因此我需要将其配置成开启自启动的服务。
171 1
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的汽车维修预约服务系统附带文章和源代码设计说明文档ppt
基于springboot+vue.js的汽车维修预约服务系统附带文章和源代码设计说明文档ppt
25 7
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的基于Web教师个人成果管理系统附带文章和源代码设计说明文档ppt
基于springboot+vue.js的基于Web教师个人成果管理系统附带文章和源代码设计说明文档ppt
26 7
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的疫情期间高校人员管理附带文章和源代码设计说明文档ppt
基于springboot+vue.js的疫情期间高校人员管理附带文章和源代码设计说明文档ppt
24 6
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的校园服务平台附带文章和源代码设计说明文档ppt
基于springboot+vue.js的校园服务平台附带文章和源代码设计说明文档ppt
22 6
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的附绿城郑州爱心公益网站带文章和源代码设计说明文档ppt
基于springboot+vue.js的附绿城郑州爱心公益网站带文章和源代码设计说明文档ppt
29 6
|
1天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue的电商数据分析的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的电商数据分析的详细设计和实现(源码+lw+部署文档+讲解等)
6 0
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的美妆购物网站附带文章和源代码设计说明文档ppt
基于springboot+vue.js的美妆购物网站附带文章和源代码设计说明文档ppt
39 5
|
26天前
|
JavaScript Java 测试技术
基于springboot+vue.js的社区物业管理系统附带文章和源代码设计说明文档ppt
基于springboot+vue.js的社区物业管理系统附带文章和源代码设计说明文档ppt
32 4