likes
comments
collection
share

SpringBoot源码分析(二)启动类流程

作者站长头像
站长
· 阅读数 32

前言:

上一篇内容,我们分析了SpringBoot的启动类上的注解,了解了SpringBoot Bean的注册和自动装配原理,这篇文章我们接着来分析源码,了解SpringBoot启动类的流程。一个应用启动具体都干了哪些事情。

正文:

入口类:

@SpringBootApplication
public class HelloWorldMainApplication {

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

SpringApplication实例的初始化

我们跟进去看看run方法到底做了什么事情

//调用静态类,参数对应的就是HelloWorldMainApplication.class以及main方法中的args
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);
}

我们可以发现实际上就是构造一个SpringApplication的实例,将我们的启动类作为参数传进去,运行它的run方法。接着我们查看下SpringApplication是怎么构造的。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
         // 设置资源加载器 
        this.resourceLoader = resourceLoader;
        // 资源加载类不能为 null
                Assert.notNull(primarySources, "PrimarySources must not be null");
        // 初始化加载资源类集合并去重
        // 将启动类HelloWorldMainApplication.class设置为属性存入primarySources (设置要加载的基础资源)
                this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 推断应用程序是不是web应用
        // 根据classpath 下的类,推算当前web应用类型(webFlux, servlet)
                this.webApplicationType = WebApplicationType.deduceFromClasspath();
                this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
        // 设置初始化器(Initializer) 设置应用上下文的初始化器 ApplicationContextInitializer,最终会调用这些初始化器
        // 就是去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializer
                setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 设置监听器 
        //就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener
                setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 推断出主应用入口类
                this.mainApplicationClass = deduceMainApplicationClass();
    }

推断应用类型

public enum WebApplicationType {

        /**
         * 不是web应用,不会使用内嵌的web容器来启动
         */
        NONE,

        /**
         * 基于servlet的web应用,会使用内嵌的servlet容器启动
         */
        SERVLET,

        /**
         * 基于reactive的web应用,会使用内嵌的reactive容器启动
         */
        REACTIVE;


        //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";


        /**
         * 默认的推断方式,检测classpath中是否有对应的核心类
         */
        static WebApplicationType deduceFromClasspath() {
                //使用ClassUtils.isPresent()判断 classpath中是否有servlet或reactive的核心类,是否能用类加载器进行加载
                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;
        }


        /**
         * 另一种推断方式,检测使用的上下文类型是否是servlet、reactive体系的
         */
        static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
                //使用的上下文是否是 WebApplicationContext 或其子类,WebApplicationContext 是servlet体系的基础上下文
                if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
                        return WebApplicationType.SERVLET;
                }
                //使用的上下文是否是 ReactiveWebApplicationContext 或其子类
                if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
                        return WebApplicationType.REACTIVE;
                }
                return WebApplicationType.NONE;
        }


        /**
         * 参数分别是String形式、Class形式的类名,用于判断后者是否是前者本身或者其子类
         */
        private static boolean isAssignable(String target, Class<?> type) {
                try {
                        return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
                }
                catch (Throwable ex) {
                        return false;
                }
        }

}

我们知道,项目中添加了spring-boot-starter-web 依赖,会自动添加 Tomcat 和 Spring MVC 的依赖

SpringBoot源码分析(二)启动类流程

查看这个Spring-webmvc的jar包,很明显spring-webmvc中存在DispatcherServlet这个类,也就是我们以前SpringMvc的核心Servlet,通过类加载能加载DispatcherServlet这个类,那么我们的应用类型自然就是WebApplicationType.SERVLET

设置初始化器

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//入参type是ApplicationContextInitializer.class
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();
   // 步骤1、使用Set保存name避免重复元素
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //步骤2、根据names来进行实例化
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   //步骤3、对实例进行排序
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

步骤1:读取配置文件存入Set中

