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

SpringBoot启动流程

程序员文章站 2022-06-29 09:39:50
1.SpringBoot项目启动方式: 1. 在IDE中启动SpringBoot主类(XXXApplication)中的main方法 2. 使用mvn spring boot:run命令启动 3. 打成jar包之后使用java jar xxx.jar运行 4. 打成war包之后放在web容器中运行 ......

1.springboot项目启动方式:

  1. 在ide中启动springboot主类(xxxapplication)中的main方法
  2. 使用mvn spring-boot:run命令启动
  3. 打成jar包之后使用java -jar xxx.jar运行
  4. 打成war包之后放在web容器中运行

这是一篇一年多前写的博客,使用的源码版本是1.5.x。当时发布在csdn,现在同步到其他平台,虽然springboot这个版本帝刷的很快,但是2.x版本的启动流程并没有怎么变化,一样可供参考。

2.springboot启动流程主要分为三步:

第一部分:springapplication初始化模块,配置一些基本的环境变量,资源,监听器,构造器;

第二部分:实现了应用具体的启动方案,包括流程的监听模块,加载配置环境模块以及创建上下文环境模块

第三部分:自动化配置模块,这个模块是实现springboot的自动配置


springboot程序的主入口就是标注了@springbootapplication注解的类,该类中有一个main方法,在main方法中调用springapplication的run()方法,这个run()方法来启动整个程序

@springbootapplication
public class crmwebapiapplication extends springbootservletinitializer {

