服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 深入讲解spring boot中servlet的启动过程与原理

深入讲解spring boot中servlet的启动过程与原理

2021-05-12 15:29期待华丽转身 Java教程

这篇文章主要给大家介绍了关于spring boot中servlet启动过程与原理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

本文主要介绍了关于spring boot中servlet启动过程与原理的相关内容,下面话不多说了,来一起看看详细的介绍吧

启动过程与原理:

1 spring boot 应用启动运行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
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);
  configurableenvironment environment = prepareenvironment(listeners,
   applicationarguments);
  banner printedbanner = printbanner(environment);
   //创建一个applicationcontext容器
  context = createapplicationcontext();
  analyzers = new failureanalyzers(context);
  preparecontext(context, environment, listeners, applicationarguments,
   printedbanner);
   //刷新ioc容器
  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  createapplicationcontext():创建ioc容器,如果是web应用则创建annotationconfigembeddedwebapplacation的ioc容器,如果不是,则创建annotationconfigapplication的ioc容器

?
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 static final string default_context_class = "org.springframework.context."
  + "annotation.annotationconfigapplicationcontext";
 
 /**
 * the class name of application context that will be used by default for web
 * environments.
 */
 public static final string default_web_context_class = "org.springframework."
  + "boot.context.embedded.annotationconfigembeddedwebapplicationcontext";
 
 
protected configurableapplicationcontext createapplicationcontext() {
 class<?> contextclass = this.applicationcontextclass;
 if (contextclass == null) {
  try {
          //根据应用环境,创建不同的ioc容器
  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);
 }

3    refreshcontext(context) spring boot刷新ioc容器(创建容器对象,并初始化容器,创建容器每一个组件)

?
1
2
3
4
5
6
7
8
9
10
11
private void refreshcontext(configurableapplicationcontext context) {
 refresh(context);
 if (this.registershutdownhook) {
  try {
  context.registershutdownhook();
  }
  catch (accesscontrolexception ex) {
  // not allowed in some environments.
  }
 }
 }

4 refresh(context);刷新刚才创建的ioc容器

?
1
2
3
4
protected void refresh(applicationcontext applicationcontext) {
 assert.isinstanceof(abstractapplicationcontext.class, applicationcontext);
 ((abstractapplicationcontext) applicationcontext).refresh();
 }

5 调用父类的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
29
30
31
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();
  }
 
 }
 }

6  抽象父类abstractapplicationcontext类的子类embeddedwebapplicationcontext的onrefresh方法

?
1
2
3
4
5
6
7
8
9
10
11
@override
 protected void onrefresh() {
 super.onrefresh();
 try {
  createembeddedservletcontainer();
 }
 catch (throwable ex) {
  throw new applicationcontextexception("unable to start embedded container",
   ex);
 }
 }

7  在createembeddedservletcontainer放啊发中会获取嵌入式servlet容器工厂,由容器工厂创建servlet

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void createembeddedservletcontainer() {
 embeddedservletcontainer localcontainer = this.embeddedservletcontainer;
 servletcontext localservletcontext = getservletcontext();
 if (localcontainer == null && localservletcontext == null) {
                //获取嵌入式servlet容器工厂
  embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory();
          //根据容器工厂获取对应嵌入式servlet容器
  this.embeddedservletcontainer = containerfactory
   .getembeddedservletcontainer(getselfinitializer());
 }
 else if (localservletcontext != null) {
  try {
  getselfinitializer().onstartup(localservletcontext);
  }
  catch (servletexception ex) {
  throw new applicationcontextexception("cannot initialize servlet context",
   ex);
  }
 }
 initpropertysources();
 }

8  从ioc容器中获取servlet容器工厂

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//embeddedwebapplicationcontext#getembeddedservletcontainerfactory
protected embeddedservletcontainerfactory getembeddedservletcontainerfactory() {
 // use bean names so that we don't consider the hierarchy
 string[] beannames = getbeanfactory()
 .getbeannamesfortype(embeddedservletcontainerfactory.class);
 if (beannames.length == 0) {
 throw new applicationcontextexception(
  "unable to start embeddedwebapplicationcontext due to missing "
  + "embeddedservletcontainerfactory bean.");
 }
 if (beannames.length > 1) {
 throw new applicationcontextexception(
  "unable to start embeddedwebapplicationcontext due to multiple "
  + "embeddedservletcontainerfactory beans : "
  + stringutils.arraytocommadelimitedstring(beannames));
 }
 return getbeanfactory().getbean(beannames[0],
     embeddedservletcontainerfactory.class);
}