在这里,我们又看到了熟悉的方法,loadFactoryNames()。通过上一篇文章,我们知道这个方法就是读取META-INF/spring.factories文件,读取相应的配置。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {
       //从类路径的META-INF/spring.factories中加载所有默认的自动配置类
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         //获取ApplicationContextInitializer.class的所有值
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }

      // Replace all lists with unmodifiable lists containing unique elements
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}

对应我们的启动类,入参实际上就是ApplicationContextInitializer.class。对应到这个方法上,其实就是会去读取配置文件中Key为org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure这个包为例,如下图key对应的value将被读取出来,放入Set集合中。

SpringBoot源码分析(二)启动类流程

SpringBoot源码分析(二)启动类流程

步骤2:实例化操作

放入Set集合中后,接着开始实例化操作。

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);
         //确认被加载的类是ApplicationContextInitializer的子类
         Assert.isAssignable(type, instanceClass);
         Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
         //反射实例化对象
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         //加入List集合中
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

这个方法首先确认被加载的类确实是org.springframework.context.ApplicationContextInitializer的子类,然后得到构造器,进行初始化,最后放入实例列表中。

ApplicationContextInitializer是一个接口。

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

   /**
    * Initialize the given application context.
    * @param applicationContext the application to configure
    */
   void initialize(C applicationContext);

}

在Spring上下文被刷新之前,最终会调用他的实现类进行初始化操作。可以先查看下www.cnblogs.com/markLogZhu/… 这篇文章的内容。比如常见的Web应用中区分不同的环境,都是通过这里去实现的。后文中在第四步Spring容器前置处理的时候我们就会用到这个initialize()方法。

设置监听器

//这里的入参type是 org.springframework.context.ApplicationListener.class
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();
   // 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;
}

@SuppressWarnings("unchecked")
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;
}

通过代码,我们可以发现,设置监听器的过程与设置初始化的过程如出一辙,只是Key变成了org.springframework.context.ApplicationListener。我们还是以spring-boot这个包举例,这是相应的Key-Value

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

这些监听器会贯穿SpringBoot整个生命周期。

推断主类

根据方法调用栈找到主类

private Class<?> deduceMainApplicationClass() {
   try {
      //获取各个调用的栈帧,一个栈帧对应一个方法调用
      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
      //遍历这些栈帧
      for (StackTraceElement stackTraceElement : stackTrace) {
         //找到方法名为main的栈帧
         if ("main".equals(stackTraceElement.getMethodName())) {
            //把该栈帧对应的java类作为主类返回
            return Class.forName(stackTraceElement.getClassName());
         }
      }
   }
   catch (ClassNotFoundException ex) {
      // Swallow and continue
   }
   return null;
}

至此,SpringApplication实例的初始化过程就结束了

SpringApplication.run方法

上面分析了一大堆,终于完成了SpringApplication的实例化。然后就可以开始调用run()方法了。

实例run()方法用于启动Spring应用,创建并刷新一个新的应用上下文。

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args main() 方法传递的参数
 * @return 一个已创建、启动的ApplicationContext
 */
