IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot run方法执行流程分析 -> 正文阅读

[Java知识库]springboot run方法执行流程分析

目录

SpringBoot项?的main函数

1.SpringApplication()

推断应?类型deduceWebApplicationType()

?getSpringFactoriesInstances();

?2. run(args)

2.1 获取并启动监听器?

?2.2 构造应?上下?环境

prepareEnvironment()?法

2.3 初始化应?上下?

2.4 刷新应?上下?前的准备阶段

?2.5刷新应?上下?(IOC容器的初始化过程)


SpringBoot项?的main函数

@SpringBootApplication
public class SpringBootMytestApplication{

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

    }
}

?点进run?法,primarySource就是引导类SpringBootMytestApplication

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
//调用重载方法
        return run(new Class[]{primarySource}, args);
    }

进入重载run方法

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
  1. ??????? SpringApplication()构造?法的作用加载各种配置信息,初始化各种配置对象
  2. ??????? run(args)初始化容器

1.SpringApplication()

public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        //初始化资源加载器为null
        this.resourceLoader = resourceLoader;
        //断?,加载资源类不能为null
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //初始化配置类的类名信息(格式转换)将primarySources数组转换为List,最后放到LinkedHashSet集合中
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        //【1.1 推断应?类型,后?会根据类型初始化对应的环境。常?的?般都是servlet环境 】
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
//【1.2 初始化classpath下 META-INF/spring.factories中已配置的BootstrapRegistryInitializer】
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//【1.2 初始化classpath下META-INF/spring.factories中已配置的ApplicationContextInitializer】
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//【1.3 初始化classpath下所有已配置的ApplicationListener。ApplicationListener是spring的事件监听器,典型的观察者模式,实现ApplicationListener接?,通过ApplicationEvent类,可以实现对spring容器全?命周期的监听,当然也可以?定义监听事件】
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//【1.4 根据调?栈,推断出 main ?法的类名 】
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

推断应?类型deduceWebApplicationType()

/**
 * 判断 应?的类型
 * NONE: 应?程序不是web应?,也不应该?web服务器去启动
 * SERVLET: 应?程序应作为基于servlet的web应?程序运?,并应启动嵌?式servlet web(tomcat)服务器。
 * REACTIVE: 应?程序应作为 reactive web应?程序运?,并应启动嵌?式reactive web服务器。
 * @return
*/
static WebApplicationType deduceFromClasspath() {
//classpath下必须存在org.springframework.web.reactive.DispatcherHandler
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }
/**classpath环境下存在javax.servlet.Servlet或者
*org.springframework.web.context.ConfigurableWebApplicationContext
*/
            return SERVLET;
        }
    }

????????返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三 个枚举的解释如其中注释   

????????具体的判断逻辑如下:

  • WebApplicationType.REACTIVE classpath下存在org.springframework.web.reactive.DispatcherHandler
  • WebApplicationType.SERVLET classpath下存在javax.servlet.Servlet或者 org.springframework.web.context.ConfigurableWebApplicationContext
  • WebApplicationType.NONE 不满?以上条件。

?getSpringFactoriesInstances();

????????初始化classpath下 META-INF/spring.factories中已配置的类

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

/**
* 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的
??实例
* @param type
* @param parameterTypes
* @param args
* @param <T>
* @return
*/
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();

// Use names and ensure unique to protect against duplicates
 //通过指定的classLoader从 META-INF/spring.factories 的资源?件中,
 //读取 key 为 type.getName() 的 value
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建Spring??实例
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对Spring??实例排序(org.springframework.core.annotation.Order注解指定的顺序)
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

?????????loadFactoryNames() 这个?法很重要,这个?法是spring-core中提供的从METAINF/spring.factories中获取指定的类(key)的同????法

????????在这?,获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。

?2. run(args)

????????SpringBoot 启动流程最重要的部分run?法,初始化容器

