欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot(1)—启动原理之SpringApplication对象的创建

程序员文章站 2022-03-20 12:16:50
创建SpringApplication对象 SpringBoot版本为 2.1.1.RELEASE @SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] arg ......

创建springapplication对象

springboot版本为 2.1.1.release

@springbootapplication
public 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 <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
        set<string> names = new linkedhashset<>(
                springfactoriesloader.loadfactorynames(type, classloader));
        list<t> 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两个注解的类。

下一篇文章将会讲解springapplicaiton中run()方法的运行。