好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringBoot整个启动过程的分析

前言

前一篇分析了 springboot如何启动以及内置web容器 ,这篇我们一起看一下springboot的整个启动过程,废话不多说,正文开始。

正文

一、springboot的启动类是**application,以注解@springbootapplication注明。

?

1

2

3

4

5

6

@springbootapplication

public class cmsapplication {

  public static void main(string[] args) {

   springapplication.run(cmsapplication. class , args);

  }

}

springbootapplication注解是@configuration,@enableautoconfiguration,@componentscan三个注解的集成,分别表示springbean的配置bean,开启自动配置spring的上下文,组件扫描的路径,这也是为什么*application.java需要放在根路径的原因,这样@componentscan扫描的才是整个项目。

二、该启动类默认只有一个main方法,调用的是springapplication.run方法,下面我们来看一下springapplication这个类。

?

1

2

3

4

5

6

7

8

public static configurableapplicationcontext run(object source, string... args) {

   return run( new object[]{source}, args);

  }

...

public static configurableapplicationcontext run(object[] sources, string[] args) {

   return ( new springapplication(sources)).run(args); //sources为具体的cmsapplication.class类

  }

...

抽出其中两个直接调用的run方法,可以看出静态方法springapplication.run最终创建了一个springapplication,并运行其中run方法。

查看起构造方法:

?

1

2

3

4

5

6

7

8

9

10

public springapplication(object... sources) {

   this .bannermode = mode.console;

   this .logstartupinfo = true ;

   this .addcommandlineproperties = true ;

   this .headless = true ;

   this .registershutdownhook = true ;

   this .additionalprofiles = new hashset();

   this .initialize(sources);

  }

...

构造方法设置了基础值后调用initialize方法进行初始化,如下:

?

1

2

3

4

5

6

7

8

9

10

private void initialize(object[] sources) {

   if (sources != null && sources.length > 0 ) {

    this .sources.addall(arrays.aslist(sources));

   }

   this .webenvironment = this .deducewebenvironment();

   this .setinitializers( this .getspringfactoriesinstances(applicationcontextinitializer. class ));

   this .setlisteners( this .getspringfactoriesinstances(applicationlistener. class ));

   this .mainapplicationclass = this .deducemainapplicationclass();

  }

...

初始化方法主要做了几步:

1.将source放入springapplication的sources属性中管理,sources是一个 linkedhashset() ,这意味着我们可以同时创建多个自定义不重复的application,但是目前只有一个。

2.判断是否是web程序( javax.servlet.servlet 和 org.springframework.web.context.configurablewebapplicationcontext 都必须在类加载器中存在),并设置到 webenvironment 属性中。

3.从spring.factories中找出applicationcontextinitializer并设置到初始化器initializers。

4.从spring.factories中找出applicationlistener,并实例化后设置到springapplication的监听器listeners属性中。这个过程就是找出所有的应用程序事件监听器。

5.找出的main方法的类(这里是cmsapplication),并返回class对象。

默认情况下,initialize方法从spring.factories文件中找出的key为applicationcontextinitializer的类有:

org.springframework.boot.context.config.delegatingapplicationcontextinitializer org.springframework.boot.context.contextidapplicationcontextinitializer org.springframework.boot.context.configurationwarningsapplicationcontextinitializer org.springframework.boot.context.web.serverportinfoapplicationcontextinitializer org.springframework.boot.autoconfigure.logging.autoconfigurationreportlogginginitializer

key为applicationlistener的有:

org.springframework.boot.context.config.configfileapplicationlistener org.springframework.boot.context.config.ansioutputapplicationlistener org.springframework.boot.logging.loggingapplicationlistener org.springframework.boot.logging.classpathloggingapplicationlistener org.springframework.boot.autoconfigure.backgroundpreinitializer org.springframework.boot.context.config.delegatingapplicationlistener org.springframework.boot.builder.parentcontextcloserapplicationlistener org.springframework.boot.context.fileencodingapplicationlistener org.springframework.boot.liquibase.liquibaseservicelocatorapplicationlistener

