专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Spring Boot启动源码分析

1 SpringApplication

SpringApplication 的作用是启动 Spring 应用。一般会做几件事情:

  • 创建一个合适的 ApplicationContext
  • 注册 CommandLinePropertySource,将程序启动时的命令行参数暴露出来,作为 Spring 的环境变量或者用于 bean 的初始化等;
  • 刷新 ApplicationContext,加载所有的单例 bean;
  • 在程序正式运行前执行 CommandLineRunner 接口;
/** SpringApplication.java **/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;  // 1.1
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();  // 1.2
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));  // 1.3
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));  // 1.4
        this.mainApplicationClass = deduceMainApplicationClass();  // 1.5
}

1.1 ResourceLoader

资源加载,比如从配置类中加载 classpath:,从文件中加载,从 url 加载等。此处为 null。

47_1.png

1.2 WebApplicationType

web 类型,有 3 种:NONE、SERVLET、REACTIVE。

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

如果依赖包中存在 org.springframework.web.reactive.DispatcherHandler 类并且不存在 org.springframework.web.servlet.DispatcherServlet,那么就是 REACTIVE 类型。

1.3 ApplicationContextInitializer

初始化 ApplicationContextInitializer 集合。从 jar 包中的 META-INF/spring.factories 文件中获取 org.springframework.context.ApplicationContextInitializer 的值,并通过反射创建实例。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));  // type = ApplicationContextInitializer.class
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

ApplicationContextInitializer 的主要作用就是在 ConfigurableApplicationContext 类型的 ApplicationContext 调用 refresh之前,允许我们对 ConfiurableApplicationContext 的实例做进一步的设置和处理。比如 ServerPortInfoApplicationContextInitializer:设置了一个回调函数,收到事件时将 local.server.port 设置到环境变量中。

public class ServerPortInfoApplicationContextInitializer implements
    ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {

   @Override
   public void initialize(ConfigurableApplicationContext applicationContext) {
    applicationContext.addApplicationListener(this);
   }

   @Override
   public void onApplicationEvent(WebServerInitializedEvent event) {
    String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
    setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
   }
}

1.4 ApplicationListener

初始化 ApplicationListener 集合。从 jar 包中的 META-INF/spring.factories 文件中获取 org.springframework.context.ApplicationListener 的值,并通过反射创建实例。

1.5 找到启动类

2 run(String… args)

/** SpringApplication.java **/
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();  // 2.1
        stopWatch.start();  // 2.1
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);  // 2.2
        listeners.starting();  // 2.2
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);  // 2.3
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);  // 2.4
            context = createApplicationContext();  // 2.5
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);  // 2.6
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);  // 2.7
            refreshContext(context);  // 2.8
            afterRefresh(context, applicationArguments);  // 2.9
            stopWatch.stop();  // 2.1
            if (this.logStartupInfo) {  // 2.1
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);  // 2.10 发布 `ApplicationStartedEvent` 事件
            callRunners(context, applicationArguments);  // 2.11
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);  // 2.12 发布 `ApplicationReadyEvent` 事件
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

2.1 StopWatch

用于计时,这里是用来计算程序启动的时间,在日志中会找到类似这样的记录:

Started Application in 1.831 seconds (JVM running for 2.433)

2.2 SpringApplicationRunListener

从 jar 包中的 META-INF/spring.factories 文件中获取 org.springframework.boot.SpringApplicationRunListener 的值,并通过反射创建实例,根据入参 args 创建 ApplicationStartingEvent 事件,并通知给监听者。

看下 EventPublishingRunListener 的部分源码:

  /** 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();
        for (ApplicationListener<?> listener : application.getListeners()) {  // 1
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    @Override
    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));  // 2
    }
  }

  /** SimpleApplicationEventMulticaster **/
  public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
  }

  public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType :   resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {  // 3
      if (executor != null) {
        executor.execute(() -> invokeListener(listener, event));  // 4
      }
      else {
        invokeListener(listener, event);
      }
    }
  }

3、SimpleApplicationEventMulticaster 类获取了所有的 ApplicationListener,这样的话当获取到 event 时,就可以通知对应的监听者。

4、根据 type 类型,通知对应的监听者,并调用监听者的回调函数。

