激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達(dá)式|

服務(wù)器之家 - 編程語言 - JAVA教程 - Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

2020-09-16 15:33draculav JAVA教程

這篇文章主要介紹了Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解,需要的朋友可以參考下

 standardengine[tomcat].standardhost[localhost]的啟動與standardengine不在同一個線程中,它的start:   

// start our child containers, if any
  container children[] = findchildren();
  list<future<void>> results = new arraylist<>();
  for (int i = 0; i < children.length; i++) {
   results.add(startstopexecutor.submit(new startchild(children[i])));
  }
  boolean fail = false;
  for (future<void> result : results) {
   try {
    result.get();
   } catch (exception e) {
    log.error(sm.getstring("containerbase.threadedstartfailed"), e);
    fail = true;
   }
  }
  if (fail) {
   throw new lifecycleexception(
     sm.getstring("containerbase.threadedstartfailed"));
  }
 private static class startchild implements callable<void> {
  private container child;
  public startchild(container child) {
   this.child = child;
  }
  @override
  public void call() throws lifecycleexception {
   child.start();
   return null;
  }
 }

  這個start流程中,initinternal方法是containerbase的代碼,還是那個初始化startstopexecutor的,線程名例如thread[localhost-startstop-1,5,main],這次是用來初始化host的子容器的,然后是standardhost中的startinternal方法,主要是注冊了一個errorvalue,如果現(xiàn)有的pipeline中沒有errorvalue,則反射創(chuàng)建org.apache.catalina.valves.errorreportvalve實例,并加入pipeline中,容器pipeline加入value時會發(fā)布一個container.add_valve_event事件,與engine一樣,之后進(jìn)入containerbase的startinternal,但是這次realm是null不需要啟動,然后findchildren出standardengine[tomcat]. standardhost [localhost].standardcontext[],然后同樣新開個線程new startchild,start同樣是上面的代碼,需要特別說明的是,這次before_init的事件有監(jiān)聽的了,fixcontextlistener,disablepersistsessionlistener,memoryleaktrackinglistener;fixcontextlistener監(jiān)聽的處理,會加入一個用于不做用戶身份認(rèn)證的安全檢查的value:               

context context = (context) event.getlifecycle();
    if (event.gettype().equals(lifecycle.configure_start_event)) {
     context.setconfigured(true);
    }
    // loginconfig is required to process @servletsecurity
    // annotations
    if (context.getloginconfig() == null) {
     context.setloginconfig(
       new loginconfig("none", null, null, null));
     context.getpipeline().addvalve(new nonloginauthenticator());
    }

   disablepersistsessionlistener監(jiān)聽只處理start事件,所以這里只判斷了一下發(fā)現(xiàn)不是就出去了,其實這里可以思考下,有沒有更好的辦法,讓監(jiān)聽不只是廣播方式,能不能用訂閱方式,先不細(xì)想了,接著看代碼,memoryleaktrackinglistener只監(jiān)聽了after_start事件,這步同樣什么都沒做。

  于是來到了standardcontext的initinternal,它的super.initinternal又是一個startstopexecutor,containerbase的super.initinternal就不再說了,發(fā)送j2ee.object.created消息:         

notification notification = new notification("j2ee.object.created",
     this.getobjectname(), sequencenumber.getandincrement());
   broadcaster.sendnotification(notification);

   notification是eventobject的子類,代表由mbean發(fā)出的通知,mbean server發(fā)出的通知會包含發(fā)出的mbean的引用,如果mbean注冊了監(jiān)聽,可以通過object name或引用獲取消息發(fā)出者,官方建議使用object name;sendnotification方法:

 /**
  * sends a notification.
  *
  * if an {@code executor} was specified in the constructor, it will be given one
  * task per selected listener to deliver the notification to that listener.
  *
  * @param notification the notification to send.
  */
 public void sendnotification(notification notification) {
  if (notification == null) {
   return;
  }
  boolean enabled;
  for (listenerinfo li : listenerlist) {
   try {
    enabled = li.filter == null ||
     li.filter.isnotificationenabled(notification);
   } catch (exception e) {
    if (logger.debugon()) {
     logger.debug("sendnotification", e);
    }
    continue;
   }
   if (enabled) {
    executor.execute(new sendnotifjob(notification, li));
   }
  }
 }

  發(fā)完消息就轉(zhuǎn)變狀態(tài)為初始化完成,因為監(jiān)聽器是注冊在context容器上的,于是after_init事件又觸發(fā)了那三個監(jiān)聽器,這一階段監(jiān)聽器什么都沒處理走了下過場而已;before_start同走過場;然后standardcontext的startinternal方法,發(fā)布了個j2ee.state.starting消息object name為tomcat:j2eetype=webmodule,name=//localhost/,j2eeapplication=none, j2eeserver=none;setconfigured(false)還沒有正確的配置;設(shè)置webresourceroot,webresourceroot提供整個應(yīng)用資源處理類的各種方法,內(nèi)嵌用的實現(xiàn)類是standardroot,set的過程中加了寫鎖:        

