首页 > 编程语言 > 详细

Springboot 启动分析

时间:2021-09-24 19:35:03      阅读:360      评论:0      收藏:0      [点我收藏+]
启动类
1 @SpringBootApplication
2 public class Application {
3     public static void main(String[] args) {
4         SpringApplication.run(Application.class, args);
5     }
6 }

 

 
Spring 启动类主要是 Annotation (@SpringBootApplication) 和 main 方法体中的 SpringApplication.run。其中注解 @SpringBootApplication 是一个统一注解,定义了 SpringBoot 的自动扫描注入等行为,SpringApplication.run 是一个 static 方法,SpringBoot 启动的实际方法。
 
Annotation
SpringBoot 启动的第一个注解,从 @SpringBootApplication 开始,它包含了 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 等主要注解,其中 @Target、@Retention、@Documented 是元注解,@Inherited 表明子类会继承父类中被此注解修饰的注解。
 
 技术分享图片
 
@SpringBootConfiguration
此注解的官方文档说明:表示一个类提供了 Spring Boot 应用程序@Configuration 。 可以用作 Spring 标准@Configuration注释的替代方法,以便可以自动找到配置(例如在测试中)。
应用程序应该只包含一个@SpringBootConfiguration并且大多数惯用的 Spring Boot 应用程序将从@SpringBootApplication继承它。
实际上就是跟 @Configuration 的作用相同的,区别就是 @SpringBootConfiguration 允许自动寻找配置,二者都是将当前类中用 @Bean 标记的方法的实例加入 Spring 容器。
 
@EnableAutoConfiguration
此注解的官方文档说明:启用 Spring 应用程序上下文的自动配置,尝试猜测和配置您可能需要的 bean。 自动配置类通常根据您的类路径和您定义的 bean 来应用。
实际上就是开启了 SpringBoot 的自动配置功能。它还包括子注解 @AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class),这两个注解中前者的作用就是将添加该注解的所在类的 package 作为自动配置 package 进行管理,意思就是扫描包时,将从当前类的包向下扫描,这也是为什么要把启动类放到最外层的原因。而后者 @Import 就是把 AutoConfigurationImportSelector 前置类,用于扫描到的需要自动配置的注入到 ApplicationContext 中。
 
@ComponentScan
此注解的官方文档说明:配置用于Configuration类的组件扫描指令。如果没有定义特定的包,将从声明该注解的类的包开始扫描。
 
注解配置总结:
SpringBoot 启动类上的这5个注解,分别有各自不同的功能,实现自动配置注入。总结一下就是:
@SpringBootConfiguration 注解声明启动类为配置类,将其替换为 @Configuration 也可以,在启动 时将 @Bean 注解标记的方法注入容器,Bean 名称就是方法名;
@EnableAutoConfiguration 注解开启 Spring 的自动配置,其原理就是执行自动配置前会调用 AnnotationScanner 检查启动类上标记的注解是否存在 @EnableAutoConfiguration ,没有就会抛出参数异常:llegalArgumentException: No auto-configuration attributes found.
@AutoConfigurationPackage 注解标记启动类所在包为扫描的起始包;
@CompnentScan 注解从起始包向下扫描配置类;
@Import 导入的 AutoConfigurationImportSelector 类会调用 SpringFactoriesLoader 读取配置文件 spring.factories 把需要自动配置的 Bean 初始化并注入到 ApplicationContext 。实际上参与自动配置注入的注解有4个:@EnableAutoConfiguration、@AutoConfigurationPackage、@Import 和 @CompnentScan,而 @SpringBootConfiguration 对启动和自动配置并无影响
 
 
SpringBootApplication
SpringBoot 启动的引导类,以下为官方文档的说明:
技术分享图片

 