2.3 ConfigurableEnvironment

  private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
      // Create and configure the environment
      ConfigurableEnvironment environment = getOrCreateEnvironment(); // 1
      configureEnvironment(environment, applicationArguments.getSourceArgs());
      ConfigurationPropertySources.attach(environment);  // 2
      listeners.environmentPrepared(environment);  // 3
      bindToSpringApplication(environment);
      if (!this.isCustomEnvironment) {
         environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
               deduceEnvironmentClass());
      }
      ConfigurationPropertySources.attach(environment);
      return environment;
  }

  public class StandardEnvironment extends AbstractEnvironment {

    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {  // 1
        propertySources.addLast(
                new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        propertySources.addLast(
                new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }
  }

  public static void attach(Environment environment) {  // 2
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));  // 2
        }
    }

1、创建 StandardEnvironment 对象,其中包括 MutablePropertySources propertySources 字段,用来保存 PropertySource列表。这里获取了 systemEnvironment 以及 systemProperties 变量。

2、这里获取了 configurationProperties 相关的变量,并且插入到了 MutablePropertySources 列表的最前面。

3、发布 ApplicationEnvironmentPreparedEvent 事件,通知监听器回调函数。比如 ConfigFileApplicationListener,会把配置文件中的变量加入到 applicationConfig

