欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot啟動(dòng)過(guò)程(四)之Spring Boot內(nèi)嵌Tomcat啟動(dòng)

 更新時(shí)間:2017年04月22日 16:31:49   作者:draculav  
這篇文章主要介紹了Spring Boot啟動(dòng)過(guò)程(四)之Spring Boot內(nèi)嵌Tomcat啟動(dòng)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

之前在Spring Boot啟動(dòng)過(guò)程(二)提到過(guò)createEmbeddedServletContainer創(chuàng)建了內(nèi)嵌的Servlet容器,我用的是默認(rèn)的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方法中調(diào)用了ServerProperties,從ServerProperties的實(shí)例方法customize可以看出Springboot支持三種內(nèi)嵌容器的定制化配置:Tomcat、Jetty、Undertow。

  這里直接說(shuō)TomcatEmbeddedServletContainerFactory的getEmbeddedServletContainer方法了,原因在前面那篇里說(shuō)過(guò)了。不過(guò)首先是getSelfInitializer方法先執(zhí)行的:

 private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
 return new ServletContextInitializer() {
  @Override
  public void onStartup(ServletContext servletContext) throws ServletException {
  selfInitialize(servletContext);
  }
 };
 }

  將初始化的ServletContextInitializer傳給了getEmbeddedServletContainer方法。進(jìn)入了getEmbeddedServletContainer方法直接就是實(shí)例化了一個(gè)Tomcat:

 Tomcat tomcat = new Tomcat();

   然后生成一個(gè)臨時(shí)目錄,并tomcat.setBaseDir,setBaseDir方法的注釋說(shuō)Tomcat需要一個(gè)目錄用于臨時(shí)文件并且它應(yīng)該是第一個(gè)被調(diào)用的方法;如果方法沒(méi)有被調(diào)用會(huì)使用默認(rèn)的幾個(gè)位置system properties - catalina.base, catalina.home - $PWD/tomcat.$PORT,另外/tmp從安全角度來(lái)說(shuō)不建議。

  接著:

 Connector connector = new Connector(this.protocol);

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

  Connector實(shí)例創(chuàng)建好了之后tomcat.getService().addConnector(connector),getService的getServer中new了一個(gè)StandardServer,StandardServer的初始化主要是創(chuàng)建了globalNamingResources(globalNamingResources主要用于管理明明上下文和JDNI上下文),并根據(jù)catalina.useNaming判斷是否注冊(cè)NamingContextListener監(jiān)聽(tīng)器給lifecycleListeners。創(chuàng)建Server之后initBaseDir,先讀取catalina.home配置System.getProperty(Globals.CATALINA_BASE_PROP),如果沒(méi)取到則使用之前生成的臨時(shí)目錄,這段直接看代碼吧:

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());
 }

   然后又實(shí)例化了個(gè)StandardService,代碼并沒(méi)有什么特別的:

 service = new StandardService();
 service.setName("Tomcat");
 server.addService( service )

  server.addService( service )這里除了發(fā)布了一個(gè)PropertyChangeEvent事件,也沒(méi)做什么特別的,最后返回這個(gè)server。addConnector的邏輯和上面addService沒(méi)什么區(qū)別。然后是customizeConnector,這里設(shè)置了Connector的端口、編碼等信息,并將“bindOnInit”和對(duì)應(yīng)值false寫(xiě)入了最開(kāi)頭說(shuō)的靜態(tài)代碼塊中的replacements集合,IntrospectionUtils.setProperty(protocolHandler, repl, value)通過(guò)反射的方法將protocolHandler實(shí)現(xiàn)對(duì)象的setBindOnInit存在的情況下(拼字符串拼出來(lái)的)set為前面的false,這個(gè)方法里有大量的判斷比如參數(shù)類(lèi)型及setter的參數(shù)類(lèi)型,比如返回值類(lèi)型以及沒(méi)找到還會(huì)try a setProperty("name", "value")等,setProperty可以處理比如AbstractEndpoint中有個(gè)HashMap<String, Object> attributes的屬性時(shí)會(huì)attributes.put(name, value)。如果是ssl還會(huì)執(zhí)行customizeSsl方法,設(shè)置一些SSL用的屬性比如協(xié)議比如秘鑰還有可以用上秘鑰倉(cāng)庫(kù)等。如果配置了壓縮,這里還會(huì)給協(xié)議的相關(guān)setter設(shè)置值。tomcat.setConnector(connector)不解釋。tomcat.getHost().setAutoDeploy(false),getHost方法中創(chuàng)建了StandardHost并設(shè)置host名(例如localhost),并getEngine().addChild( host );然后設(shè)置host的自動(dòng)部署。configureEngine(tomcat.getEngine()),getEngine中如果engine為null就初始化標(biāo)準(zhǔn)引擎,設(shè)置名字為T(mén)omcat,設(shè)置Realm和service.setContainer(engine),不過(guò)這里engine已經(jīng)在getHost初始化過(guò)了所以直接返回;configureEngine方法先設(shè)置引擎的后臺(tái)進(jìn)程延遲,并將引擎的Value對(duì)象注冊(cè)給引擎的pipeline,此時(shí)尚無(wú)value對(duì)象實(shí)例。這里簡(jiǎn)單說(shuō)明一下:value對(duì)象在Tomcat的各級(jí)容器中都有標(biāo)準(zhǔn)類(lèi)型,并且各級(jí)容器都有一個(gè)pipeline,在請(qǐng)求處理過(guò)程中會(huì)從各級(jí)的第一個(gè)value對(duì)象開(kāi)始依次執(zhí)行一遍,value用于加入到對(duì)應(yīng)的各級(jí)容器的邏輯,默認(rèn)有一個(gè)標(biāo)注value實(shí)現(xiàn),名字類(lèi)似StandardHostValue。

  prepareContext(tomcat.getHost(), initializers),initializers這里是AnnotationConfigEmbeddedWebApplicationContext,Context級(jí)的根;準(zhǔn)備Context的過(guò)程主要設(shè)置Base目錄,new一個(gè)TomcatEmbeddedContext并在構(gòu)造中判斷了下loadOnStartup方法是否被重寫(xiě);注冊(cè)一個(gè)FixContextListener監(jiān)聽(tīng),這個(gè)監(jiān)聽(tīng)用于設(shè)置context的配置狀態(tài)以及是否加入登錄驗(yàn)證的邏輯;context.setParentClassLoader;設(shè)置各種語(yǔ)言的編碼映射,我這里是en和fr設(shè)置為UTF-8,此處可以使用配置文件org/apache/catalina/util/CharsetMapperDefault .properties;設(shè)置是否使用相對(duì)地址重定向useRelativeRedirects=false,此屬性應(yīng)該是Tomcat 8.0.30版本加上的;接著就是初始化webapploader,這里和完整版的Tomcat有點(diǎn)不一樣,它用的是虛擬機(jī)的方式,會(huì)將加載類(lèi)向上委托l(wèi)oader.setDelegate(true),context.setLoader(loader);之后就開(kāi)始創(chuàng)建Wapper了,至此engine,host,context及wrapper四個(gè)層次的容器都創(chuàng)建完了:

 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接收的數(shù)據(jù),解析成HttpServletRequest后就會(huì)經(jīng)過(guò)這幾層容器,有容器各自的Value對(duì)象鏈依次處理。

  接著是是否注冊(cè)jspServlet,jasperInitializer和StoreMergedWebXmlListener我這里是都沒(méi)有的。接著的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()]);
 }

  configureContext(context, initializersToUse)對(duì)context做了些設(shè)置工作,包括TomcatStarter(實(shí)例化并set給context),LifecycleListener,contextValue,errorpage,Mime,session超時(shí)持久化等以及一些自定義工作:    

 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());
 }

  Session如果不需要持久化會(huì)注冊(cè)一個(gè)DisablePersistSessionListener。其他定制化操作是通過(guò)TomcatContextCustomizer的實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)的:

  context配置完了作為child add給host,add時(shí)給context注冊(cè)了個(gè)內(nèi)存泄漏跟蹤的監(jiān)聽(tīng)MemoryLeakTrackingListener。postProcessContext(context)方法是空的,留給子類(lèi)重寫(xiě)用的。

   getEmbeddedServletContainer方法的最后一行:return getTomcatEmbeddedServletContainer(tomcat)。

protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
  Tomcat tomcat) {
 return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
 }

   TomcatEmbeddedServletContainer的構(gòu)造函數(shù):

 public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
 Assert.notNull(tomcat, "Tomcat Server must not be null");
 this.tomcat = tomcat;
 this.autoStart = autoStart;
 initialize();
 }

  initialize的第一個(gè)方法addInstanceIdToEngineName對(duì)全局原子變量containerCounter+1,由于初始值是-1,所以addInstanceIdToEngineName方法內(nèi)后續(xù)的獲取引擎并設(shè)置名字的邏輯沒(méi)有執(zhí)行:

 private void addInstanceIdToEngineName() {
 int instanceId = containerCounter.incrementAndGet();
 if (instanceId > 0) {
  Engine engine = this.tomcat.getEngine();
  engine.setName(engine.getName() + "-" + instanceId);
 }
 }

   initialize的第二個(gè)方法removeServiceConnectors,將上面new的connection以service(這里是StandardService[Tomcat])做key保存到private final Map<Service, Connector[]> serviceConnectors中,并將StandardService中的protected Connector[] connectors與service解綁(connector.setService((Service)null);),解綁后下面利用LifecycleBase啟動(dòng)容器就不會(huì)啟動(dòng)到Connector了。

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

  TomcatEmbeddedServletContainer的初始化,接下來(lái)是rethrowDeferredStartupExceptions,這個(gè)方法檢查初始化過(guò)程中的異常,如果有直接在主線程拋出,檢查方法是TomcatStarter中的private volatile Exception startUpException,這個(gè)值是在Context啟動(dòng)過(guò)程中記錄的:

 @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,不成功也無(wú)所謂:          

 try {
   ContextBindings.bindClassLoader(context, getNamingToken(context),
    getClass().getClassLoader());
  }
  catch (NamingException ex) {
   // Naming is not enabled. Continue
  }

  startDaemonAwaitThread方法的注釋是:與Jetty不同,Tomcat所有的線程都是守護(hù)線程,所以創(chuàng)建一個(gè)非守護(hù)線程(例:Thread[container-0,5,main])來(lái)避免服務(wù)到這就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();
 }

  這個(gè)await每10秒檢查一次是否關(guān)閉了:        

 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完善環(huán)境變量:

 /**
 * {@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就結(jié)束了,內(nèi)嵌容器的啟動(dòng)過(guò)程至此結(jié)束。

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

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

相關(guān)文章

  • 解決IDEA?2022?Translation?翻譯文檔失敗:?未知錯(cuò)誤的問(wèn)題

    解決IDEA?2022?Translation?翻譯文檔失敗:?未知錯(cuò)誤的問(wèn)題

    這篇文章主要介紹了IDEA?2022?Translation?翻譯文檔失敗:?未知錯(cuò)誤,本文較詳細(xì)的給大家介紹了IDEA?2022?Translation未知錯(cuò)誤翻譯文檔失敗的解決方法,需要的朋友可以參考下
    2022-04-04
  • IDEA中Git的基本應(yīng)用詳解

    IDEA中Git的基本應(yīng)用詳解

    這篇文章主要介紹了IDEA中Git的基本應(yīng)用詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java利用Jackson序列化實(shí)現(xiàn)數(shù)據(jù)脫敏詳解

    Java利用Jackson序列化實(shí)現(xiàn)數(shù)據(jù)脫敏詳解

    在項(xiàng)目中有些敏感信息不能直接展示,比如客戶手機(jī)號(hào)、身份證、車(chē)牌號(hào)等信息,展示時(shí)均需要進(jìn)行數(shù)據(jù)脫敏,防止泄露客戶隱私。本文將利用Jackson序列化實(shí)現(xiàn)數(shù)據(jù)脫敏,需要的可以參考一下
    2023-03-03
  • Spring源碼解密之默認(rèn)標(biāo)簽的解析

    Spring源碼解密之默認(rèn)標(biāo)簽的解析

    這篇文章主要給大家介紹了關(guān)于Spring源碼解密之默認(rèn)標(biāo)簽的解析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • springboot之如何獲取項(xiàng)目目錄路徑

    springboot之如何獲取項(xiàng)目目錄路徑

    這篇文章主要介紹了springboot之如何獲取項(xiàng)目目錄路徑問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • idea中service或者mapper引入報(bào)紅的問(wèn)題及解決

    idea中service或者mapper引入報(bào)紅的問(wèn)題及解決

    在使用IntelliJ IDEA開(kāi)發(fā)SpringBoot項(xiàng)目時(shí),有時(shí)會(huì)遇到Service或Mapper接口引入時(shí)報(bào)紅但不影響項(xiàng)目運(yùn)行的情況,這主要是因?yàn)镮DEA的檢查級(jí)別設(shè)置問(wèn)題,解決方法是將有問(wèn)題的Error級(jí)別改為編譯通過(guò)的安全級(jí)別,即可消除報(bào)紅
    2024-09-09
  • spring security與corsFilter沖突的解決方案

    spring security與corsFilter沖突的解決方案

    這篇文章主要介紹了spring security與corsFilter沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot中@ConditionalOnProperty的使用及作用詳解

    SpringBoot中@ConditionalOnProperty的使用及作用詳解

    這篇文章主要介紹了SpringBoot中@ConditionalOnProperty的使用及作用詳解,@ConditionalOnProperty通過(guò)讀取本地配置文件中的值來(lái)判斷 某些 Bean 或者 配置類(lèi) 是否加入spring 中,需要的朋友可以參考下
    2024-01-01
  • spring四種依賴(lài)注入方式的詳細(xì)介紹

    spring四種依賴(lài)注入方式的詳細(xì)介紹

    本篇文章主要介紹了spring四種依賴(lài)注入方式的詳細(xì)介紹,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • Java多線程中的Phaser使用解析

    Java多線程中的Phaser使用解析

    這篇文章主要介紹了Java多線程中的Phaser使用解析,java多線程技術(shù)提供了Phaser工具類(lèi),Phaser表示“階段器”,用來(lái)解決控制多個(gè)線程分階段共同完成任務(wù)的情景問(wèn)題,其作用相比CountDownLatch和CyclicBarrier更加靈活,需要的朋友可以參考下
    2023-11-11

最新評(píng)論