try {
    setresources(new standardroot(this));
   } catch (illegalargumentexception e) {
    log.error(sm.getstring("standardcontext.resourcesinit"), e);
    ok = false;
   }

   standardroot的屬性allresources:

private final list<list<webresourceset>> allresources =
   new arraylist<>();
 {
  allresources.add(preresources);
  allresources.add(mainresources);
  allresources.add(classresources);
  allresources.add(jarresources);
  allresources.add(postresources);
 }

  http://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/webresourceroot.html有相關(guān)說明,我就不翻譯了。

  set之后就是啟動resourcesstart,initinternal執(zhí)行的是standardroot的initinternal方法,super.initinternal中依然是那兩行代碼,register(cache, getobjectnamekeyproperties() + ",name=cache")會發(fā)送mbeanservernotification. registration_notification通知,生成objectname這里cachejmxname是tomcat:type=webresourceroot,host=localhost,context=/,name=cache;registerurlstreamhandlerfactory里面的代碼是tomcaturlstreamhandlerfactory.register()這行代碼的注釋說這是為了支持war包內(nèi)的jar資源的。之后是循環(huán)上面的allresources,init里面加入的webresourceset,但是由于全都是空的,所以等于沒執(zhí)行,就不說了,回頭再仔細(xì)看看什么情況下回不為空,還是內(nèi)嵌的就是空的。createmainresourceset主要是設(shè)置個主目錄,例如/tmp/tomcat-docbase.3031819619941848514.80,然后是各種資源該放在哪個子目錄的一些設(shè)置代碼;這次資源有一個了,所以可以有一個start了,dirresourceset的;super.initinternal()的super是abstractfileresourceset:

 //-------------------------------------------------------- lifecycle methods
 @override
 protected void initinternal() throws lifecycleexception {
  super.initinternal();
  // is this an exploded web application?
  if (getwebappmount().equals("")) {
   // look for a manifest
   file mf = file("meta-inf/manifest.mf", true);
   if (mf != null && mf.isfile()) {
    try (fileinputstream fis = new fileinputstream(mf)) {
     setmanifest(new manifest(fis));
    } catch (ioexception e) {
     log.warn(sm.getstring("dirresourceset.manifestfail", mf.getabsolutepath()), e);
    }
   }
  }
 }

  super.initinternal主要是對base目錄進(jìn)行了一些規(guī)范化處理,規(guī)范的方法主要是unixfilesystem中的canonicalize其中還使用expiringcache對路徑做了緩存,另外還有在normalize方法中對路徑中類似"\.."的部分做了處理。webappmount是web應(yīng)用發(fā)布資源的位置,必須以‘/'開頭,這里應(yīng)該是通過它來判斷不是war包部署的模式,然后由于manifest沒找到,所以方法返回初始化完成,這個資源一路狀態(tài)變化就啟動完了。

  回到standardroot,接下來是processwebinflib方法,代碼很直觀,不解釋了:

private void processwebinflib() {
  webresource[] possiblejars = listresources("/web-inf/lib", false);
  for (webresource possiblejar : possiblejars) {
   if (possiblejar.isfile() && possiblejar.getname().endswith(".jar")) {
    createwebresourceset(resourcesettype.classes_jar,
      "/web-inf/classes", possiblejar.geturl(), "/");
   }
  }
 }

  接下來也不解釋:

 // need to start the newly found resources
  for (webresourceset classresource : classresources) {
   classresource.start();
  }

  cache.enforceobjectmaxsizelimit是計算緩存限制的,詳細(xì)的可以參考http://tomcat.apache.org/tomcat-8.0-doc/config/resources.html,至此standardroot的啟動完成就只剩下改狀態(tài)了。

  回到standardcontext,因為classloader已經(jīng)有了不需要new了;接著創(chuàng)建rfc6265cookieprocessor類型的cookieprocessor實例,關(guān)于rfc6265標(biāo)準(zhǔn)參考http://www.rfc-editor.org/rfc/rfc6265.txt;character set mapper因為已經(jīng)初始化好了只判斷了下;工作目錄處理,先根據(jù)host和engine名生成路徑如:work/tomcat/localhost/root,結(jié)合前面的base創(chuàng)建目錄例如/tmp/tomcat.3726907762383543267.80/work/tomcat/localhost/root,然后初始化standardcontext中的applicationcontext類型可繼承的全局變量context構(gòu)造用參數(shù)是this(context = new applicationcontext(this)),返回new applicationcontextfacade(this);將上面的全路徑設(shè)置給servletcontext.tempdir屬性,并將這個屬性設(shè)置為只讀:

 /**
  * set an attribute as read only.
  */
 void setattributereadonly(string name) {
  if (attributes.containskey(name))
   readonlyattributes.put(name, name);
 }

  之后是對擴(kuò)展進(jìn)行驗證,這里說一下,standardcontext中不管是這里的獲取資源還是之后的讀取classloader都是加了讀鎖的:

 // validate required extensions
  boolean dependencycheck = true;
  try {
   dependencycheck = extensionvalidator.validateapplication
    (getresources(), this);
  } catch (ioexception ioe) {
   log.error(sm.getstring("standardcontext.extensionvalidationerror"), ioe);
   dependencycheck = false;
  }

  catalina.usenaming用于是否開啟命名服務(wù)支持,開啟了就會注冊namingcontextlistener監(jiān)聽器:

if (!dependencycheck) {
   // do not make application available if depency check fails
   ok = false;
  }
  // reading the "catalina.usenaming" environment variable
  string usenamingproperty = system.getproperty("catalina.usenaming");
  if ((usenamingproperty != null)
   && (usenamingproperty.equals("false"))) {
   usenaming = false;
  }
  if (ok && isusenaming()) {
   if (getnamingcontextlistener() == null) {
    namingcontextlistener ncl = new namingcontextlistener();
    ncl.setname(getnamingcontextname());
    ncl.setexceptiononfailedwrite(getjndiexceptiononfailedwrite());
    addlifecyclelistener(ncl);
    setnamingcontextlistener(ncl);
   }
  }

  classloader oldccl = bindthread()里有個threadbindinglistener,不過因為webapplicationclassloader是null,所以等于沒執(zhí)行,返回的是null,里面的邏輯還不少,命名服務(wù)也沒開contextbindings.bindthread于是也沒執(zhí)行。

  old的沒有,但是loader還是有的,到了loader的start了,主要要說的是webapploader的startinternal方法,classloader創(chuàng)建:       

 classloader = createclassloader();
   classloader.setresources(context.getresources());
   classloader.setdelegate(this.delegate);

  buildclasspath的主要功能是遍歷各個層次的classloader并將其中classpath的jar拼成一個字符串,例如
:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar...,是以':'作為分隔是因為我的開發(fā)環(huán)境是linux,在windows中應(yīng)該是';':

 while (loader != null) {
   if (!buildclasspath(classpath, loader)) {
    break;
   }
   loader = loader.getparent();
  }
  if (delegate) {
   // delegation was enabled, go back and add the webapp paths
   loader = getclassloader();
   if (loader != null) {
    buildclasspath(classpath, loader);
   }
  }

         delegate之前提過了,是會向基loader類委托的;setclasspath的最后一句:servletcontext.setattribute(globals.class_path_attr, this.classpath)。

  setpermissions方法,由于我這第一個判斷就返回了,而且看上去代碼也很直觀,我就不說了:

private void setpermissions() {
  if (!globals.is_security_enabled)
   return;
  if (context == null)
   return;
  // tell the class loader the root of the context
  servletcontext servletcontext = context.getservletcontext();
  // assigning permissions for the work directory
  file workdir =
   (file) servletcontext.getattribute(servletcontext.tempdir);
  if (workdir != null) {
   try {
    string workdirpath = workdir.getcanonicalpath();
    classloader.addpermission
     (new filepermission(workdirpath, "read,write"));
    classloader.addpermission
     (new filepermission(workdirpath + file.separator + "-",
          "read,write,delete"));
   } catch (ioexception e) {
    // ignore
   }
  }
  for (url url : context.getresources().getbaseurls()) {
   classloader.addpermission(url);
  }
 }

      ((lifecycle) classloader).start(),這個classloader是tomcatembeddedwebappclassloader走的是webappclassloaderbase中的start方法,這里因為是內(nèi)嵌的版本(我沒確認(rèn),猜測)所以也并沒有加載到東西,所以也不細(xì)說了:

 public void start() throws lifecycleexception {
  state = lifecyclestate.starting_prep;
  webresource classes = resources.getresource("/web-inf/classes");
  if (classes.isdirectory() && classes.canread()) {
   localrepositories.add(classes.geturl());
  }
  webresource[] jars = resources.listresources("/web-inf/lib");
  for (webresource jar : jars) {
   if (jar.getname().endswith(".jar") && jar.isfile() && jar.canread()) {
    localrepositories.add(jar.geturl());
    jarmodificationtimes.put(
      jar.getname(), long.valueof(jar.getlastmodified()));
   }
  }
  state = lifecyclestate.started;
 }

  然后生成objectname例如:tomcat:context=/,host=localhost,type=tomcatembeddedwebappclassloader,然后注冊mbean:getmbeanserver().registermbean( mbean, oname);webapploader的start就沒什么了,started之后就是設(shè)置了幾個屬性:

 // since the loader just started, the webapp classloader is now
    // created.
    setclassloaderproperty("clearreferencesrmitargets",
      getclearreferencesrmitargets());
    setclassloaderproperty("clearreferencesstopthreads",
      getclearreferencesstopthreads());
    setclassloaderproperty("clearreferencesstoptimerthreads",
      getclearreferencesstoptimerthreads());
    setclassloaderproperty("clearreferenceshttpclientkeepalivethread",
      getclearreferenceshttpclientkeepalivethread());

  這里的unbindthread因為前面的bind幾乎沒做什么,所以什么也沒做;接著的bindthread主要講線程與classloader做了綁定: thread.currentthread().setcontextclassloader (webapplicationclassloader),至于threadbindinglistener.bind()由于threadbindinglistener用了個空實現(xiàn),所以這里什么也沒做。

  接下來用讀鎖取到realm并start它;接下來發(fā)布configure_start事件,fixcontextlistener中執(zhí)行了context.setconfigured(true)。

  終于到了standardwrapper(standardengine[tomcat].standardhost[localhost].standardcontext[].standardwrapper[default])的start了,initinternal直接就是containerbase的初始化startstopexecutor,startinternal方法是發(fā)了個j2ee.state.starting的消息,objectname是tomcat:j2eetype=servlet, webmodule=//localhost/, name=default, j2eeapplication=none, j2eeserver=none,然后又到containerbase的startinternal,然而由于它沒有子容器了,所以這里并沒有startchild的任務(wù)產(chǎn)生;于是開始執(zhí)行它的value,先start它的pipeline,startinternal方法依然是standardpipeline的,按順序start,由于到這的時候一個都沒有,所以執(zhí)行的是basic的,standardwrappervalve的initinternal中只有一句注釋:don't register this valve in jmx;startinternal的最后是threadstart,但由于backgroundprocessordelay是-1所以并沒有啟動背景線程;setavailable(0l)設(shè)置可用,它的說明 the date and time at which this servlet will become available (in milliseconds since the epoch), or zero if the servlet is available;然后發(fā)送一個消息j2ee.state.running,objectname是tomcat:j2eetype=servlet,webmodule=//localhost/,name=default,j2eeapplication=none,j2eeserver=none;

  standardwrapper就啟動完了,回到standardcontext,start它的pipeline;與standardwrapper的pipeline不同,它之前被注冊過nonloginauthenticator,它的startinternal方法定義在authenticatorbase,方法中設(shè)置了jaspicappcontextid(例如:tomcat/localhost ),然后獲取上級容器也就是host的pipeline中的所有value,并找到其中singlesignon類型的value,明顯是用于單點登錄的,我這里沒有,于是又去找了上一級容器engine當(dāng)然還是沒有,于是就往下走了;實例化了一個standardsessionidgenerator,設(shè)置安全隨機(jī)數(shù)生成算法我這里是sha1prng,生成器類名為null,生成器provider也是null,然后就是下一個value對象standardcontextvalve的start,只不過它的start是標(biāo)準(zhǔn)的什么額外事都沒干,于是回到了standardcontext中。下面一段主要是執(zhí)行了tomcatembeddedcontext中的setmanager方法:

@override
 public void setmanager(manager manager) {
  if (manager instanceof managerbase) {
   ((managerbase) manager).setsessionidgenerator(new lazysessionidgenerator());
  }
  super.setmanager(manager);
 }

  這里判斷是true,lazysessionidgenerator整個的代碼:

class lazysessionidgenerator extends standardsessionidgenerator {

@override
 protected void startinternal() throws lifecycleexception {
  setstate(lifecyclestate.starting);
 }
}

  tomcatembeddedcontext的super.setmanager(manager)的super是standardcontext,在寫鎖中執(zhí)行的,spring中多數(shù)的set都是交換的方式,先set個old保存下來,然后判斷新值和old是否相同,不相同用新的并將新值綁定容器,相同直接返回;getservletcontext().setattribute(globals.resources_attr, getresources())沒什么好解釋的;setnamingresources(new namingresourcesimpl());然后init這個namingresources,namingresourcesimpl的initinternal,在設(shè)置當(dāng)前已知命名資源前設(shè)置resourcerequireexplicitregistration用于避免時序問題,重復(fù)注冊是正常的,后面一段我不想解釋:

 for (contextresource cr : resources.values()) {
   try {
    mbeanutils.creatembean(cr);
   } catch (exception e) {
    log.warn(sm.getstring(
      "namingresources.mbeancreatefail", cr.getname()), e);
   }
  }
  for (contextenvironment ce : envs.values()) {
   try {
    mbeanutils.creatembean(ce);
   } catch (exception e) {
    log.warn(sm.getstring(
      "namingresources.mbeancreatefail", ce.getname()), e);
   }
  }
  for (contextresourcelink crl : resourcelinks.values()) {
   try {
    mbeanutils.creatembean(crl);
   } catch (exception e) {
    log.warn(sm.getstring(
      "namingresources.mbeancreatefail", crl.getname()), e);
   }
  }

        init之后是start,start中只發(fā)布了個configure_start事件。

  setinstancemanager(new defaultinstancemanager(context, injectionmap, this, this.getclass().getclassloader())),instancemanager主要是用于創(chuàng)建和回收實例,然后綁定:         

 getservletcontext().setattribute(
      instancemanager.class.getname(), getinstancemanager());
    instancemanagerbindings.bind(getloader().getclassloader(), getinstancemanager());

  還有:            

 getservletcontext().setattribute(
      jarscanner.class.getname(), getjarscanner());

  合并參數(shù)mergeparameters由于我這里是空的,所以什么也沒做;然后遍歷initializers并onstartup:

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

  先是進(jìn)入到tomcatstarter的onstartup,這里又是:     

 for (servletcontextinitializer initializer : this.initializers) {
    initializer.onstartup(servletcontext);
   }

  先是執(zhí)行:   

private org.springframework.boot.web.servlet.servletcontextinitializer getselfinitializer() {
  return new servletcontextinitializer() {
   @override
   public void onstartup(servletcontext servletcontext) throws servletexception {
    selfinitialize(servletcontext);
   }
  };
 }

  embeddedwebapplicationcontext中的selfinitialize ,prepareembeddedwebapplicationcontext正常情況下先打一條日志initializing spring embedded webapplicationcontext然后servletcontext.setattribute(webapplicationcontext.root_web_application_context_attribute, this)然后將this綁定servletcontext,如果啟動info級別日志,會打印類似這樣的日志:root webapplicationcontext: initialization completed in 3150193 ms;然后new existingwebapplicationscopes,這玩意的注釋說它允許與非嵌入式相同的方式注冊作用域到applicationcontextinitializer,先執(zhí)行了一個靜態(tài)代碼塊:   

 static {
   set<string> scopes = new linkedhashset<string>();
   scopes.add(webapplicationcontext.scope_request);//request
   scopes.add(webapplicationcontext.scope_session);//session
   scopes.add(webapplicationcontext.scope_global_session);//global session
   scopes = collections.unmodifiableset(scopes);
  }

  但是似乎在我這add白做了,因為構(gòu)造函數(shù)中從bean工廠并沒取到scope實例:     

this.beanfactory = beanfactory;
   for (string scopename : scopes) {
    scope scope = beanfactory.getregisteredscope(scopename);
    if (scope != null) {
     this.scopes.put(scopename, scope);
    }
   }

   真正注冊作用域是在下一句webapplicationcontextutils.registerwebapplicationscopes(beanfactory, getservletcontext()):  

 beanfactory.registerscope(webapplicationcontext.scope_request, new requestscope());
  beanfactory.registerscope(webapplicationcontext.scope_session, new sessionscope(false));
  beanfactory.registerscope(webapplicationcontext.scope_global_session, new sessionscope(true));
  if (sc != null) {
   servletcontextscope appscope = new servletcontextscope(sc);
   beanfactory.registerscope(webapplicationcontext.scope_application, appscope);
   // register as servletcontext attribute, for contextcleanuplistener to detect it.
   sc.setattribute(servletcontextscope.class.getname(), appscope);
  }
  beanfactory.registerresolvabledependency(servletrequest.class, new requestobjectfactory());
  beanfactory.registerresolvabledependency(servletresponse.class, new responseobjectfactory());
  beanfactory.registerresolvabledependency(httpsession.class, new sessionobjectfactory());
  beanfactory.registerresolvabledependency(webrequest.class, new webrequestobjectfactory());
  if (jsfpresent) {
   facesdependencyregistrar.registerfacesdependencies(beanfactory);
  }

  registerresolvabledependency將類型與對應(yīng)的裝配對象注冊進(jìn)bean工廠。existingscopes.restore里的代碼:      

public void restore() {
   for (map.entry<string, scope> entry : this.scopes.entryset()) {
    if (logger.isinfoenabled()) {
     logger.info("restoring user defined scope " + entry.getkey());
    }
    this.beanfactory.registerscope(entry.getkey(), entry.getvalue());
   }
  }

  webapplicationcontextutils.registerenvironmentbeans(beanfactory, getservletcontext())把相應(yīng)的變量key與值注冊給bean工廠,如servletcontext、contextparameters和contextattributes;從bean工廠中獲取所有org.springframework.boot.web.servlet.servletcontextinitializer類型的bean,如filterregistrationbean和dispatcherservletregistration然后add給servletcontextinitializerbeans實例的initializers;addadaptablebeans方法先從bean工廠中獲取javax.servlet.multipartconfigelement類型的對象,然而javax.servlet.servlet沒在bean工廠里找到,所以add什么也沒做;javax.servlet.filter找到characterencodingfilter、hiddenhttpmethodfilter、httpputformcontentfilter、requestcontextfilter;servletlistenerregistrationbean.getsupportedtypes()取的是servletlistenerregistrationbean的supported_types,不過全都沒找到,所以什么也沒做:

 static {
  set<class<?>> types = new hashset<class<?>>();
  types.add(servletcontextattributelistener.class);
  types.add(servletrequestlistener.class);
  types.add(servletrequestattributelistener.class);
  types.add(httpsessionattributelistener.class);
  types.add(httpsessionlistener.class);
  types.add(servletcontextlistener.class);
  supported_types = collections.unmodifiableset(types);
 }

  然后是對找到的進(jìn)行排序:     

