SpringBoot2.x启动源码简析

缘起

学习了《小D课堂》springboot2.x课程5.2节. 开始写笔记

分析

本文仅仅是简单分析一下springboot启动的入口

1
2
3
4
5
6
@SpringBootApplication
public class App {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}

​ 源码1

跟进run方法, 连跟几步,来到

org.springframework.boot.SpringApplication.run(Class<?>[], String[])

1
2
3
4
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}

​ 源码2

于是我们进行2个入口点的分析

  1. SpringApplication的构造器. 即源码2 的第3行的 new SpringApplication(primarySources), 此时 primarySources 是 class com.yfs.App, 然后连跟几步

    来到了如下SpringApplication的构造器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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 = deduceWebApplicationType();
    setInitializers((Collection) getSpringFactoriesInstances(
    ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
    }

    ​ 源码3

    此时 primarySources 是 [class com.yfs.App], 第6行,源码如下

    org.springframework.boot.SpringApplication.deduceWebApplicationType()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
    && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
    return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
    if (!ClassUtils.isPresent(className, null)) {
    return WebApplicationType.NONE;
    }
    }
    return WebApplicationType.SERVLET;
    }

    ​ 源码4

    这里是在决定web容器的类型——这种类型是一个枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public enum WebApplicationType {

    /**
    * The application should not run as a web application and should not start an
    * embedded web server.
    */
    NONE,

    /**
    * The application should run as a servlet-based web application and should start an
    * embedded servlet web server.
    */
    SERVLET,

    /**
    * The application should run as a reactive web application and should start an
    * embedded reactive web server.
    */
    REACTIVE

    }

    ​ 源码5

    SERVLET就是普通的SERVLET(内嵌tomcat),REACTIVE就是以后要讲的WEBFLUX.

    WEB_ENVIRONMENT_CLASSES是

    1
    "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"

    REACTIVE_WEB_ENVIRONMENT_CLASS 是

    1
    org.springframework.web.reactive.DispatcherHandler

    很明显,源码4就是在根据当前类路径下是否存在这些类来判断WebApplicationType. 这里得到的是SERVLET(因为我是IDE里面运行main方法启动的,就是内嵌tomcat)

    然后来到源码3的第七行, setInitializers 到是平淡无奇

    org.springframework.boot.SpringApplication.setInitializers(Collection<? extends ApplicationContextInitializer<?>>)

    1
    2
    3
    4
    5
    public void setInitializers(
    Collection<? extends ApplicationContextInitializer<?>> initializers) {
    this.initializers = new ArrayList<>();
    this.initializers.addAll(initializers);
    }

    initializers 是SpringApplication的属性。源码3的第七行的getSpringFactoriesInstances才比较值得关注

    跟几步来到了

    org.springframework.boot.SpringApplication.getSpringFactoriesInstances(Class, Class<?>[], Object…)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
    Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
    }

    ​ 源码6

    除了type是 ApplicationContextInitializer之外,其余两个入参都是空的. 源码6的第一行得到的classLoader是AppClassLoader.

    然后来到了重点——SpringFactoriesLoader.loadFactoryNames方法. 跟进去

    org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames(Class<?>, ClassLoader)

    入参分别为ApplicationContextInitializer 和 AppClassLoader

1
2
3
4
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

​ 源码7

factoryClassName 是 org.springframework.context.ApplicationListener. classLoader是AppClassLoader.

跟进 org.springframework.core.io.support.SpringFactoriesLoader.loadSpringFactories(ClassLoader)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}

try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

​ 源码8

这里第九行的参数 FACTORIES_RESOURCE_LOCATION 是 META-INF/spring.factories. 这玩意在以后自定义starter有用.

最后返回的result就是一堆的autoconfigure包中的东西. 然后源码7的第三行的getOrDefault就是从这一堆的autoconfigure的类中掏取出 ApplicationContextInitializer. 最后源码7第三行返回的就是result中的一堆的ApplicationListener. 所以源码6的第五行返回的是这些ApplicationContextInitializer的全限定名, 然后源码6的第7行就是拿这些names去实例化这些bean得到instances.

源码6的第9行进行排序, 最后源码6返回排序之后的 ApplicationContextInitializer。

也就是源码3的第七行和第九行就分别获取ApplicationContextInitializer和ApplicationListener来初始化SpringApplication中的两个属性 initializers和 listeners.

源码3的最后一行获取main方法入口类. 跟进

org.springframework.boot.SpringApplication.deduceMainApplicationClass()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

​ 源码9

这里采用的就是取得运行时,然后返回main方法的类的字节码对象.

mainApplicationClass这里得到的是 class org.springframework.boot.SpringApplication(注意,SpringApplication中也有main方法)

然后来到了SpringApplication的run方法.

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

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

​ 源码10

第七行获取监听器. 值得注意的是22行,它其实就是在调用创建的application的refresh方法. 这个方法我们经常讲的