//初始化容器,得到ApplicationContext对象
public ConfigurableApplicationContext run(String... args) {
        //记录程序开始运?时间
        long startTime = System.nanoTime();
        //将系统配置引导信息再次封装为上下文对象
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        //模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器、鼠标、键盘....),设置了一个java.awt.headless=true的信号,只是为了设备兼容
        this.configureHeadlessProperty();
        //1、获取当前注册的可运行的监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //监听器执行starting
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            //获取参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //2、将前期读取的数据加载成了一个环境对象,用来描述信息,构造应?上下?环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            //处理需要忽略的Bean
            this.configureIgnoreBeanInfo(environment);
            //打印banner
            Banner printedBanner = this.printBanner(environment);
            //3、初始化应?上下?,创建容器对象,根据前期配置的容器类型进行判定并创建
            context = this.createApplicationContext();
            //设置启动模式
            context.setApplicationStartup(this.applicationStartup);
            //4、刷新应?上下?(容器)前的准备阶段,对容器进行设置,参数来源于前期的设定
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            //5、刷新应?上下?(容器)
            this.refreshContext(context);
            //刷新应?上下?(容器)后处理
            this.afterRefresh(context, applicationArguments);
            //记录程序运行时间
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            //判断是否记录启动时间的日志
            if (this.logStartupInfo) {
                //创建日志对象,输出日志信息,包含启动时间
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
            //监听器执行started
            listeners.started(context, timeTakenToStartup);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            //监听器执行ready
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }

?????????在以上的代码中,启动过程中的重要步骤共分为六步

  1. 第?步:获取并启动监听器
  2. 第?步:构造应?上下?环境
  3. 第三步:初始化应?上下?
  4. 第四步:刷新应?上下?前的准备阶段
  5. 第五步:刷新应?上下?
  6. 第六步:刷新应?上下?后的扩展接??

2.1 获取并启动监听器?

获取监听器

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

?启动监听器

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        this.doWithListeners("spring.boot.application.starting", (listener) -> {
            listener.starting(bootstrapContext);
        }, (step) -> {
            if (mainApplicationClass != null) {
                step.tag("mainApplicationClass", mainApplicationClass.getName());
            }

        });
    }

?2.2 构造应?上下?环境

????????应?上下?环境包括计算机的环境,Java环境,Spring的运?环 境,Spring项?的配置(在SpringBoot中就是那个熟悉的 application.properties/yml)等等。

prepareEnvironment()?法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        //创建并配置相应的环境
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        //根据?户配置,配置 environment系统环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        // 启动相应的监听器,其中?个重要的监听器ConfigFileApplicationListener 就是加载项?配置?件的监听器。
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
            environment = environmentConverter.convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

?????????法中主要完成的?作,?先是创建并按照相应的应?类型配 置相应的环境,然后根据?户的配置,配置系统环境,然后启动监听器,并加载系 统配置?件。

2.3 初始化应?上下?

2.4 刷新应?上下?前的准备阶段

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //设置容器环境
        context.setEnvironment(environment);
        //执?容器后置处理
        this.postProcessApplicationContext(context);
        //执?容器中的 ApplicationContextInitializer 包括spring.factories和通过三种?式?定义的
        this.applyInitializers(context);
        //向各个监听器发送容器已经准备好的事件
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // Add boot specific singleton beans
        //将main函数中的args参数封装成单例Bean,注册进容器
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        //将 printedBanner 也封装成单例,注册进容器
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        context.addBeanFactoryPostProcessor(new SpringApplication.PropertySourceOrderingBeanFactoryPostProcessor(context));
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        //加载我们的启动类,将启动类注?容器
        this.load(context, sources.toArray(new Object[0]));
        //发布容器已加载事件
        listeners.contextLoaded(context);
    }

?2.5刷新应?上下?(IOC容器的初始化过程)

?到IoC容器的初始化过程,主要分下?三步:

  1. BeanDefinition的Resource定位
  2. BeanDefinition的载?
  3. 向IoC容器注册BeanDefinition
@Override
public void refresh() throws BeansException,IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
 // Prepare this context for refreshing.
 //刷新上下?环境
 prepareRefresh();
 // Tell the subclass to refresh the internal bean
factory.
 //这?是在?类中启动 refreshBeanFactory() 的地?
@Override
public void refresh() throws BeansException,
IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
 // Prepare this context for refreshing.
 //刷新上下?环境
 prepareRefresh();
 // Tell the subclass to refresh the internal bean
factory.
 //这?是在?类中启动 refreshBeanFactory() 的地?
//发布容器事件,结束Refresh过程
 finishRefresh();
 } 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();
 }
 }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:38:26  更:2022-09-30 00:40:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/26 5:35:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码