Spring Boot启动流程

Spring Boot的启动非常简单,就是一行代码并且很强大,于是我就花了些时间研究了一下这行代码都做了些什么事情,并记录下来。下面是我绘制的一张启动流程图:
image.png

Spring Boot启动方法的改造

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

Spring Boot的启动的这行代码,内部就是执行了SpringApplication的一个静态方法run。进入这个run方法:

public static ConfigurableApplicationContext run(Class primarySource,
      String... args) {
   return run(new Class[] { primarySource }, args);
}

当调用SpringApplication.run方法后,该方法会继续调用第2个run方法

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

发现在第2个run方法中new了一个新的SpringApplication对象,并调用了run方法,传入args参数。也就意味着这一行代码可以改写成下面这个样子:

public static void main(String[] args) {
    //SpringApplication.run(DILoopApplication.class, args);
    SpringApplication sbapp = new SpringApplication(DILoopApplication.class);
    ConfigurableApplicationContext context = sbapp.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));
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

构造函数代码比较少,我们来一行一行的分析 
1: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";

WebApplicationType其实就是一个枚举,定义了三个应用类型:
• NONE:不作为web应用启动,依赖外部web server。
• SERVLET:基于servlet的web应用启动, 使用内嵌 web server。
• REACTIVE:响应式web应用启动,使用内嵌的响应式web server。
然后来看下Spring boot的推断过程

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

ClassUtils.isPresent主要工作就是通过传入的类名,在应用程序里反射,如果反射成功返回true, 反射失败返回 false
Case 1: class path 中存在类org.springframework.web.reactive.DispatcherHandler同时不存在类org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer 那应用就是REACTIVE类型
Case 2: 在class path 中同时存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext那应用就是SERVLET类型
CASE 3: 既不属于case1, 也不属于case2那应用就是NONE类型
2:setInitializers

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

这个函数很简单就是把将getSpringFactoriesInstances的返回结果全部添加到成员变量initializers数组中。
下面主要来看getSpringFactoriesInstances函数,他的主要功能就是扫描应用程序的所有jar包,然后读取jar中meta-inf/factory.properties文件中配置的ApplicationContextInitializer类,加载到内存中,并反射创建出具体对象。

private  Collection getSpringFactoriesInstances(Class type) {
   return getSpringFactoriesInstances(type, new Class[] {});
}

private  Collection getSpringFactoriesInstances(Class type,
      Class[] parameterTypes, Object... args) {
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   Set names = new LinkedHashSet<>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   List instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

getSpringFactoriesInstances函数中又调用了SpringFactoriesLoader.loadFactoryNames,
这个函数的功能就是遍历所有的jar包并扫描jar下的factory.properties文件。然后解析文件中的所有的properties,并保存到缓冲中。函数loadSpringFactoires执行完后会返回一个key-value的map,然后spring boot又通过getOrDefault函数来过滤了一遍返回值(只获取key是ApplicationContextInitializer类的value)
后面会写一个小case来说明这个getOrDefault的功能。
下面是loadFactory的部分源代码:

public static List loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {

                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {

                    for(int var11 = 0; var11 < var10; ++var11) {
                        

                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;

    }
}

然后再回到getSpringFactoriesInstances,看一下他里面调用的createSpringFactoriesInstances方法,这个函数功能很简单就是,将上一步获取到的所有类全名,进行创建并load到内存中。( 循环names集合,先进行forName加载获取反射出class对象,然后再调用他的构造函数初始化对象信息。)
下面是部分源代码:

private  List createSpringFactoriesInstances(Class type,
      Class[] parameterTypes, ClassLoader classLoader, Object[] args,
      Set names) {
   for (String name : names) {
      try {
         Class instanceClass = ClassUtils.forName(name, classLoader);
         
         Constructor constructor = instanceClass
               .getDeclaredConstructor(parameterTypes);
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
      }
      catch (Throwable ex) {}
      }
   }
}

3:setListeners

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

这个函数很简单就是把将getSpringFactoriesInstances的返回结果全部添加到成员变量listeners数组中。
setListeners中调用的getSpringFactoriesInstances流程和setInitializers时执行逻辑是一样的,只是传入的参数不一样了,setListeners是传入的参数是ApplicationListener
deduceMainApplicationClass
最后一步就是创建springboot的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;
}

getOrDefault(Object key, V defaultValue)

当map中存在该key时,返回该key对应的value,如果map中不存在该key时,则返回defaultValue

Map name_age = new HashMap<>();
name_age.put("qingfen", 16);
name_age.put("lantian", 17);
name_age.put("baiyun", 18);

int mm = name_age.getOrDefault("qingfen", 99);
int mm1 = name_age.getOrDefault("lantian2", 99);

System.out.println("在map中找到数据,getOrDefault返回的数据" + mm);
System.out.println("在map中找不到数据,getOrDefault返回的数据" + mm1);

Map中可以查询到qingfen,所以返回qingfen对应 的Value16,但在查询不到lantian2,所以返回给定的默认值99。


image.png

你可能感兴趣的:(Spring Boot启动流程)