美文网首页
SpringBoot启动分析

SpringBoot启动分析

作者: 飘飘飘荡 | 来源:发表于2020-07-18 23:31 被阅读0次

前言:本次源码分析使用SpringBoot-2.2.5.RELEASE版本。

SpringBoot启动入口

想要启动一个SpringBoot应用很简单,只需要在包的根目录下创建一个类,并且在类上添加注解@SpringBootApplication标注其是一个SpringBoot应用即可:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

通过执行该启动类的Main方法即可启动一个SpringBoot应用,接下来整个SpringBoot的启动过程就是通过SpringApplication的run方法来进行。

SpringApplication初始化

SpringApplication类可用于从main方法引导和启动Spring应用程序,默认情况下该类将执行以下步骤来引导程序的启动:

  • 创建一个ApplicationContext上下文实例
  • 注册CommandLinePropertySource以将命令行参数公开为Spring属性
  • 刷新应用程序上下文并加载所有单例Bean

该类的初始化由该类调用自身的run方法开始,再调用自身重载的run方法后得到初始化机会:

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

上面的new 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));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

核心的初始化分为以下三个步骤:

初始化WebApplicationType

应用类型的初始化通过WebApplicationType.deduceFromClasspath()来进行,点击进入其中查看对应源码:

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };
    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_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    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;
    }
}

可以看出,通过反射判断环境是否存在这个类来判断获取初始化的web应用类型。

初始化Initializers

用于读取所有META-INF/spring.factories文件中配置了ApplicationContextInitializer的初始化器:

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

可以看出它将从spring.factories文件中获取的配置进行实例化后,封装成一个集合对SpringApplication中的initializers集合属性进行初始化。

初始化Listeners

跟初始化Initializers类似也是通过读取所有META-INF/spring.factories文件中配置的ApplicationListener类型的监听器,最终配置被实例化之后封装成一个集合对SpringApplication中的listeners集合属性进行初始化:

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    this.listeners = new ArrayList<>(listeners);
}

值得注意的是,不管是初始化Initializers或者初始化Listeners,上面都省略了一个步骤,即读取配置和实例化的步骤,是因为它是一个通用读取的过程,以下详细讲解。
要读取配置中的组件信息,需要调用通用方式getSpringFactoriesInstances并传入对应的参数类型,例如:

# 读取ApplicationContextInitializer类型的配置信息
getSpringFactoriesInstances(ApplicationContextInitializer.class)
# 读取ApplicationListener类型的配置信息
getSpringFactoriesInstances(ApplicationListener.class)

这里列举了ApplicationContextInitializer类型和ApplicationListener类型在META-INF/spring.factories文件中的配置:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

点击getSpringFactoriesInstances方法进入内部查看源码:

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();
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

可以看出最终它是由loadFactoryNames方法解析出配置并存放到Set集合中,最终将该集合作为参数传入createSpringFactoriesInstances方法,通过Set参数集合实例化出一个个实例。
而这里的Set集合就是一个个的ApplicationContextInitializer或ApplicationListener所对应的组件信息,点击loadFactoryNames方法进入,需要注意的是参数type为要读取的配置类型:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
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 factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

通过以上的loadSpringFactories方法,它遍历了每个jar包中的META-INF/spring.factories文件,并把spring.factories文件中的每个配置全部读取出来放到一个map中,每个map的key就是spring.factories文件中对应的一个属性配置key,value则是这个key所对应的一些需要初始化的组件,如下图展示其中一个spring.factories文件内容,以及读取出来的组件信息,以ApplicationContextInitializer类型为例:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

该配置对应读取出来的map信息为:



最后在loadFactoryNames方法中通过factoryTypeName即ApplicationContextInitializer这个类型来筛选出其中的实现类,得到了以下结果:



这就是ApplicationContextInitializer类型的Set<String>集合,拿到该集合后需要将集合中的ApplicationContextInitializer类型的实现进行一个个实例化出来,所以调用createSpringFactoriesInstances方法并将上述解析出来的实现集合作为参数传入(另外还有一个重要的参数即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<?> 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;
}

这段代码的意思就是将把spring.factories文件中的每个配置全部读取出来放到一个map中后,遍历该map的key,通过反射的方式实例化出来放到放instances集合中。
至此,整个SpringApplication实例初始化完成,而核心的属性Initializers和Listeners会在后续启动做作为重要参数来初始化。

SpringApplicationRunListeners初始化

待续...

相关文章

网友评论

      本文标题:SpringBoot启动分析

      本文链接:https://www.haomeiwen.com/subject/yjuqkktx.html