public ConfigurableApplicationContext run(String... args) {
   //创建并启动计时器
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   //配置系统属性 java.awt.headless.
   configureHeadlessProperty();
   
   //第一步:获取并启动监听器
   //创建运行监听器组,先根据spring.factories中的配置创建要使用的运行监听实例,再把这些运行监听实例封装为运行监听器组
   SpringApplicationRunListeners listeners = getRunListeners(args);
   //执行运行监听器的starting()回调方法
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      //构建应用参数
      //应用参数指的是传给main()方法的参数arg 这一步会将main()方法的参数封装为ApplicationArguments
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      
      //第二步:根据SpringApplicationRunListeners 以及参数来准备环境
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      //配置spring.beaninfo.ignore属性
      configureIgnoreBeanInfo(environment);
      // 准备Banner打印器 -SpringBoot启动的时候打印在console上的内容
      Banner printedBanner = printBanner(environment);
      //第三步:创建Spring容器(创建应用上下文)
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      //第四步:Spring容器前置处理
      //(准备上下文,主要对上一步创建好的上下文进行配置、完善,这一步会加载所有的bean定义)
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      //第五步:刷新容器
      //(刷新上下文,主要调用高级容器的refresh()刷新上下文,这一步会实例化所有的单例bean,懒加载的除外)
      refreshContext(context);
      //第六步:Spring容器后置处理
      //(执行刷新后处理,这个方法是protected修饰的空方法,作为预留的扩展点可被子类重写,可以在上下文刷新之后做一些自定义的操作)
      afterRefresh(context, applicationArguments);
      //终止计时器,打印启动花费的时长
      stopWatch.stop();
      if (this.logStartupInfo) {
         // 日志:Started HelloWorldMainApplication in 3.21 seconds (JVM running for 5.216)
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      //第七步:发出结束执行的事件
      //(运行监听器的started()回调方法)
      listeners.started(context);
      //第八步:执行Runners
      //(执行Runner回调,这也是一个扩展点,可以在应用启动后,根据main()参数做一些自定义的处理)
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      // 1、执行运行监听器的failed()回调方法;2、使用异常报告器 SpringBootExceptionReporter 打印\记录异常信息
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      //执行运行监听器的 running() 回调方法
      listeners.running(context);
   }
   catch (Throwable ex) {
      //类似上面的handleRunFailure()方法,但是传入的监听器组是null,也就是不会执行运行监听器的failed()回调方法,只打印异常信息
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   //返回容器(应用上下文)
   return context;
}

上面代码中注释写了很多,下面我们挨个进行具体分析。

  • 第一步:获取并启动监听器
  • 第二步:根据SpringApplicationRunListeners以及参数来准备环境
  • 第三步:创建Spring容器
  • 第四步:Spring容器前置处理
  • 第五步:刷新容器
  • 第六步:Spring容器后置处理
  • 第七步:发出结束执行的事件
  • 第八步:执行Runners

第一步:获取并启动监听器

跟进getRunListeners()方法看看到底在搞什么鬼

private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   return new SpringApplicationRunListeners(logger,
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
         this.applicationStartup);
}

好嘛,又是这个getSpringFactoriesInstances()方法,通过上面构造SpringApplication实例的时候设置初始化器和监听器的过程,我们已经十分了解这个方法了,就是读取META-INF/spring.factories配置文件Key-Value,通过反射获取实例。

那么我们就看看这个SpringApplicationRunListener.class到底是个啥

//easy
public interface SpringApplicationRunListener {

        //在run()方法开始执行时,该方法就立即被调用,可用于在初始化早期时做一些工作
        default void starting() {
        }
        //当environment构建完成,ApplicationContext创建之前,该方法被调用
        default void environmentPrepared(ConfigurableEnvironment environment) {
        }
        //当ApplicatonContext构建完成时,该方法被调用
        default void contextPrepared(ConfigurableApplicationContext context) {
        }
        //在ApplicationContext完成加载,但没有被刷新前,该方法被调用
        default void contextLoaded(ConfigurableApplicationContext context) {
        }
        // 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
        default void started(ConfigurableApplicationContext context) {
        }
        // 在run()方法执行完成前该方法被调用
        default void running(ConfigurableApplicationContext context) {
        }
        // 当应用运行出错时该方法被调用
        default void failed(ConfigurableApplicationContext context, Throwable exception) {
        }

}

可以看到这个监听器是用于监听spring应用的启动事件,也就是监听SpirngApplication的实例run()方法的执行过程,提供了一系列的回调方法,可在run()方法执行/启动过程的特定阶段做一些额外的操作。

这个时候就有疑问了,我们之前在对SpringApplication实例初始化的时候设置的监听器是ApplicationListener,现在启动的监听器却是SpringApplicationRunListener,那么这两个监听器有什么区别呢?

这两个接口都是监听器的2个顶级接口,两者之间没有继承关系。