list<servletcontextinitializer> sortedinitializers = new arraylist<servletcontextinitializer>();
  for (map.entry<?, list<servletcontextinitializer>> entry : this.initializers
    .entryset()) {
   annotationawareordercomparator.sort(entry.getvalue());
   sortedinitializers.addall(entry.getvalue());
  }
  this.sortedlist = collections.unmodifiablelist(sortedinitializers);
 public static void sort(object[] array) {
  if (array.length > 1) {
   arrays.sort(array, instance);
  }
 }
 private int docompare(object o1, object o2, ordersourceprovider sourceprovider) {
  boolean p1 = (o1 instanceof priorityordered);
  boolean p2 = (o2 instanceof priorityordered);
  if (p1 && !p2) {
   return -1;
  }
  else if (p2 && !p1) {
   return 1;
  }
  // direct evaluation instead of integer.compareto to avoid unnecessary object creation.
  int i1 = getorder(o1, sourceprovider);
  int i2 = getorder(o2, sourceprovider);
  return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
 }

  然后對這些初始化器進(jìn)行beans.onstartup(servletcontext);filterregistrationbean執(zhí)行的abstractfilterregistrationbean的,主要執(zhí)行了這兩句:

        filterregistration.dynamic added = servletcontext.addfilter(name, filter);
                ...
        configure(added);
  name:characterencodingfilter,filter:orderedcharacterencodingfilter,它的配置中這里設(shè)定了過濾器轉(zhuǎn)發(fā)模式有forward、include、request、async,攔截路徑:"/*";然后是hiddenhttpmethodfilter和orderedhiddenhttpmethodfilter,httpputformcontentfilter和orderedhttpputformcontentfilter,requestcontextfilter和orderedrequestcontextfilter,cipherfilter和cipherfilter(我這自定義的)。servletregistrationbean的:dispatcherservlet和dispatcherservlet,asyncsupported是true,url映射是‘/',設(shè)置standardwrapper的loadonstartup、 multipartconfigelement。

  到了下一個初始化器sessionconfiguringinitializer:      

 public void onstartup(servletcontext servletcontext) throws servletexception {
   if (this.session.gettrackingmodes() != null) {
    servletcontext.setsessiontrackingmodes(this.session.gettrackingmodes());
   }
   configuresessioncookie(servletcontext.getsessioncookieconfig());
  }

  將session中的cookie信息補(bǔ)充進(jìn)applicationsessioncookieconfig的實例中,例如:           

 config.setname(cookie.getname());
    config.setdomain(cookie.getdomain());
    config.setpath(cookie.getpath());
    config.setcomment(cookie.getcomment());
    config.sethttponly(cookie.gethttponly());
    config.setsecure(cookie.getsecure());
    config.setmaxage(cookie.getmaxage());

  實際中我這里一個都沒執(zhí)行,因為我這的session中cookie信息都是null。

  下一個初始化器initparameterconfiguringservletcontextinitializer由于參數(shù)沒有,所以進(jìn)去就出來了。

  回到listenerstart,listenerstart:org.apache.tomcat.websocket.server.wscontextlistener,用前面的defaultinstancemanager的newinstance創(chuàng)建,然后加到lifecyclelisteners中,然后傳給applicationlifecyclelistenersobjects,然后是newservletcontextlistenerallowed=false:當(dāng)listener發(fā)生調(diào)用后不允許添加,發(fā)布beforecontextinitialized事件,然后wscontextlistener的contextinitialized:    

 servletcontext sc = sce.getservletcontext();
  if(sc.getattribute("javax.websocket.server.servercontainer") == null) {
   wssci.init(sce.getservletcontext(), false);
  }

  init中先是初始化wsservercontainer:  

 static {
  get_bytes = "get ".getbytes(standardcharsets.iso_8859_1);
  root_uri_bytes = "/".getbytes(standardcharsets.iso_8859_1);
  http_version_bytes = " http/1.1\r\n".getbytes(standardcharsets.iso_8859_1);
 }
 static {
  authenticated_http_session_closed = new closereason(closecodes.violated_policy, "this connection was established under an authenticated http session that has ended.");
 }
 wsservercontainer(servletcontext servletcontext) {
  this.enforcenoaddafterhandshake = constants.strict_spec_compliance; //boolean.getboolean("org.apache.tomcat.websocket.strict_spec_compliance")
  this.addallowed = true;
  this.authenticatedsessions = new concurrenthashmap();
  this.endpointsregistered = false;
  this.servletcontext = servletcontext;
   //我這里添加了org.apache.tomcat.websocket.server和本地語言en_us(我代碼是在英文版ubuntu上跑的)
  this.setinstancemanager((instancemanager)servletcontext.getattribute(instancemanager.class.getname())); 
  string value = servletcontext.getinitparameter("org.apache.tomcat.websocket.binarybuffersize");
  if(value != null) {
   this.setdefaultmaxbinarymessagebuffersize(integer.parseint(value));
  }
  value = servletcontext.getinitparameter("org.apache.tomcat.websocket.textbuffersize");
  if(value != null) {
   this.setdefaultmaxtextmessagebuffersize(integer.parseint(value));
  }
    //java websocket 規(guī)范 1.0 并不允許第一個服務(wù)端點開始 websocket 握手之后進(jìn)行程序性部署。默認(rèn)情況下,tomcat 繼續(xù)允許額外的程序性部署。
  value = servletcontext.getinitparameter("org.apache.tomcat.websocket.noaddafterhandshake");
  if(value != null) {
   this.setenforcenoaddafterhandshake(boolean.parseboolean(value));
  }
  dynamic fr = servletcontext.addfilter("tomcat websocket (jsr356) filter", new wsfilter());
  fr.setasyncsupported(true);
  enumset types = enumset.of(dispatchertype.request, dispatchertype.forward);
  fr.addmappingforurlpatterns(types, true, new string[]{"/*"});
 }

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

  init創(chuàng)建了 wsservercontainer之后,將它設(shè)置給servletcontext的javax.websocket.server.servercontainer屬性,然后servletcontext.addlistener(new wssessionlistener(sc))加進(jìn)前面的applicationlifecyclelistenersobjects中,init結(jié)束,回到standardcontext發(fā)布aftercontextinitialized事件,我這到這里listenerstart結(jié)束。

  checkconstraintsforuncoveredmethods(findconstraints())因為我這里find出來的并沒有,所以pass;start standardmanager startinternal先是super(managerbase),一進(jìn)方法先是將兩個雙端隊列sessioncreationtiming和sessionexpirationtiming根據(jù)常量timing_stats_cache_size用null填滿,設(shè)置jvmroute(jvmroute用于區(qū)分多tomcat節(jié)點,根據(jù)jvmroute的值來確定當(dāng)前會話屬于哪個節(jié)點 ),從engine上取得,之前設(shè)置過,getengine: 

 public engine getengine() {
  engine e = null;
  for (container c = getcontext(); e == null && c != null ; c = c.getparent()) {
   if (c instanceof engine) {
    e = (engine)c;
   }
  }
  return e;
 }

  set給sessionidgenerator,將之前初始化過的一些sessionidgenerator值set給新new的sessionidgeneratorbase,然后start之前的sessionidgenerator,這個start沒做什么特別的,于是回到standardmanager,加載文件(例:/tmp/tomcat.7550276477249965168.80/work/tomcat/localhost/root/sessions.ser),用于session持久化的,這時候找不到的。

  filterstart對filterconfigs同步鎖,filterconfigs.put(name, filterconfig):

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

  loadonstartup(findchildren()),其實都一起start過了就不用了:

Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解

   該啟動standardcontext的后天線程了super.threadstart(),當(dāng)然因為backgroundprocessordelay所以也沒啟,unbindthread說是解綁,其實只是把classloader還原了,別的沒做什么,對應(yīng)著之前的bind。

  設(shè)置standardcontext的starttime=system.currenttimemillis(),發(fā)j2ee.state.running的通知,objectname是tomcat:j2eeapplication=none, j2eeserver=none, j2eetype=webmodule, name=//localhost/;getresources().gc()因為webresources引用了一些jar,有些平臺可能會對jar加鎖,這里先清理,但實際上這里的實現(xiàn)是空的。

   disablepersistsessionlistener由于并沒有配置session持久化,所以會觸發(fā)這個監(jiān)聽器,實際只執(zhí)行了((standardmanager) manager).setpathname(null)。memoryleaktrackinglistener只走了個過場。

   發(fā)布after_start事件,這回終于執(zhí)行了memoryleaktrackinglistener:      

 if (event.gettype().equals(lifecycle.after_start_event)) {
    if (event.getsource() instanceof context) {
     context context = ((context) event.getsource());
     childclassloaders.put(context.getloader().getclassloader(),
       context.getservletcontext().getcontextpath());
    }
   }

  子容器就啟動完成了。