9  使用servlet容器工厂获取嵌入式servlet容器,具体使用哪一个容器工厂看配置环境依赖

?
1
2
this.embeddedservletcontainer = containerfactory
  .getembeddedservletcontainer(getselfinitializer());

10  上述创建过程  首先启动ioc容器,接着启动嵌入式servlet容器,接着将ioc容器中剩下没有创建的对象获取出来,比如自己创建的controller

?
1
2
// instantiate all remaining (non-lazy-init) singletons.
  finishbeanfactoryinitialization(beanfactory);
?
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
protected void finishbeanfactoryinitialization(configurablelistablebeanfactory beanfactory) {
 // initialize conversion service for this context.
 if (beanfactory.containsbean(conversion_service_bean_name) &&
  beanfactory.istypematch(conversion_service_bean_name, conversionservice.class)) {
  beanfactory.setconversionservice(
   beanfactory.getbean(conversion_service_bean_name, conversionservice.class));
 }
 
 // register a default embedded value resolver if no bean post-processor
 // (such as a propertyplaceholderconfigurer bean) registered any before:
 // at this point, primarily for resolution in annotation attribute values.
 if (!beanfactory.hasembeddedvalueresolver()) {
  beanfactory.addembeddedvalueresolver(new stringvalueresolver() {
  @override
  public string resolvestringvalue(string strval) {
   return getenvironment().resolveplaceholders(strval);
  }
  });
 }
 
 // initialize loadtimeweaveraware beans early to allow for registering their transformers early.
 string[] weaverawarenames = beanfactory.getbeannamesfortype(loadtimeweaveraware.class, false, false);
 for (string weaverawarename : weaverawarenames) {
  getbean(weaverawarename);
 }
 
 // stop using the temporary classloader for type matching.
 beanfactory.settempclassloader(null);
 
 // allow for caching all bean definition metadata, not expecting further changes.
 beanfactory.freezeconfiguration();
 
 // instantiate all remaining (non-lazy-init) singletons.
 beanfactory.preinstantiatesingletons();
 }

看看 preinstantiatesingletons方法

?
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public void preinstantiatesingletons() throws beansexception {
  if (this.logger.isdebugenabled()) {
   this.logger.debug("pre-instantiating singletons in " + this);
  }
 
  list<string> beannames = new arraylist(this.beandefinitionnames);
  iterator var2 = beannames.iterator();
 
  while(true) {
   while(true) {
    string beanname;
    rootbeandefinition bd;
    do {
     do {
      do {
       if (!var2.hasnext()) {
        var2 = beannames.iterator();
 
        while(var2.hasnext()) {
         beanname = (string)var2.next();
         object singletoninstance = this.getsingleton(beanname);
         if (singletoninstance instanceof smartinitializingsingleton) {
          final smartinitializingsingleton smartsingleton = (smartinitializingsingleton)singletoninstance;
          if (system.getsecuritymanager() != null) {
           accesscontroller.doprivileged(new privilegedaction<object>() {
            public object run() {
             smartsingleton.aftersingletonsinstantiated();
             return null;
            }
           }, this.getaccesscontrolcontext());
          } else {
           smartsingleton.aftersingletonsinstantiated();
          }
         }
        }
 
        return;
       }
 
       beanname = (string)var2.next();
       bd = this.getmergedlocalbeandefinition(beanname);
      } while(bd.isabstract());
     } while(!bd.issingleton());
    } while(bd.islazyinit());
 
    if (this.isfactorybean(beanname)) {
     final factorybean<?> factory = (factorybean)this.getbean("&" + beanname);
     boolean iseagerinit;
     if (system.getsecuritymanager() != null && factory instanceof smartfactorybean) {
      iseagerinit = ((boolean)accesscontroller.doprivileged(new privilegedaction<boolean>() {
       public boolean run() {
        return ((smartfactorybean)factory).iseagerinit();
       }
      }, this.getaccesscontrolcontext())).booleanvalue();
     } else {
      iseagerinit = factory instanceof smartfactorybean && ((smartfactorybean)factory).iseagerinit();
     }
 
     if (iseagerinit) {
      this.getbean(beanname);
     }
    } else {
            //注册bean
     this.getbean(beanname);
    }
   }
  }
 }

