国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

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

這篇文章主要介紹了Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

之前在spring boot啟動過程(二)提到過createembeddedservletcontainer創建了內嵌的servlet容器,我用的是默認的tomcat。

private void createembeddedservletcontainer() {
 embeddedservletcontainer localcontainer = this.embeddedservletcontainer;
 servletcontext localservletcontext = getservletcontext();
 if (localcontainer == null && localservletcontext == null) {
  embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory();
  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();
 }

  getembeddedservletcontainerfactory方法中調用了serverproperties,從serverproperties的實例方法customize可以看出springboot支持三種內嵌容器的定制化配置:tomcat、jetty、undertow。

  這里直接說tomcatembeddedservletcontainerfactory的getembeddedservletcontainer方法了,原因在前面那篇里說過了。不過首先是getselfinitializer方法先執行的:

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

  將初始化的servletcontextinitializer傳給了getembeddedservletcontainer方法。進入了getembeddedservletcontainer方法直接就是實例化了一個tomcat:

 tomcat tomcat = new tomcat();

   然后生成一個臨時目錄,并tomcat.setbasedir,setbasedir方法的注釋說tomcat需要一個目錄用于臨時文件并且它應該是第一個被調用的方法;如果方法沒有被調用會使用默認的幾個位置system properties - catalina.base, catalina.home - $pwd/tomcat.$port,另外/tmp從安全角度來說不建議。

  接著:

 connector connector = new connector(this.protocol);

  創建Connector過程中,靜態代碼塊:單獨抽出來寫了。RECYCLE_FACADES屬性可以通過啟動參數JAVA_OPTS來配置: -Dorg.apache.catalina.connector.RECYCLE_FACADES=,默認是false,配置成true可以提高安全性但同時性能會有些損耗,參考:http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html和http://bztax.gov.cn/docs/security-howto.html。其他屬性不細說了,Connector構造的邏輯主要是在NIO和APR選擇中選擇一個協議,我的是org.apache.coyote.http11.Http11NioProtocol,然后反射創建實例并強轉為ProtocolHandler。關于apr,似乎是更native,性能據說更好,但我沒測,相關文檔可參考:http://tomcat.apache.org/tomcat-8.5-doc/apr.html。這里簡單提一下coyote,它的主要作用是將socket接受的信息封裝為request和response并提供給上Servlet容器,進行上下層之間的溝通,文檔我沒找到比較新的:http://tomcat.apache.org/tomcat-4.1-doc/config/coyote.html。STRICT_SERVLET_COMPLIANCE也是啟動參數控制,默認是false,配置項是org.apache.catalina.STRICT_SERVLET_COMPLIANCE,默認情況下會設置URIEncoding = "UTF-8"和URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH),相關詳細介紹可參考:http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html。Connector的創建過程比較關鍵,容我單獨寫一篇吧。

  connector實例創建好了之后tomcat.getservice().addconnector(connector),getservice的getserver中new了一個standardserver,standardserver的初始化主要是創建了globalnamingresources(globalnamingresources主要用于管理明明上下文和jdni上下文),并根據catalina.usenaming判斷是否注冊namingcontextlistener監聽器給lifecyclelisteners。創建server之后initbasedir,先讀取catalina.home配置system.getproperty(globals.catalina_base_prop),如果沒取到則使用之前生成的臨時目錄,這段直接看代碼吧:

protected void initbasedir() {
 string catalinahome = system.getproperty(globals.catalina_home_prop);
 if (basedir == null) {
  basedir = system.getproperty(globals.catalina_base_prop);
 }
 if (basedir == null) {
  basedir = catalinahome;
 }
 if (basedir == null) {
  // create a temp dir.
  basedir = system.getproperty("user.dir") +
  "/tomcat." + port;
 }
 file basefile = new file(basedir);
 basefile.mkdirs();
 try {
  basefile = basefile.getcanonicalfile();
 } catch (ioexception e) {
  basefile = basefile.getabsolutefile();
 }
 server.setcatalinabase(basefile);
 system.setproperty(globals.catalina_base_prop, basefile.getpath());
 basedir = basefile.getpath();
 if (catalinahome == null) {
  server.setcatalinahome(basefile);
 } else {
  file homefile = new file(catalinahome);
  homefile.mkdirs();
  try {
  homefile = homefile.getcanonicalfile();
  } catch (ioexception e) {
  homefile = homefile.getabsolutefile();
  }
  server.setcatalinahome(homefile);
 }
 system.setproperty(globals.catalina_home_prop,
  server.getcatalinahome().getpath());
 }

   然后又實例化了個standardservice,代碼并沒有什么特別的:

 service = new standardservice();
 service.setname("tomcat");
 server.addservice( service )

  server.addservice( service )這里除了發布了一個propertychangeevent事件,也沒做什么特別的,最后返回這個server。addconnector的邏輯和上面addservice沒什么區別。然后是customizeconnector,這里設置了connector的端口、編碼等信息,并將“bindoninit”和對應值false寫入了最開頭說的靜態代碼塊中的replacements集合,introspectionutils.setproperty(protocolhandler, repl, value)通過反射的方法將protocolhandler實現對象的setbindoninit存在的情況下(拼字符串拼出來的)set為前面的false,這個方法里有大量的判斷比如參數類型及setter的參數類型,比如返回值類型以及沒找到還會try a setproperty("name", "value")等,setproperty可以處理比如abstractendpoint中有個hashmap<string, object> attributes的屬性時會attributes.put(name, value)。如果是ssl還會執行customizessl方法,設置一些ssl用的屬性比如協議比如秘鑰還有可以用上秘鑰倉庫等。如果配置了壓縮,這里還會給協議的相關setter設置值。tomcat.setconnector(connector)不解釋。tomcat.gethost().setautodeploy(false),gethost方法中創建了standardhost并設置host名(例如localhost),并getengine().addchild( host );然后設置host的自動部署。configureengine(tomcat.getengine()),getengine中如果engine為null就初始化標準引擎,設置名字為tomcat,設置realm和service.setcontainer(engine),不過這里engine已經在gethost初始化過了所以直接返回;configureengine方法先設置引擎的后臺進程延遲,并將引擎的value對象注冊給引擎的pipeline,此時尚無value對象實例。這里簡單說明一下:value對象在tomcat的各級容器中都有標準類型,并且各級容器都有一個pipeline,在請求處理過程中會從各級的第一個value對象開始依次執行一遍,value用于加入到對應的各級容器的邏輯,默認有一個標注value實現,名字類似standardhostvalue。

  preparecontext(tomcat.gethost(), initializers),initializers這里是annotationconfigembeddedwebapplicationcontext,context級的根;準備context的過程主要設置base目錄,new一個tomcatembeddedcontext并在構造中判斷了下loadonstartup方法是否被重寫;注冊一個fixcontextlistener監聽,這個監聽用于設置context的配置狀態以及是否加入登錄驗證的邏輯;context.setparentclassloader;設置各種語言的編碼映射,我這里是en和fr設置為utf-8,此處可以使用配置文件org/apache/catalina/util/charsetmapperdefault .properties;設置是否使用相對地址重定向userelativeredirects=false,此屬性應該是tomcat 8.0.30版本加上的;接著就是初始化webapploader,這里和完整版的tomcat有點不一樣,它用的是虛擬機的方式,會將加載類向上委托loader.setdelegate(true),context.setloader(loader);之后就開始創建wapper了,至此engine,host,context及wrapper四個層次的容器都創建完了:

 private void adddefaultservlet(context context) {
 wrapper defaultservlet = context.createwrapper();
 defaultservlet.setname("default");
 defaultservlet.setservletclass("org.apache.catalina.servlets.defaultservlet");
 defaultservlet.addinitparameter("debug", "0");
 defaultservlet.addinitparameter("listings", "false");
 defaultservlet.setloadonstartup(1);
 // otherwise the default location of a spring dispatcherservlet cannot be set
 defaultservlet.setoverridable(true);
 context.addchild(defaultservlet);
 addservletmapping(context, "/", "default");
 }

   connector從socket接收的數據,解析成httpservletrequest后就會經過這幾層容器,有容器各自的value對象鏈依次處理。

  接著是是否注冊jspservlet,jasperinitializer和storemergedwebxmllistener我這里是都沒有的。接著的mergeinitializers方法:

 protected final servletcontextinitializer[] mergeinitializers(
  servletcontextinitializer... initializers) {
 list<servletcontextinitializer> mergedinitializers = new arraylist<servletcontextinitializer>();
 mergedinitializers.addall(arrays.aslist(initializers));
 mergedinitializers.addall(this.initializers);
 return mergedinitializers
  .toarray(new servletcontextinitializer[mergedinitializers.size()]);
 }

Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

  configurecontext(context, initializerstouse)對context做了些設置工作,包括tomcatstarter(實例化并set給context),lifecyclelistener,contextvalue,errorpage,mime,session超時持久化等以及一些自定義工作:    

 tomcatstarter starter = new tomcatstarter(initializers);
 if (context instanceof tomcatembeddedcontext) {
  // should be true
  ((tomcatembeddedcontext) context).setstarter(starter);
 }
 context.addservletcontainerinitializer(starter, no_classes);
 for (lifecyclelistener lifecyclelistener : this.contextlifecyclelisteners) {
  context.addlifecyclelistener(lifecyclelistener);
 }
 for (valve valve : this.contextvalves) {
  context.getpipeline().addvalve(valve);
 }
 for (errorpage errorpage : geterrorpages()) {
  new tomcaterrorpage(errorpage).addtocontext(context);
 }
 for (mimemappings.mapping mapping : getmimemappings()) {
  context.addmimemapping(mapping.getextension(), mapping.getmimetype());
 }

Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

  session如果不需要持久化會注冊一個disablepersistsessionlistener。其他定制化操作是通過tomcatcontextcustomizer的實現類實現的:

Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

  context配置完了作為child add給host,add時給context注冊了個內存泄漏跟蹤的監聽memoryleaktrackinglistener。postprocesscontext(context)方法是空的,留給子類重寫用的。

   getembeddedservletcontainer方法的最后一行:return gettomcatembeddedservletcontainer(tomcat)。

protected tomcatembeddedservletcontainer gettomcatembeddedservletcontainer(
  tomcat tomcat) {
 return new tomcatembeddedservletcontainer(tomcat, getport() >= 0);
 }

   tomcatembeddedservletcontainer的構造函數:

 public tomcatembeddedservletcontainer(tomcat tomcat, boolean autostart) {
 assert.notnull(tomcat, "tomcat server must not be null");
 this.tomcat = tomcat;
 this.autostart = autostart;
 initialize();
 }

  initialize的第一個方法addinstanceidtoenginename對全局原子變量containercounter+1,由于初始值是-1,所以addinstanceidtoenginename方法內后續的獲取引擎并設置名字的邏輯沒有執行:

 private void addinstanceidtoenginename() {
 int instanceid = containercounter.incrementandget();
 if (instanceid > 0) {
  engine engine = this.tomcat.getengine();
  engine.setname(engine.getname() + "-" + instanceid);
 }
 }

   initialize的第二個方法removeserviceconnectors,將上面new的connection以service(這里是standardservice[tomcat])做key保存到private final map<service, connector[]> serviceconnectors中,并將standardservice中的protected connector[] connectors與service解綁(connector.setservice((service)null);),解綁后下面利用lifecyclebase啟動容器就不會啟動到connector了。

  之后是this.tomcat.start(),這段比較復雜,我單獨總結一篇吧。

  tomcatembeddedservletcontainer的初始化,接下來是rethrowdeferredstartupexceptions,這個方法檢查初始化過程中的異常,如果有直接在主線程拋出,檢查方法是tomcatstarter中的private volatile exception startupexception,這個值是在context啟動過程中記錄的:

Spring Boot啟動過程(四)之Spring Boot內嵌Tomcat啟動

 @override
 public void onstartup(set<class<?>> classes, servletcontext servletcontext)
  throws servletexception {
 try {
  for (servletcontextinitializer initializer : this.initializers) {
  initializer.onstartup(servletcontext);
  }
 }
 catch (exception ex) {
  this.startupexception = ex;
  // prevent tomcat from logging and re-throwing when we know we can
  // deal with it in the main thread, but log for information here.
  if (logger.iserrorenabled()) {
  logger.error("error starting tomcat context. exception: "
   + ex.getclass().getname() + ". message: " + ex.getmessage());
  }
 }
 }

  context context = findcontext():

 private context findcontext() {
 for (container child : this.tomcat.gethost().findchildren()) {
  if (child instanceof context) {
  return (context) child;
  }
 }
 throw new illegalstateexception("the host does not contain a context");
 }

  綁定命名的上下文和classloader,不成功也無所謂:          

 try {
   contextbindings.bindclassloader(context, getnamingtoken(context),
    getclass().getclassloader());
  }
  catch (namingexception ex) {
   // naming is not enabled. continue
  }

  startdaemonawaitthread方法的注釋是:與jetty不同,tomcat所有的線程都是守護線程,所以創建一個非守護線程(例:thread[container-0,5,main])來避免服務到這就shutdown了:

 private void startdaemonawaitthread() {
 thread awaitthread = new thread("container-" + (containercounter.get())) {
  @override
  public void run() {
  tomcatembeddedservletcontainer.this.tomcat.getserver().await();
  }
 };
 awaitthread.setcontextclassloader(getclass().getclassloader());
 awaitthread.setdaemon(false);
 awaitthread.start();
 }

  這個await每10秒檢查一次是否關閉了:        

 try {
  awaitthread = thread.currentthread();
  while(!stopawait) {
   try {
   thread.sleep( 10000 );
   } catch( interruptedexception ex ) {
   // continue and check the flag
   }
  }
  } finally {
  awaitthread = null;
  }
  return;

  回到embeddedwebapplicationcontext,initpropertysources方法,用初始化好的servletcontext完善環境變量:

 /**
 * {@inheritdoc}
 * <p>replace {@code servlet}-related property sources.
 */
 @override
 protected void initpropertysources() {
 configurableenvironment env = getenvironment();
 if (env instanceof configurablewebenvironment) {
  ((configurablewebenvironment) env).initpropertysources(this.servletcontext, null);
 }
 }

  createembeddedservletcontainer就結束了,內嵌容器的啟動過程至此結束。

==========================================================

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费毛片视频 | 在线观看亚洲 | 一区在线观看 | a级性生活 | 国产精品欧美一区二区三区 | 欧美视频在线观看 | 一区二区三区欧美在线 | 91av在线免费播放 | 一级a毛片 | 高清国产一区二区三区 | 91高清视频在线观看 | 久久久女女女女999久久 | 懂色av中文字幕一区二区三区 | 国产精品日本一区二区不卡视频 | 91中文字幕在线 | 中文字幕在线精品 | 黄色片免费| 亚洲国产精品成人 | 免费看黄色一级 | 久久精品亚洲精品 | 先锋av资源在线 | 国产一区二区在线免费观看 | www中文字幕| 美女高潮久久久 | 国产精品自产拍在线观看 | 亚洲人成网亚洲欧洲无码 | 在线a视频| 成人a级片在线观看 | 亚洲第一成av人网站懂色 | 亚州中文字幕蜜桃视频 | 久久久久无码国产精品一区 | 国产精品久久久久久久久费观看 | 中文字幕一区二区三区乱码在线 | 中文字幕一区二区三区乱码在线 | 日韩激情一区二区 | 成人在线日本 | 亚洲日本乱码一区两区在线观看 | 黄色裸体网站 | 国产日韩一区二区三区 | 黄色免费视频 | 成人综合区|