对 SpringBootApplication.run 的源码分析如下:
public ConfigurableApplicationContext run(String... args) {

    // 任务执行计时监听器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置系统java.awt.headless属性,确定是否开启headless模式(默认开启headless模式)
    configureHeadlessProperty();
    // SpringApplication run方法的监听器,通过 SpringFactoriesLoader 加载
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听器
    listeners.starting();
    try {

        // 创建 SpringBoot 默认启动参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 用于设置活动和默认配置文件以及操作基础属性源的工具,即配置启动环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 配置对不存在的BeanInfo类的重复 ClassLoader 访问
        configureIgnoreBeanInfo(environment);
        // 打印LOGO
        Banner printedBanner = printBanner(environment);
        // 创建合适的 ApplicationContext
        context = createApplicationContext();
        // SpringBoot 启动错误的回调接口,通过 SpringFactoriesLoader 加载
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 准备 Spring 上下文
        // 在这个方法中,主要完成了以下几件事:
        //  1、设置SpringBoot的环境配置(Environment)
        //  2、注册Spring Bean名称的序列化器BeanNameGenerator,并设置资源加载器ResourceLoader
        //  3、加载ApplicationContextInitializer初始化器,并进行初始化
        //  4、统一将上面的Environment、BeanNameGenerator、ResourceLoader使用默认的Bean注册器进行注册
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 注册销毁上下文的钩子
        // 刷新 Spring 上下文
        refreshContext(context);
        // 刷新后处理
        afterRefresh(context, applicationArguments);
        // 停止计时监听器
        stopWatch.stop();
        if (this.logStartupInfo) {
            // 打印计时器日志
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 上下文已刷新且应用程序已启动
        listeners.started(context);
        // 运行 ApplicationRunner 和 CommandLineRunner 的实现类
        // 二者区别在于入参不同
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 发布事件以指示应用程序已准备好上下文环境为请求提供服务
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

 

在执行 refreshContext(context); 时,实际上是刷新了 ApplicationContext 上下文资源,根据代码追踪如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备此上下文以进行刷新。
        prepareRefresh();

        // 解析所有 Spring 配置文件,
        // 将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,
        // 加载到 BeanFactory 中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 配置 beanFactory 的标准上下文特征,
        // 例如上下文的 ClassLoader、后置处理器等
        prepareBeanFactory(beanFactory);

        try {
            // 允许在上下文子类中对 BeanFactory 进行后续处理,
            // 默认实现为空,留给子类实现。
            postProcessBeanFactory(beanFactory);

            // 实例化和调用所有 BeanFactoryPostProcessor
            // BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,
            // Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册拦截 BeanPostProcessor,
            // 将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
            // BeanFactoryPostProcessor 是针对 BeanFactory 的扩展,
            // 主要用在 bean 实例化之前,读取 bean 的定义,并可以修改它。
            registerBeanPostProcessors(beanFactory);

            // 初始化此上下文的消息源。 
            initMessageSource();

            // 为此上下文初始化应用的事件广播器 ApplicationEventMulticaster
            initApplicationEventMulticaster();

            // 初始化特定上下文子类中的其他特殊bean ,默认实现为空。
            onRefresh();

            // 检查监听器bean并注册它们。 
            registerListeners();

            // 实例化所有剩余的(非懒加载)单例。 
            finishBeanFactoryInitialization(beanFactory);

            // 最后一步:发布相应的事件。 
            // 完成此上下文的刷新,主要是推送上下文刷新完毕事件(ContextRefreshedEvent )到监听器。
            finishRefresh();
        }

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

            // 销毁已创建的单例以避免闲置资源(清理不被引用的 Bean)。 
            destroyBeans();

            // 重置“active”标志。 
            cancelRefresh(ex);

            // 将异常传播到调用方。 
            throw ex;
        }

        finally {
            // 重置Spring核心中的常见内省缓存,因为我们
            // 可能不再需要单例bean的元数据了。。。
            resetCommonCaches();
        }
    }
}

 

构建 IoC 构成中最重要的4个方法:
obtainFreshBeanFactory 创建一个新的 BeanFactory、读取和解析 bean 定义。
invokeBeanFactoryPostProcessors 提供给开发者对 BeanFactory 进行扩展。
registerBeanPostProcessors 提供给开发者对 bean 进行扩展。
finishBeanFactoryInitialization 实例化剩余的所有非懒加载单例 bean。
与一般的应用程序一样,SpringBoot 的启动基本流程也是:启动事件监听、配置环境、初始化Spring上下文、注入Bean。其中作为自动配置的 AutoConfigurationImportSelector 类的主要方法 getAutoConfigurationEntry -> getAutoConfigurationImportFilters 调用 SpringFactoriesLoader.loadFactories 读取配置、初始化 Bean factory,在 SpringBootApplication.run 方法执行到 refreshContext(context); 时注入 ApplicationContext。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
    }

 

 
对 SpringFactoriesLoader.loadFactories 源码分析如下
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryType, "‘factoryType‘ must not be null");
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    // 加载配置文件 META-INF/spring.factories,获取自动配置的类路径,即加载 Resource 资源
    List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    if (logger.isTraceEnabled()) {
        logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
    }
    List<T> result = new ArrayList<>(factoryImplementationNames.size());
    for (String factoryImplementationName : factoryImplementationNames) {
        // 通过反射初始化 Bean factory
        result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    }
    AnnotationAwareOrderComparator.sort(result);
    return result;
}

 

 
 
启动流程分析图
 
技术分享图片

 

 
断点调试参考位点
技术分享图片

 



Springboot 启动分析

原文:https://www.cnblogs.com/sxdear/p/15305804.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!