是使用getbean方法来通过反射将所有未创建的实例创建出来

  使用嵌入式servlet容器:

     优点:   简单,便携

     缺点:   默认不支持jsp,优化定制比较复杂

使用外置servlet容器的步骤:

  1  必须创建war项目,需要剑豪web项目的目录结构

  2  嵌入式tomcat依赖scope指定provided

  3  编写springbootservletinitializer类子类,并重写configure方法

?
1
2
3
4
5
6
7
public class servletinitializer extends springbootservletinitializer {
 
 @override
 protected springapplicationbuilder configure(springapplicationbuilder application) {
  return application.sources(springboot04webjspapplication.class);
 }
}

        4  启动服务器

jar包和war包启动区别

    jar包:执行springbootapplication的run方法,启动ioc容器,然后创建嵌入式servlet容器

 war包:  先是启动servlet服务器,服务器启动springboot应用(springbootservletinitizer),然后启动ioc容器

servlet 3.0+规则

    1  服务器启动(web应用启动),会创建当前web应用里面所有jar包里面的servletcontainerlnitializer实例

     2 servletcontainerinitializer的实现放在jar包的meta-inf/services文件夹下

   3  还可以使用@handlestypes注解,在应用启动的时候加载指定的类。

外部tomcat流程以及原理

  ①  启动tomcat

  ②  根据上述描述的servlet3.0+规则,可以在spring的web模块里面找到有个文件名为javax.servlet.servletcontainerinitializer的文件,而文件的内容为org.springframework.web.springservletcontainerinitializer,用于加载springservletcontainerinitializer类

  ③看看springservletcontainerinitializer定义

?
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@handlestypes(webapplicationinitializer.class)
public class springservletcontainerinitializer implements servletcontainerinitializer {
 
 /**
  * delegate the {@code servletcontext} to any {@link webapplicationinitializer}
  * implementations present on the application classpath.
  * <p>because this class declares @{@code handlestypes(webapplicationinitializer.class)},
  * servlet 3.0+ containers will automatically scan the classpath for implementations
  * of spring's {@code webapplicationinitializer} interface and provide the set of all
  * such types to the {@code webappinitializerclasses} parameter of this method.
  * <p>if no {@code webapplicationinitializer} implementations are found on the classpath,
  * this method is effectively a no-op. an info-level log message will be issued notifying
  * the user that the {@code servletcontainerinitializer} has indeed been invoked but that
  * no {@code webapplicationinitializer} implementations were found.
  * <p>assuming that one or more {@code webapplicationinitializer} types are detected,
  * they will be instantiated (and <em>sorted</em> if the @{@link
  * org.springframework.core.annotation.order @order} annotation is present or
  * the {@link org.springframework.core.ordered ordered} interface has been
  * implemented). then the {@link webapplicationinitializer#onstartup(servletcontext)}
  * method will be invoked on each instance, delegating the {@code servletcontext} such
  * that each instance may register and configure servlets such as spring's
  * {@code dispatcherservlet}, listeners such as spring's {@code contextloaderlistener},
  * or any other servlet api componentry such as filters.
  * @param webappinitializerclasses all implementations of
  * {@link webapplicationinitializer} found on the application classpath
  * @param servletcontext the servlet context to be initialized
  * @see webapplicationinitializer#onstartup(servletcontext)
  * @see annotationawareordercomparator
  */
 @override
 public void onstartup(set<class<?>> webappinitializerclasses, servletcontext servletcontext)
   throws servletexception {
 
  list<webapplicationinitializer> initializers = new linkedlist<webapplicationinitializer>();
 
  if (webappinitializerclasses != null) {
   for (class<?> waiclass : webappinitializerclasses) {
    // be defensive: some servlet containers provide us with invalid classes,
    // no matter what @handlestypes says...
    if (!waiclass.isinterface() && !modifier.isabstract(waiclass.getmodifiers()) &&
      webapplicationinitializer.class.isassignablefrom(waiclass)) {
     try {
                //为所有的webapplicationinitializer类型创建实例,并加入集合中
      initializers.add((webapplicationinitializer) waiclass.newinstance());
     }
     catch (throwable ex) {
      throw new servletexception("failed to instantiate webapplicationinitializer class", ex);
     }
    }
   }
  }
 
  if (initializers.isempty()) {
   servletcontext.log("no spring webapplicationinitializer types detected on classpath");
   return;
  }
 
  servletcontext.log(initializers.size() + " spring webapplicationinitializers detected on classpath");
  annotationawareordercomparator.sort(initializers);
      //调用每一个webapplicationinitializer实例的onstartup方法
  for (webapplicationinitializer initializer : initializers) {
   initializer.onstartup(servletcontext);
  }
 }
}

 在上面一段长长的注释中可以看到,springservletcontainerinitializer将@handlestypes(webapplicationinitializer.class)标注的所有webapplicationinitializer这个类型的类都传入到onstartup方法的set参数中,并通过反射为这些webapplicationinitializer类型的类创建实例;

  ④  方法最后,每一个webapplicationinitilizer实现调用自己onstartup方法

  ⑤  而webapplicationinitializer有个抽象实现类springbootservletinitializer(记住我们继承了该抽象类),则会调用每一个webapplicationinitializer实例(包括springbootservletinitializer)的onstartup方法:

