JAVA语言之SpringBoot—启动原理(SpringApplication对象的创建)
从安 2019-06-05 来源 : 阅读 969 评论 0

摘要:本篇文章主要讲述JAVA语言之SpringBoot—启动原理(SpringApplication对象的创建),希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

本篇文章主要讲述JAVA语言之SpringBoot—启动原理(SpringApplication对象的创建),希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入。

JAVA语言之SpringBoot—启动原理(SpringApplication对象的创建)

创建SpringApplication对象

SpringBoot版本为 2.1.1.RELEASE

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

这是一个SpringBoot项目的启动类,在主方法内,调用了SpringApplication类的静态run( )方法,并且把启动类的字节码和主方法的参数作为函数入参。

我们以Debug方式来运行代码,可以发现SpringBoot项目在启动时会先创建SpringApplication对象。具体代码如下:

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 SpringApplication(Class... primarySources) {
        this(null, primarySources);
    }
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

可以发现,其实在创建SpringApplcation对象时就是把启动类的Class类作为入参调用了SpringApplication类的构造方法。

下面将会重点说明一下这个构造方法:

A: this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
这行代码是将启动类存储到SpringApplicationd对象的primarySources 属性中
B: this.webApplicationType = WebApplicationType.deduceFromClasspath();
这个方法是推断Web应用类型

static WebApplicationType deduceFromClasspath() {
        //存在DispatcherHander但是不存在DispatcherServlet时,推断为WebFlux类型
        
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        // 当Servlet和ConfigurableWebApplicationContext均不存在时,推断为非Web应用
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

由于我们只引入了web依赖,所以返回为Servlet

C: setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));

这个方法是用来加载Spring应用上下文初始化器的。

我们debug进入getSpringFactoriesInstances() 中:

private  Collection getSpringFactoriesInstances(Class type) {
        return getSpringFactoriesInstances(type, new Class[] {});
    }
    private  Collection getSpringFactoriesInstances(Class type,
            Class[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

其实这个方法就是通过SpringFactoriesLoader类的loadFactoryNames()方法从类路径下的 "META-INF/spring.factories" 文件中找到所有的 "ApplicationContextInitializer",然后保存到 SpringApplication的initializers属性中。

在下图中,我们可以看到通过getSpringFactoriesInstances()方法,initializers属性中保存了6个初始化器

D: setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
和上个方法类似,只不过这个方法是加载Spring应用事件监听器,但是原理是 一样的,都是利用SpringFactoriesLoader类的loadFactoryNames()方法,只不过这个初始化的对象变成了ApplicationListener

E: this.mainApplicationClass = deduceMainApplicationClass();
这个方法的作用是从多个配置类中找到含有main方法的主配置类

其实这个方法设计的并不严谨,因为在SpringApplication.run(XXX.class)方法中,这个class对象并一定要传入其主配置类,因为我们知道@SpringBootApplication 元标注 @EnableAutoConfiguration,它们的作用基本相同,换言之,这里的class 对象我们可以传入任意标注了@EnableAutoConfiguration、@Configuration两个注解的类。


本文由职坐标整理发布,学习更多的相关知识,请关注职坐标IT知识库!

本文由 @从安 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程