三、springapplication构造和初始化完成后,便是运行其run方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public configurableapplicationcontext run(string... args) {

   stopwatch stopwatch = new stopwatch(); // 构造一个任务执行观察器

   stopwatch.start(); // 开始执行,记录开始时间

   configurableapplicationcontext context = null ;

   failureanalyzers analyzers = null ;

   this .configureheadlessproperty();

   // 获取springapplicationrunlisteners,内部只有一个eventpublishingrunlistener

   springapplicationrunlisteners listeners = this .getrunlisteners(args);

   // 封装成springapplicationevent事件然后广播出去给springapplication中的listeners所监听,启动监听

   listeners.starting();

   try {

    // 构造一个应用程序参数持有类

    applicationarguments applicationarguments = new defaultapplicationarguments(args);

    // 加载配置环境

    configurableenvironment environment = this .prepareenvironment(listeners, applicationarguments);

    banner printedbanner = this .printbanner(environment);

    // 创建spring容器(使用beanutils.instantiate)

    context = this .createapplicationcontext();

    // 若容器创建失败,分析输出失败原因

    new failureanalyzers(context);

    // 设置容器配置环境,监听等

    this .preparecontext(context, environment, listeners, applicationarguments, printedbanner);

    // 刷新容器

    this .refreshcontext(context);

    this .afterrefresh(context, applicationarguments);

    // 广播出applicationreadyevent事件给相应的监听器执行

    listeners.finished(context, (throwable) null );

    stopwatch.stop(); // 执行结束,记录执行时间

    if ( this .logstartupinfo) {

     ( new startupinfologger( this .mainapplicationclass)).logstarted( this .getapplicationlog(), stopwatch);

    }

    return context; // 返回spring容器

   } catch (throwable var9) {

    this .handlerunfailure(context, listeners, (failureanalyzers)analyzers, var9);

    throw new illegalstateexception(var9);

   }

  }

run方法过程分析如上,该方法几个关键步骤如下:

1.创建了应用的监听器springapplicationrunlisteners并开始监听

2.加载springboot配置环境(configurableenvironment),如果是通过web容器发布,会加载standardenvironment,其最终也是继承了configurableenvironment,类图如下 

可以看出,*environment最终都实现了propertyresolver接口,我们平时通过environment对象获取配置文件中指定key对应的value方法时,就是调用了propertyresolver接口的getproperty方法。

3.配置环境(environment)加入到监听器对象中(springapplicationrunlisteners)

4.创建spring容器:configurableapplicationcontext(应用配置上下文),我们可以看一下创建方法

?

1

2

3

4

5

6

7

8

9

10

11

protected configurableapplicationcontext createapplicationcontext() {

   class <?> contextclass = this .applicationcontextclass;

   if (contextclass == null ) {

    try {

     contextclass = class .forname( this .webenvironment ? "org.springframework.boot.context.embedded.annotationconfigembeddedwebapplicationcontext" : "org.springframework.context.annotation.annotationconfigapplicationcontext" );

    } catch (classnotfoundexception var3) {

     throw new illegalstateexception( "unable create a default applicationcontext, please specify an applicationcontextclass" , var3);

    }

   }

   return (configurableapplicationcontext)beanutils.instantiate(contextclass);

  }

方法会先获取显式设置的应用上下文(applicationcontextclass),如果不存在,再加载默认的环境配置(通过是否是web environment判断),默认选择annotationconfigapplicationcontext注解上下文(通过扫描所有注解类来加载bean),最后通过beanutils实例化上下文对象,并返回,configurableapplicationcontext类图如下

主要看其继承的两个方向:

lifecycle:生命周期类,定义了start启动、stop结束、isrunning是否运行中等生命周期空值方法 applicationcontext:应用上下文类,其主要继承了beanfactory(bean的工厂类)。

5.回到run方法内,设置容器preparecontext方法,将listeners、environment、applicationarguments、banner等重要组件与上下文对象关联

6.刷新容器, refresh() 方法,初始化方法如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public void refresh() throws beansexception, illegalstateexception {

   object var1 = this .startupshutdownmonitor;

   synchronized ( this .startupshutdownmonitor) {

    this .preparerefresh();

    configurablelistablebeanfactory beanfactory = this .obtainfreshbeanfactory();

    this .preparebeanfactory(beanfactory);

    try {

     this .postprocessbeanfactory(beanfactory);

     this .invokebeanfactorypostprocessors(beanfactory);

     this .registerbeanpostprocessors(beanfactory);

     this .initmessagesource();

     this .initapplicationeventmulticaster();

     this .onrefresh();

     this .registerlisteners();

     this .finishbeanfactoryinitialization(beanfactory);

     this .finishrefresh();

    } catch (beansexception var9) {

     if ( this .logger.iswarnenabled()) {

      this .logger.warn( "exception encountered during context initialization - cancelling refresh attempt: " + var9);

     }

     this .destroybeans();

     this .cancelrefresh(var9);

     throw var9;

    } finally {

     this .resetcommoncaches();

    }

   }

  }

refresh() 方法做了很多核心工作比如beanfactory的设置,beanfactorypostprocessor接口的执行、beanpostprocessor接口的执行、自动化配置类的解析、spring.factories的加载、bean的实例化、条件注解的解析、国际化的初始化等等。这部分内容会在之后的文章中分析。

7.广播出applicationreadyevent,执行结束返回configurableapplicationcontext。

至此,springboot启动完成,回顾整体流程,springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationcontext),并基于以上条件,在容器中开始实例化我们需要的bean。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接

原文链接:https://blog.csdn.net/u011961421/article/details/80227453

查看更多关于SpringBoot整个启动过程的分析的详细内容...

  阅读:11次