咱最近用的github:https://github.com/saaavsaaa

以上所述是小編給大家介紹的spring boot啟動過程(六)之內(nèi)嵌tomcat中standardhost、standardcontext和standardwrapper的啟動教程詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!

原文鏈接:http://www.cnblogs.com/saaav/p/6541750.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲综合精品成人 | 99欧美精品| 国产成人精品一区二区视频免费 | 亚洲视频在线网 | 一区二区三区四区高清视频 | 中文字幕精品亚洲 | 一级做a爱性色毛片免费1 | 亚洲最新色 | 在线免费黄色网 | 免费毛片视频 | 精品欧美一区二区精品久久小说 | 精品国产看高清国产毛片 | 国外成人在线视频 | 欧美大胆xxxx肉体摄影 | 黄色免费av网站 | 91精品国产乱码久久久久 | 免费观看的毛片手机视频 | 欧美成人高清视频 | 国产精品欧美久久久久一区二区 | aa级黄色片 | 久久久久久久久久91 | 日本欧美中文字幕 | 久久福利小视频 | 中文字幕线观看 | 巨根插入| 嫩草影院在线观看网站成人 | 免费国产自久久久久三四区久久 | 91久久国产露脸精品国产护士 | 一区二区国产在线 | 成人福利视频在线 | 精品成人av一区二区在线播放 | 免费的性生活视频 | 一级做a爰片性色毛片2021 | 欧美一级高清片_欧美高清aa | 免费在线观看毛片视频 | 国产午夜精品一区二区三区视频 | 国产精选电影免费在线观看网站 | 中文字幕xxx | 久久视讯 | 午夜国产在线观看 | 日日爱夜夜操 |