    public static void main(string[] args) {
        springapplication.run(crmwebapiapplication.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 {

这是一个组合注解,其中标注的注解主要有以下作用

@enableautoconfiguration: 开启springboot自动配置,在程序启动时会自动加载springboot的默认配置,如果有对一些参数进行配置,则会在程序启动时或调用时进行追加或者覆盖

@springbootconfiguration: 这个注解和@configuration注解的作用一样,用来表示被标注的类是一个配置类,会将被标注的类中一个或多个被@bean注解修饰的方法添加到spring容器中,实例的名字默认是方法名

@componentscan: 包扫描注解,默认扫描主类包路径下的类


进入run()方法后的代码如下:

/**
     * static helper that can be used to run a {@link springapplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param sources the sources to load
     * @param args the application arguments (usually passed from a java main method)
     * @return the running {@link applicationcontext}
     */
    public static configurableapplicationcontext run(object[] sources, string[] args) {
        return new springapplication(sources).run(args);
    }

这里会创建一个springapplication类的实例,进入springapplication类中可以看到构造方法里调用了一个initialize(sources)方法

/**
     * create a new {@link springapplication} instance. the application context will load
     * beans from the specified sources (see {@link springapplication class-level}
     * documentation for details. the instance can be customized before calling
     * {@link #run(string...)}.
     * @param sources the bean sources
     * @see #run(object, string[])
     * @see #springapplication(resourceloader, object...)
     */
    public springapplication(object... sources) {
        initialize(sources);
    }

initialize(sources)方法源码如下:

@suppresswarnings({ "unchecked", "rawtypes" })
private void initialize(object[] sources) {
    if (sources != null && sources.length > 0) {
        //将sources设置到springapplication类的source属性中,这时的source值只有主类
        this.sources.addall(arrays.aslist(sources));
    }
    //判断是不是web程序,
    this.webenvironment = deducewebenvironment();
    //从spring.factories文件中找出key为applicationcontextinitializer的类进行实例化,然后设置到springapplciation类的initializers属性中,这个过程也是找出所有的应用程序初始化器
    setinitializers((collection) getspringfactoriesinstances( applicationcontextinitializer.class));
    //从spring.factories文件中找出key为applicationlistener的类并实例化后设置到springapplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器
    setlisteners((collection) getspringfactoriesinstances(applicationlistener.class));
    //找出main类,也就是springboot项目的主类
    this.mainapplicationclass = deducemainapplicationclass();
}

2.1 run方法完整代码

执行完初始化之后回到run()方法中,完整代码如下:

/**
* run the spring application, creating and refreshing a new
* {@link applicationcontext}.
* @param args the application arguments (usually passed from a java main method)
* @return a running {@link applicationcontext}
*/
public configurableapplicationcontext run(string... args) {
  stopwatch stopwatch = new stopwatch();
  stopwatch.start();
  configurableapplicationcontext context = null;
  failureanalyzers analyzers = null;
  configureheadlessproperty();
   //创建应用监听器
  springapplicationrunlisteners listeners = getrunlisteners(args);
  //开始监听
  listeners.starting();
  
  try {
     applicationarguments applicationarguments = new defaultapplicationarguments(args);
     //加载springboot配置环境configurableenvironment,见2.2配置configurableenvironment
     configurableenvironment environment = prepareenvironment(listeners,applicationarguments);
     //打印banner
     banner printedbanner = printbanner(environment);
     //创建应用程序上下文,见2.3 创建应用程序上下文
     context = createapplicationcontext();
     analyzers = new failureanalyzers(context);
     preparecontext(context, environment, listeners, applicationarguments,printedbanner);
     refreshcontext(context);
     afterrefresh(context, applicationarguments);
     listeners.finished(context, null);
     stopwatch.stop();
     if (this.logstartupinfo) {
      new startupinfologger(this.mainapplicationclass)
            .logstarted(getapplicationlog(), stopwatch);
     }
     return context;
   }catch (throwable ex) {
     handlerunfailure(context, listeners, analyzers, ex);
     throw new illegalstateexception(ex);
   }
}

2.2 配置configurableenvironment

加载springboot配置环境configurableenvironment流程如下:

private configurableenvironment prepareenvironment(
            springapplicationrunlisteners listeners,
            applicationarguments applicationarguments) {
        // create and configure the environment
        configurableenvironment environment = getorcreateenvironment();
        configureenvironment(environment, applicationarguments.getsourceargs());
        listeners.environmentprepared(environment);
        if (!this.webenvironment) {
            environment = new environmentconverter(getclassloader())
                    .converttostandardenvironmentifnecessary(environment);
        }
        return environment;
    }

在加载配置环境的过程中会判断是否是web容器启动,如果是容器启动会加载standardservletenvironment

private configurableenvironment getorcreateenvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        if (this.webenvironment) {
            return new standardservletenvironment();
        }
        return new standardenvironment();
    }

standardservletenvironment类的继承关系如下,standardservletenvironment

SpringBoot启动流程

propertyresolver接口是用于解析任何基础源的属性的接口,在加载完配置之后会将配置环境加入到监听器对象springapplicationrunlisteners中。

2.3 创建应用程序上下文

然后会创建应用上下文对象,具体代码如下:

protected configurableapplicationcontext createapplicationcontext() {
        class<?> contextclass = this.applicationcontextclass;
        if (contextclass == null) {
            try {
                contextclass = class.forname(this.webenvironment
                        ? default_web_context_class : default_context_class);
            }
            catch (classnotfoundexception ex) {
                throw new illegalstateexception(
                        "unable create a default applicationcontext, "
                                + "please specify an applicationcontextclass",
                        ex);
            }
        }
        return (configurableapplicationcontext) beanutils.instantiate(contextclass);
    }

方法会先显式的获取应用上下文对象,如果对象为空,再加载默认的环境配置,通过是否是webenvironment进行判断,默认选择的是annotationconfigapplicationcontext(注解上下文,通过扫秒注解来加载bean),然后通过beanutils来实例化应用上下文对象然后返回,configurableapplicationcontext类继承关系如下:

这里推荐一下我的另一篇博客,不太懂configurableapplicationcontext的可以去看一下,https://juejin.im/post/5d72055f5188256bab4c0b6d

SpringBoot启动流程

回到run()方法中,会调用preparecontext()方法将environment, listeners,applicationarguments, printedbanner等组件与上下文对象进行关联

private void preparecontext(configurableapplicationcontext context,
            configurableenvironment environment, springapplicationrunlisteners listeners,
            applicationarguments applicationarguments, banner printedbanner) {
        context.setenvironment(environment);
        postprocessapplicationcontext(context);
        applyinitializers(context);
        listeners.contextprepared(context);
        if (this.logstartupinfo) {
            logstartupinfo(context.getparent() == null);
            logstartupprofileinfo(context);
        }

        // add boot specific singleton beans
        context.getbeanfactory().registersingleton("springapplicationarguments",
                applicationarguments);
        if (printedbanner != null) {
            context.getbeanfactory().registersingleton("springbootbanner", printedbanner);
        }

        // load the sources
        set<object> sources = getsources();
        assert.notempty(sources, "sources must not be empty");
        load(context, sources.toarray(new object[sources.size()]));
        listeners.contextloaded(context);
    }

然后会调用refreshcontext()方法,实际调用org.springframework.context.support.abstractapplicationcontext.refresh()内的相关方法。这个方法里会进行redis,mybatis等的自动配置,包括spring.factories的加载,bean的实例化,benfactorypostprocessor接口的执行,beanpostprocessor接口的执行,条件注解的解析,国际化功能的初始化等。

refreshcontext()方法执行完毕之后会执行afterrefresh方法,当run()方法执行完之后spring容器也就初始化完毕了

protected void afterrefresh(configurableapplicationcontext context,
            applicationarguments args) {
        callrunners(context, args);
    }

    private void callrunners(applicationcontext context, applicationarguments args) {
        list<object> runners = new arraylist<object>();
        runners.addall(context.getbeansoftype(applicationrunner.class).values());
        runners.addall(context.getbeansoftype(commandlinerunner.class).values());
        annotationawareordercomparator.sort(runners);
        for (object runner : new linkedhashset<object>(runners)) {
            if (runner instanceof applicationrunner) {
                callrunner((applicationrunner) runner, args);
            }
            if (runner instanceof commandlinerunner) {
                callrunner((commandlinerunner) runner, args);
            }
        }
    }

    private void callrunner(applicationrunner runner, applicationarguments args) {
        try {
            (runner).run(args);
        }
        catch (exception ex) {
            throw new illegalstateexception("failed to execute applicationrunner", ex);
        }
    }

    private void callrunner(commandlinerunner runner, applicationarguments args) {
        try {
            (runner).run(args.getsourceargs());
        }
        catch (exception ex) {
            throw new illegalstateexception("failed to execute commandlinerunner", ex);
        }
    }

SpringBoot启动流程