ApplicationListener 应用监听器、SpringApplicationRunListener 运行监听器,这两个虽然都是监听器,但区别很大。

  • 来源不同:ApplicationListener是Spring提供的,用于构建Spring事件驱动模型;SpringApplicationRunListener 是SpringBoot提供的,用于应用启动的特定阶段做一些额外。
  • 监听的对象不同:ApplicationListener是监听事件广播器发布的应用事件,SpringApplicationRunListener 是监听应用的启动过程(也就是SpringApplication的实例run方法执行过程)
  • 触发机制不同:ApplicationListener要监听到事件广播器发布的特定事件时,才会触发回调方法;而SpringApplicationRunListener 是在应用启动的特定阶段执行方法回调,应用启动到了特定阶段就会执行,跟事件发布没有必然联系。

好,解释完这个问题后,我们继续跟进看SpringApplicationRunListener 的实现类EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;
    //广播器
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        //初始化应用事件广播器
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //将SpringApplication设置的监听器全部添加到SimpleApplicationEventMulticaster这个广播器中
        //new SpringApplication() 创建 SpringApplication 实例时,只是创建了 ApplicationListener 的实例,这一步添加到事件广播器上,ApplicationListener才会生效,此能监听这个广播器发布的事件
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
    //.....
}

我们重点先看下EventPublishingRunListener 的构造方法,getSpringFactoriesInstances()方法通过反射获取实例的时候会触发EventPublishingRunListener 的构造方法。

所以针对这个类,我们重点关注两点:

  1. 构造方法中创建了广播器ApplicationEventMulticaster,将之前创建的ApplicationListener实例绑定到事件广播器上,使这些应用监听器生效。

  2. 提供7个回调方法,回调方法都是通过事件广播器发布相应的事件,对应7种事件,除去 failed() —— ApplicationFailedEvent,剩下的6个对应springboot应用启动的6个阶段。

接着我们看下是怎么添加广播器的。

public abstract class AbstractApplicationEventMulticaster
      implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    
    //广播器的父类中存放保存监听器
   private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();

   @Override
   public void addApplicationListener(ApplicationListener<?> listener) {
      //锁定监听器助手对象
      synchronized (this.defaultRetriever) {
         //如果已经注册,则显式删除已经注册的监听器对象
         //为了避免调用重复的监听器对象
         Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
         if (singletonTarget instanceof ApplicationListener) {
            //删除监听器对象
            this.defaultRetriever.applicationListeners.remove(singletonTarget);
         }
         //内部类对象。新增监听器对象
         this.defaultRetriever.applicationListeners.add(listener);
         //清空监听器助手缓存Map
         this.retrieverCache.clear();
      }
   }

   private class DefaultListenerRetriever {
      //存放应用程序事件监听器,有序、不可重复
      public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
      //存放应用程序事件监听器bean名称,有序,不可重复
      public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
      //获取应用程序的事件监听器
      public Collection<ApplicationListener<?>> getApplicationListeners() {
         //创建一个指定大小的ApplicationListener监听器List集合
         List<ApplicationListener<?>> allListeners = new ArrayList<>(
               this.applicationListeners.size() + this.applicationListenerBeans.size());
         allListeners.addAll(this.applicationListeners);
         //如果存放监听器bean name的集合不为空
         if (!this.applicationListenerBeans.isEmpty()) {
            //获取IOC容器工厂类
            BeanFactory beanFactory = getBeanFactory();
            for (String listenerBeanName : this.applicationListenerBeans) {
               try {
                  //获取指定bean name的监听器实例
                  ApplicationListener<?> listener =
                        beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                  //如果集合中不包含监听器实例则添加到集合中。
                  if (!allListeners.contains(listener)) {
                     allListeners.add(listener);
                  }
               }
               catch (NoSuchBeanDefinitionException ex) {
                  // Singleton listener instance (without backing bean definition) disappeared -
                  // probably in the middle of the destruction phase
               }
            }
         }
         AnnotationAwareOrderComparator.sort(allListeners);
         return allListeners;
      }
   }

}