?
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public abstract class springbootservletinitializer implements webapplicationinitializer {
 
  //other code...
   
  @override
  public void onstartup(servletcontext servletcontext) throws servletexception {
    // logger initialization is deferred in case a ordered
    // logservletcontextinitializer is being used
    this.logger = logfactory.getlog(getclass());
    //创建ioc容器
    webapplicationcontext rootappcontext = createrootapplicationcontext(
        servletcontext);
    if (rootappcontext != null) {
      servletcontext.addlistener(new contextloaderlistener(rootappcontext) {
        @override
        public void contextinitialized(servletcontextevent event) {
          // no-op because the application context is already initialized
        }
      });
    }
    else {
      this.logger.debug("no contextloaderlistener registered, as "
          + "createrootapplicationcontext() did not "
          + "return an application context");
    }
  }
 
  protected webapplicationcontext createrootapplicationcontext(
      servletcontext servletcontext) {
    //创建spring应用构建器,并进行相关属性设置
    springapplicationbuilder builder = createspringapplicationbuilder();
    standardservletenvironment environment = new standardservletenvironment();
    environment.initpropertysources(servletcontext, null);
    builder.environment(environment);
    builder.main(getclass());
    applicationcontext parent = getexistingrootwebapplicationcontext(servletcontext);
    if (parent != null) {
      this.logger.info("root context already created (using as parent).");
      servletcontext.setattribute(
          webapplicationcontext.root_web_application_context_attribute, null);
      builder.initializers(new parentcontextapplicationcontextinitializer(parent));
    }
    builder.initializers(
        new servletcontextapplicationcontextinitializer(servletcontext));
    builder.contextclass(annotationconfigembeddedwebapplicationcontext.class);
     
    //调用configure方法,创建war类型的web项目后,由于编写springbootservletinitializer的子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法
    builder = configure(builder);
     
    //通过构建器构建了一个spring应用
    springapplication application = builder.build();
    if (application.getsources().isempty() && annotationutils
        .findannotation(getclass(), configuration.class) != null) {
      application.getsources().add(getclass());
    }
    assert.state(!application.getsources().isempty(),
        "no springapplication sources have been defined. either override the "
            + "configure method or add an @configuration annotation");
    // ensure error pages are registered
    if (this.registererrorpagefilter) {
      application.getsources().add(errorpagefilterconfiguration.class);
    }
    //启动spring应用
    return run(application);
  }
   
  //spring应用启动,创建并返回ioc容器
  protected webapplicationcontext run(springapplication application) {
    return (webapplicationcontext) application.run();
  }  
}

springbootservletinitializer实例执行onstartup方法的时候会通过createrootapplicationcontext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建ioc容器并返回,只是以war包形式的应用在创建ioc容器过程中,不再创建servlet容器了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:https://www.cnblogs.com/developerxiaofeng/p/9081689.html

延伸 · 阅读

精彩推荐