SpringBoot 启动流程

in TCEH Java

前言

SpringBoot 启动类要求两部分:一、@SpringBootApplication 注解;二、在 main 方法中,需使用 SpringApplication#run 方法来启动。

接下来咱们一部分,一部分去了解,SpringBoot 到底是如何启动的?

示例

package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

@SpringBootApplication 注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
// 开启自动配置
@EnableAutoConfiguration
// 开启组件扫描
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    // 排除指定的配置类
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    // 排除指定的配置类
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    // 指定扫描包路径,默认会自动递归扫描当前目录及子目录的所有类
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    // 扫描指定类其所在的包路径
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    // 指定代理方法
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}

SpringApplication 构造方法

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // 默认 NULL
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 缓存启动类对象
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 确定应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 设置默认初始化器
        // 首先,读取所有依赖包中的 spring.factories 文件,并缓存里面的所有配置信息
        // 其次,实例化属性 org.springframework.context.ApplicationContextInitializer 配置的类
        // 最后,将实例化转化成集合赋值给当前类的 initializers 属性备用
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 设置默认监听器
        // 与上面类似,除了无需再次读取配置文件
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 确定启动类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

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();
        // Use names and ensure unique to protect against duplicates
        // 扫描依赖包  spring.factories 文件,获取需要的数据
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 实例化特定类
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

loadFactoryNames() 方法

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        // 加载  spring.factories 文件
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

loadSpringFactories() 方法

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 优先从缓存中获取
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            // 由类加载器获取所有依赖包中 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 factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        // 封装属性
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            // 缓存属性
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

run() 方法

SpringApplication#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);
    }
    public ConfigurableApplicationContext run(String... args) {
        // 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        // 异常报告器集合
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 配置外设缺失模式
        configureHeadlessProperty();
        // 获取运行监听器
        // 从 spring.factories 文件中加载
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 广播 ApplicationStartingEvent 事件
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            // 打印横幅 Logo
            Banner printedBanner = printBanner(environment);
            // 创建上下文
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 准备上下文
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 刷新上下文
            refreshContext(context);
            // 暂无任何处理
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            // 广播 ApplicationStartedEvent 事件
            listeners.started(context);
            // 回调实现了 ApplicationRunner 、CommandLineRunner 接口的类
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

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

prepareEnvironment() 方法

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 创建和配置环境
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        // 广播 ApplicationEnvironmentPreparedEvent 事件
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

createApplicationContext() 方法

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

prepareContext() 方法

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 给上下文配置环境
        context.setEnvironment(environment);
        // 前置处理。包含可能配置 Bean 名称生成器、资源加载器、转化服务
        postProcessApplicationContext(context);
        // 应用初始化器
        applyInitializers(context);
        // 广播 ApplicationContextInitializedEvent 事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 单例注册 ApplicationArguments 到 BeanFactory
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            // 是否允许 BeanName 相同情况下覆盖。默认 false 抛异常
            ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            // 懒加载
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 注册启动类到 BeanFactory 
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        // 广播 ApplicationPreparedEvent 事件
        // 配置监听器到 BeanFactory 等
        listeners.contextLoaded(context);
    }

refreshContext() 方法

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                // 向 JVM 注册关闭钩子
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        // 由 AbstractApplicationContext 扫描、加载、注册 Bean 、启动 Web 容器等等一系列操作
        ((AbstractApplicationContext) applicationContext).refresh();
    }

总结

大致启动流程: