SpringBoot启动流程分析原理(一)

最后更新于:2022-08-12 11:32:53

首先,先找到应用程序的入口主方法,在上面打一个断点

启动之后,F5进入到run()方法

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

到这里会执行 new SpringApplication(primarySources)创建Spring应用对象,继续F5往下会执行SpringApplication构造器

// SpringApplication构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;

        // 资源加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));

        // 1. 可能的web应用程序类型的类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();

        //2. 设置初始化应用
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

        //3.设置初始化监听 
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

        //4. 推演主程序类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

对象初始化

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }

一、推断web应用类型

  • NONE, // 不是web应用
  • SERVLET, // servlet容器
  • REACTIVE; // 反应型web应用(webflux)

因为一开始加入了web依赖,所以是servlet容器。

二、初始化应用上下文

在设置初始化应用context的时候 ,是先执行了getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,参数是ApplicationContextInitializer.class字节码对象

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
    // 加载ApplicationContextInitializer.class类型的类
    // 这里传入就是参数 ApplicationContextInitializer.clas
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化加载到的类
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    // 返回
    return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

再来看看是如何加载到这些类

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // 先从缓存中拿
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
    return result;
    }
    try {
        // 去资源路径下加载
        public static final String ACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 
        public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 
        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()) {
                String factoryClassName = ((String) entry.getKey()).trim();
                for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryClassName, factoryName.trim());
             }
            }
        }
        cache.put(classLoader, result);
            // 返回所有的加载的类
        return result;
    }catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
     }
}

这里两个加载配置类的地方都指向META-INF/spring.factories,通过断点我们可以看到应用程序时加载了一下几个jar的spring.factories文件

spring-boot-2.1.5.RELEASE.jar下的spring.factores

spring-boot-autoconfigure-2.1.5.RELEASE.jar下的spring.factores

spring-beans-5.1.7.RELEASE.jar下的spring.factores

从Map中根据org.springframework.context.ApplicationContextInitializer的类型拿到需要的类初始化类,断点进入getOrDefault(factoryClassName, Collections.emptyList());方法

之后就是把加载到的需要初始化的类进行实例化添加到一个集合中等待备用

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

三、初始化监听器类

当我们跟进去之后,会发现在初始化监听类的时候和上面初始化应用上下文是一样的代码。唯一不同的是getSpringFactoriesInstances(ApplicationListener.class))传进去的是ApplicationListener.class所以这里就不再赘述。

四、推演主程序类

this.mainApplicationClass = deduceMainApplicationClass();


到这里就完成了SpringBoot启动过程中初始化SpringApplication的过程。

SpringBoot启动过程中初始化SpringApplication的流程,大致可以分为四个步骤:
  1. 推演web应用的类型(如果没有加web依赖类型NONE)
  2. 初始化ApplicationContextInitializer
  3. 初始化ApplicationListener
  4. 推演出主程序类