这个方法是在SimpleApplicationEventMulticaster 的父类AbstractApplicationEventMulticaster中。

关键代码为this.defaultRetriever.applicationListeners.add(listener),这是一个内部类,用来保存所有的监听器。也就是在这一步,将spring.factories中的监听器传递到SimpleApplicationEventMulticaster中。也就是上面我们说的第一点。

现在我们知道EventPublishingRunListener中有一个广播器SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster广播器中又存放所有的监听器。

最终我们通过 getRunListeners() 方法得到的是SpringApplicationRunListeners监听器组,它是

SpringApplicationRunListener 的复数形式,用于封装多个运行监听器,以便对这些运行监听器进行批量调用。

通过前面的分析我们知道,不管是SpringApplicationRunListener 提供的实现类还是spring.factories中都是一个实现,也就是EventPublishingRunListener,那为什么需要SpringApplicationRunListeners 进行批量操作呢?

其实这是因为我们在开发的过程中可能需要在应用启动的特定阶段做一些自定义操作,要实现SpringApplicationRunListener 重写回调方法,这样就会存在多个SpringApplicationRunListener 了。

至此,我们分析完了第一步,获取并启动监听器。

哎,等等,好像分析到现在都是去获取监听器,没看到在哪里启动呀。

其实监听器启动就是SpringApplicationRunListener 类中的starting()方法, 我们继续跟进EventPublishingRunListener类,看下是怎么实现的。

//org/springframework/boot/context/event/EventPublishingRunListener.java
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
   //关键代码,先创建application启动事件:ApplicationStartingEvent
   this.initialMulticaster
         .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}

//org/springframework/context/event/SimpleApplicationEventMulticaster.java
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   //获取线程池,如果为空则同步处理
   Executor executor = getTaskExecutor();
   //通过事件类型ApplicatonStartingEvent获取对应的监听器
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         //异步发送事件
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         //同步发送事件
         invokeListener(listener, event);
      }
   }
}

通过上述代码,最终通过SimpleApplicationEventMulticaster的multicastEvent方法执行启动监听器。这个方法最终会调用onApplicationEvent()方法,SpringBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法,感兴趣的同学可以跟进去再研究研究。至此,我们终于分析完了获取启动监听器的步骤,是不是也没那么难呢。

第二步:构建环境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   //获取或创建环境,已现成的环境就直接使用,没有则调用构造方法new一个
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   //配置环境:上一步调用无参构造器创建了Environment实例,这一步对创建好的Environmet实例进行配置
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   //绑定配置属性源、环境
   ConfigurationPropertySources.attach(environment);
   //至此环境已配置好,出发SpringApplicationRunListener的 environmentPrepared()回调方法
   //发布环境已准备事件,这是第二次发布事件
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   //将配置好的环境绑定到SpringApplication上
   bindToSpringApplication(environment);
   //如果不是自定义的环境,则根据情况确定是否需要转换环境
   if (!this.isCustomEnvironment) {
      environment = convertEnvironment(environment);
   }
   //重新绑定属性源,环境
   ConfigurationPropertySources.attach(environment);
   return environment;
}

我们首先先看下configureEnvironment()配置环境方法。

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
   //处理ConversionService配置
   //ConversionService是Spring的一个线程安全的类型转换接口,convert(Object,Class)可以将对象转换为指定类型,关键在于是线程安全的。
   if (this.addConversionService) {
      environment.setConversionService(new ApplicationConversionService());
   }
   //配置属性源
   configurePropertySources(environment, args);
   //配置profiles属性:这一步会把 spring.profiles 激活的配置文件添加到环境中
   configureProfiles(environment, args);
}

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
   MutablePropertySources sources = environment.getPropertySources();
   //配置默认属性
   if (!CollectionUtils.isEmpty(this.defaultProperties)) {
      DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
   }
   //配置命令行属性:如果给main()方法传递了命令行参数,则添加到Environment实例中。
   if (this.addCommandLineProperties && args.length > 0) {
      String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
      if (sources.contains(name)) {
         PropertySource<?> source = sources.get(name);
         CompositePropertySource composite = new CompositePropertySource(name);
         composite.addPropertySource(
               new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
         composite.addPropertySource(source);
         sources.replace(name, composite);
      }
      else {
         sources.addFirst(new SimpleCommandLinePropertySource(args));
      }
   }
}