注意:MutablePropertySources 列表里面元素的次序很重要,如果有多个 Source 存在同样的 key,会选择列表中第一个存在该 key 的 Source。比如 ``systemEnvironment的优先级比applicationConfig高,application.yml中配置了server.port=8080,而程序启动时java -jar xxx.jar –server.port=9090`;那么实际监听端口为 9090。

看下 PropertySourcesPropertyResolver 的源码:

  protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
        if (this.propertySources != null) {
                          // 存在多个 PropertySource 时,依次遍历
            for (PropertySource<?> propertySource : this.propertySources) {
                Object value = propertySource.getProperty(key);
                                  // 一旦找到该 key,直接返回
                if (value != null) {
                    if (resolveNestedPlaceholders && value instanceof String) {
                        value = resolveNestedPlaceholders((String) value);
                    }
                    logKeyFound(key, propertySource, value);
                    return convertValueIfNecessary(value, targetValueType);
                }
            }
        }
        return null;
    }

更多信息,参考: 基于SpringBoot的Environment源码理解实现分散配置

2.4 Banner

可自定义,不展开。

2.5 ConfigurableApplicationContext

根据webApplicationType 类型创建 ConfigurableApplicationContext 实例。如果是 REACTIVE 类型,那么创建 AnnotationConfigReactiveWebServerApplicationContext实例。

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

看下 AnnotationConfigReactiveWebServerApplicationContext构造函数,

public AnnotationConfigReactiveWebServerApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

其中 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner都是用来加载注册 bean。

2.6 SpringBootExceptionReporter

创建SpringBootExceptionReporter 实例,用来分析故障并提供诊断信息。查看 FailureAnalyzers 部分源码:

FailureAnalyzers(ConfigurableApplicationContext context) {
        this(context, null);  // 1
    }

    FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
        Assert.notNull(context, "Context must not be null");
        this.classLoader = (classLoader != null) ? classLoader : context.getClassLoader();
        this.analyzers = loadFailureAnalyzers(this.classLoader);  // 2
        prepareFailureAnalyzers(this.analyzers, context);
    }

    private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
        List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);  // 3 从 `META-INF/spring.factories` 从获取 `FailureAnalyzer` 的实现类,`FailureAnalyzer` 可用来分析程序故障并提供诊断信息,比如`NoUniqueBeanDefinitionFailureAnalyzer`。
        List<FailureAnalyzer> analyzers = new ArrayList<>();
        for (String analyzerName : analyzerNames) {
            try {
                Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor();
                ReflectionUtils.makeAccessible(constructor);
                analyzers.add((FailureAnalyzer) constructor.newInstance());  // 4 创建实例。
            }
            catch (Throwable ex) {
                logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex);
            }
        }
        AnnotationAwareOrderComparator.sort(analyzers);
        return analyzers;
    }

2.7 prepareContext

  private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);  // 1 设置环境变量
        postProcessApplicationContext(context);  // 2
        applyInitializers(context);  // 3
        listeners.contextPrepared(context);  // 4
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
      // 延迟实例化 bean
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();  // 5
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));  // 5
        listeners.contextLoaded(context);  // 6
  }

3、执行 1.3 实例的 initialize 方法。

4、发布 ApplicationContextInitializedEvent 事件,通知对应的 listener 处理。

5、加载 bean。过程如下: 获取资源(比如配置类、xml、url 等);解析资源为 BeanDefinition;通过 BeanDefinitionRegistryregisterBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法进行注册。ApplicationContext 中有个 Map<String, BeanDefinition> beanDefinitionMap,其中 key 为 beanName,value 为 BeanDefinition,所谓注册,就是在这个 map 中新增元素,后续可以根据 BeanDefinition 来创建 bean 实例。

  private int load(Object source) {
        Assert.notNull(source, "Source must not be null");
        if (source instanceof Class<?>) {
            return load((Class<?>) source);
        }
        if (source instanceof Resource) {
            return load((Resource) source);
        }
        if (source instanceof Package) {
            return load((Package) source);
        }
        if (source instanceof CharSequence) {
            return load((CharSequence) source);
        }
        throw new IllegalArgumentException("Invalid source type " + source.getClass());
    }

6、发布ApplicationPreparedEvent事件,通知对应的 listener 处理。

2.8 refreshContext

  public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();  // 1

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
                          // 设置 BeanExpressionResolver,ResourceEditorRegistrar 等
            prepareBeanFactory(beanFactory);  // 2

            try {
                // Allows post-processing of the bean factory in context subclasses.
                                  // 允许 bean factory 实例化之后进行修改
                postProcessBeanFactory(beanFactory);  // 3

                // Invoke factory processors registered as beans in the context.
                                  // 实例化并调用所有注册的 BeanFactoryPostProcessor Bean
                invokeBeanFactoryPostProcessors(beanFactory);  // 4

                // Register bean processors that intercept bean creation.
                                  // 注册 BeanPostProcessor
                registerBeanPostProcessors(beanFactory);  // 5

                // Initialize message source for this context.
                                  // MessageSource:用于信息的国际化
                initMessageSource();  // 6

                // Initialize event multicaster for this context.
                                  // 初始化 ApplicationEventMulticaster,用于广播事件
                initApplicationEventMulticaster();  // 7

                // Initialize other special beans in specific context subclasses.
                onRefresh();  // 8

                // Check for listener beans and register them.
                                  // 把 listeners 添加到 applicationEventMulticaster(见 step7),用于发布事件;
                                  // 如果当前已有事件(earlyApplicationEvents != null),直接发布
                registerListeners();  // 9 

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);  // 10

                // Last step: publish corresponding event.
                finishRefresh();  // 11
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

1、prepareRefresh() :设置当前 Context 为 active 状态,校验必需的环境变量,初始化 earlyApplicationListenersearlyApplicationEvents

  protected void prepareRefresh() {
        // Switch to active.
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        // Initialize any placeholder property sources in the context environment.
        initPropertySources();

        // 可以通过 ConfigurablePropertyResolver#setRequiredProperties 设置必需的环境变量;
        // 然后在此校验必需的变量是否存在
        getEnvironment().validateRequiredProperties();

        // Store pre-refresh ApplicationListeners...
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

8、onRefresh():初始化一些特殊的 bean。以 ReactiveWebServerApplicationContext 为例,创建了 ReactiveWebServerFactoryWebServer 等对象。

  protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start reactive web server", ex);
        }
    }

    private void createWebServer() {
        ServerManager serverManager = this.serverManager;
        if (serverManager == null) {
            String webServerFactoryBeanName = getWebServerFactoryBeanName();
            ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
            boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
            this.serverManager = ServerManager.get(webServerFactory, lazyInit);
        }
        initPropertySources();
    }

2.11 ApplicationRunner & CommandLineRunner

执行 ApplicationRunner & CommandLineRunner 的 run() 方法。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

文章永久链接:https://tech.souyunku.com/25822

未经允许不得转载:搜云库技术团队 » Spring Boot启动源码分析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们