接着我们看下getOrCreateEnvironment()方法,前面推断应用类型的时候我们已经知道被设置成了servlet类,所以这里创建的是ApplicationServletEnvironment,他继承自StandardServletEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
   if (this.environment != null) {
      return this.environment;
   }
   switch (this.webApplicationType) {
   case SERVLET:
      return new ApplicationServletEnvironment();
   case REACTIVE:
      return new ApplicationReactiveWebEnvironment();
   default:
      return new ApplicationEnvironment();
   }
}

最后再看下listeners.environmentPrepared(bootstrapContext, environment)方法,为什么说这个是第二次发布事件,因为当我们跟进去查看的时候,会发现其实这里又调用了一遍environmentPrepared()方法。而最终会再次调用multicastEvent()方法。

这里用到了观察者模式

@Override
public void multicastEvent(ApplicationEvent event) {
   multicastEvent(event, resolveDefaultEventType(event));
}

第三步:创建容器(应用上下文)

context = createApplicationContext();

protected ConfigurableApplicationContext createApplicationContext() {
   return this.applicationContextFactory.create(this.webApplicationType);
}

可以看到这里使用了工厂设计模式。通过Web的类型判断执行ApplicationContextFactory的create方法。

static class Factory implements ApplicationContextFactory {

   @Override
   public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
      return (webApplicationType != WebApplicationType.SERVLET) ? null
            : new AnnotationConfigServletWebServerApplicationContext();
   }

}

---------------------------------------------------------------------------------
static class Factory implements ApplicationContextFactory {

   @Override
   public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
      return (webApplicationType != WebApplicationType.REACTIVE) ? null
            : new AnnotationConfigReactiveWebServerApplicationContext();
   }

}

那么我们接着来看下这个ApplicationContextFactory 是怎么进行实例化的。

private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
   try {
      for (ApplicationContextFactory candidate : SpringFactoriesLoader
            .loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
         ConfigurableApplicationContext context = candidate.create(webApplicationType);
         if (context != null) {
            return context;
         }
      }
      return new AnnotationConfigApplicationContext();
   }
   catch (Exception ex) {
      throw new IllegalStateException("Unable create a default ApplicationContext instance, "
            + "you may need a custom ApplicationContextFactory", ex);
   }
};

我们可以看到又是loadFactories()方法,这个方法我们已经很熟悉了,就是去读取META-INF/spring.factories配置文件的,而他传递的参数就是ApplicationContextFactory.class。我们去对应的配置文件中查找。他的位置就在spring-boot-2.5.13.jar包下的spring.factories文件中

# Application Context Factories
org.springframework.boot.ApplicationContextFactory=\
org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory

接着我们直接来看SERVLET类型的创建过程。因为在实例化的时候就已经设置好是SERVLET类型的了。这里返回了一个AnnotationConfigServletWebServerApplicationContext 对象,我们看这个对象的实例化都干了哪些事情。

public AnnotationConfigServletWebServerApplicationContext() {
   //创建AnnotatedBeanDefinitionReader
   this.reader = new AnnotatedBeanDefinitionReader(this);
   //创建ClassPathBeanDefinitionScanner
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

创建AnnotatedBeanDefinitionReader

//创建一个新的AnnotatedBeanDefinitionReader,如果这个registry是EnvironmentCapable的
//比如是一个ApplicationContext,那么Environment将从这个ApplicationContext继承
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
   this(registry, getOrCreateEnvironment(registry));
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   Assert.notNull(environment, "Environment must not be null");
   this.registry = registry;
   //推断ApplicationContext所需要的BeanFactory,Classloader等
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
   //注册注释 Annotation的postprocessor
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

创建ClassPathBeanDefinitionScanner类扫描器

ClassPathBeanDefinitionScanner会对指定路径下所有class进行扫描,将符合条件的转成BeanDefinition并注册到ApplicationContext中。

/**
 * 创建一个新的ClassPathBeanDefinitionScanner
 */
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, @Nullable ResourceLoader resourceLoader) {

   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   this.registry = registry;
    
   // 定义扫描过滤器
   if (useDefaultFilters) {
      registerDefaultFilters();
   }
   //设置环境
   setEnvironment(environment);
   //设置resourceLoader
   setResourceLoader(resourceLoader);
}

到此,我们了解了创建ApplicationContext的步骤,其实我们最终创建了一个AnnotationConfigServletWebServerApplicationContext

第四步:Spring容器前置处理

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   //设置容器环境,包括各种变量
   context.setEnvironment(environment);
   //执行容器(应用上下文)后置处理,主要设置一些未填充的属性
   postProcessApplicationContext(context);
   //执行容器中的ApplicationContextInitializer (包括 spring.factories和自定义的实例)
   //执行应用上下文的各个初始化器 用于对应用上下文进行初始化,这个可以当做扩展点做一些自定义的初始化操作
   applyInitializers(context);
   //发送容器已经准备好的事件,通知各监听器,至此上下文准备完毕,执行运行监听器的contextPerpared()回调方法
   listeners.contextPrepared(context);
   bootstrapContext.close(context);
   //如果允许打印启动信息
   if (this.logStartupInfo) {
      //打印应用正在启动的信息 例如:Starting HelloWorldMainApplication ...
      logStartupInfo(context.getParent() == null);
      //打印 profiles 激活的配置,示例:The following profiles are active: test
      //没有设置激活的配置则打印默认配置:No active profile set, falling back to default profiles: default
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   //注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
   //获取内置的低级容器,以对内置的低级容器进行一些配置
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //注册一些特殊的单例bean:应用参数、banner
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   //设置banner
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   //配置是否允许bean定义覆盖
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   //配置是否运行bean的懒加载,默认false,高级容器启动时就实例化所有单例
   if (this.lazyInitialization) {
      //如果允许bean的懒加载,则给内置的低级容器添加懒加载对应的后置处理器
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   //获取所有的资源,指的是this.primarySources、this.sources 2个属性指定的资源 
   //这个primarySources就是我们在SpringApplication实例初始化的时候存入的HelloWorldMainApplication.class
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   //加载所有的资源:使用BeanDefinitionLoader 加载所有的bean定义,完成bean的注册
   load(context, sources.toArray(new Object[0]));
   //执行运行监听器的contextLoaded() 回调方法
   //通知监听器,容器已准备就绪,主要是针对一些日志等监听器的响应处理。
   listeners.contextLoaded(context);
}

我们看一下applyInitializers(context)这个方法。

protected void applyInitializers(ConfigurableApplicationContext context) {
   //从SpringApplication类中的initializers集合中获取所有的实例
   for (ApplicationContextInitializer initializer : getInitializers()) {
      //循环调用ApplicationContextInitializer中的initialize方法
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
            ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
   }
}

不知道大家还记不记得在设置初始化器的时候将ApplicationContextInitializer这个实例放入了实例列表。当时我们就接触到了ApplicationContextInitializer 接口,他里面只有一个initialize()方法,现在在这里终于用到了。

第五步:刷新应用上下文

到了这一步,SpringBoot相关的处理工作已经结束,接下来的工作就交给Spring。

private void refreshContext(ConfigurableApplicationContext context) {
   if (this.registerShutdownHook) {
      //向jvm注册ShutdownHook关闭钩子,在jvm终止时自动关闭应用上下文
      shutdownHook.registerApplicationContext(context);
   }
   //刷新上下文
   refresh(context);
}

可以发现其实是调用了2个高级容器的方法。

其中refresh()方法在spring整个源码体系中举足轻重,是实现ioc和aop的关键,这里我们先不展开去讲,在接下来的文章中具体分析下refresh()方法。

第六步:Spring容器后置处理

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

可以看到这是一个模板方法设计模式,默认是空的实现,是SpringBoot的一个扩展接口,可以重写该方法自定义一些需求,比如打印一些启动结束log,或者其他的后置处理。

第七步:运行监听器的started()回调方法

@Override
public void started(ConfigurableApplicationContext context) {
   context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
   AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}

注意,这里获取的是EventPublishingRunListener监听器,执行其的started方法,并且将创建的Spring容器传入进去创建了一个ApplicationStartedEvent事件,执行ConfigurableApplicationContext 的publishEvent()方法,也就是说这里是在Spring容器中发布事件,并不是之前的SpringApplication发布事件,和前面的start不同,前面的start是直接向SpringApplication中的所有监听器发布启动事件。

第八步:执行Runners

最后一步啦

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   //获取容器中所有的ApplicationRunner的Bean实例
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   //获取容器中所有的CommandLineRunner的Bean实例
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   //对runner进行排序,支持多个runner,可以用@Order 或者实现Order接口指定这些runner的执行顺序
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         //执行ApplicationRunner的run方法。传入的args是ApplicationArguments类型
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         //执行CommandLineRunner的run方法 ,会通过args.getSourceArgs() 获取String... 类型参数
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

可以看到有两种Runner都是函数式接口,只有一个run()方法。区别是参数不同

//使用的是应用参数ApplicationArguments
@FunctionalInterface
public interface ApplicationRunner {
   void run(ApplicationArguments args) throws Exception;
}

//基于命令行,使用的Sring... 接受命令行的多个参数
@FunctionalInterface
public interface CommandLineRunner {
   void run(String... args) throws Exception;
}

对应的callRunner()方法

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args);
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
   }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args.getSourceArgs());
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
   }
}

我们可以自定义一些ApplicationRunner或者CommandLineRunner,实现对应的run方法,并注入到Spring容器中,在SpringBoot启动完成后,会执行所有的runner的run方法。

整体流程

至此,我们分析完了SpringBoot启动的整体流程。我们来通过流程图的形式总结一下整体的流程。顺便再整体过一遍整个流程。

整体

SpringBoot源码分析(二)启动类流程

上图是整体上的一个启动流程,也是我们跟着源码很容易了解到的。

然后整篇文章重点分析了

1、SpringApplication的构造方法

2、SpringApplication实例的run()方法。

接下来我们再来回顾下整体的流程。

SpringApplication的构造方法

SpringBoot源码分析(二)启动类流程

实例run()方法

SpringBoot源码分析(二)启动类流程

总结:

在本篇文章中,我们跟随源码分析了SpringBoot应用启动的整体流程。通过run()方法入口,重点分析了SpringApplication实例的初始化和SpringApplication.run()方法的细节。到这里其实SpringBoot的整体我们就分析完毕了,可以看到SpringBoot原理性的东西并不多,主要就是自动装配与启动流程,因为SpringBoot就是一个开箱即用的脚手架,其内核依然是基于Spring的。在接下来的文章中,我们来分析下本篇文章中提到的Spring的refresh()方法。

巨人的肩膀:

www.cnblogs.com/java-chen-h…

blog.csdn.net/chy_1888370…

blog.csdn.net/weixin_4212…

ApplicationContextInitializer:

www.cnblogs.com/markLogZhu/…

ApplicationEventMulticaster(监听器)

blog.csdn.net/yaomingyang…

转载自:https://juejin.cn/post/7193611148753633339
评论
请登录