Heim  >  Artikel  >  Java  >  Analyse des Servlet-Funktionsprinzips

Analyse des Servlet-Funktionsprinzips

巴扎黑
巴扎黑Original
2017-04-30 10:01:161150Durchsuche

Die Webtechnologie ist heute zu einer der gängigen Internet-Webanwendungstechnologien geworden, und Servlet ist die Kerngrundlage der Java-Webtechnologie. Daher ist die Beherrschung des Funktionsprinzips von Servlet eine Grundvoraussetzung, um ein qualifizierter Java-Webtechnologieentwickler zu werden. In diesem Artikel erfahren Sie, wie die auf Servlets basierende Java-Webtechnologie funktioniert. Nehmen Sie Tomcat als Beispiel, um zu verstehen, wie der Servlet-Container funktioniert. Wie wird ein Webprojekt in einem Servlet-Container gestartet? Wie analysiert der Servlet-Container das Servlet, das Sie in web.xml definieren? Wie werden Benutzeranfragen bestimmten Servlets zugewiesen? Wie verwaltet der Servlet-Container den Servlet-Lebenszyklus? Außerdem erfahren Sie mehr über die Klassenhierarchie der neuesten Servlet-API sowie die Analyse einiger schwieriger Probleme in Servlet.

Ausgehend vom Servlet-Container

Um Servlet vorzustellen, müssen wir zunächst den Servlet-Container klar erklären. Die Beziehung zwischen Servlet und Servlet-Container ist ein bisschen wie die Beziehung zwischen einer Waffe und Kugeln. Die Waffe ist für Kugeln gemacht, und die Kugeln machen die Waffe tödlich. Obwohl sie voneinander abhängig sind, entwickeln sie sich unabhängig voneinander und sind allesamt das Ergebnis der Anpassung an die industrielle Produktion. Aus technischer Sicht geht es darum, über standardisierte Schnittstellen voneinander zu entkoppeln und zusammenzuarbeiten. Da Schnittstellen der Schlüssel zur Verbindung von Servlets und Servlet-Containern sind, beginnen wir mit ihren Schnittstellen.

Wie bereits erwähnt, handelt es sich bei dem Servlet-Container um ein unabhängig entwickeltes, standardisiertes Produkt. Es gibt jedoch alle ihre eigene Marktpositionierung. Es ist schwierig zu sagen, wer besser und wer schlechter ist. Beispielsweise hat der beliebte Jetty im Bereich Anpassung und Mobilgeräte gute Fortschritte gemacht. Hier nehmen wir Tomcat, das allen am besten bekannt ist, als Beispiel, um vorzustellen, wie der Servlet-Container Servlets verwaltet. Tomcat selbst ist ebenfalls sehr komplex. Wir beginnen nur mit der Schnittstelle zwischen Servlet und Servlet-Container. Eine detaillierte Einführung in Tomcat finden Sie in meinem anderen Artikel „Tomcat-Systemarchitektur und Musterdesignanalyse“.

Auf der Containerebene von Tomcat ist der Kontextcontainer die Wrapper-Klasse Wrapper, die das Servlet im Container direkt verwaltet. Die Art und Weise, wie der Kontextcontainer ausgeführt wird, wirkt sich also direkt auf die Funktionsweise des Servlets aus.

Abbildung 1. Tomcat-Containermodell

图 1 . Tomcat 容器模型

Wie aus der obigen Abbildung ersichtlich ist, sind Tomcat-Container in vier Ebenen unterteilt. Der Container, der tatsächlich Servlets verwaltet, ist ein Kontextcontainer, der leicht in der Tomcat-Konfigurationsdatei zu finden ist.

Liste 1 Kontextkonfigurationsparameter

 <Context path="/projectOne " docBase="D:\projects\projectOne" 
 reloadable="true" />

Im Folgenden finden Sie eine detaillierte Einführung in den Prozess der Tomcat-Analyse des Kontextcontainers, einschließlich der Erstellung eines Servlets.

Startvorgang des Servlet-Containers

Tomcat7 begann auch, eingebettete Funktionen zu unterstützen und fügte eine Startup-Klasse org.apache.catalina.startup.Tomcat hinzu. Tomcat kann einfach gestartet werden, indem ein Instanzobjekt erstellt und die Startmethode aufgerufen wird. Wir können dieses Objekt auch verwenden, um die Konfigurationsparameter von Tomcat hinzuzufügen und zu ändern, z. B. das dynamische Hinzufügen von Kontext, Servlet usw. Als Nächstes verwenden wir diese Tomcat-Klasse, um einen neuen Kontextcontainer zu verwalten. Wir wählen das Beispiel-Webprojekt aus, das mit Tomcat7 geliefert wird, und sehen, wie es diesem Kontextcontainer hinzugefügt wird.

Liste 2. Ein Webprojekt zu Tomcat hinzufügen

 Tomcat tomcat = getTomcatInstance(); 
 File appDir = new File(getBuildDirectory(), "webapps/examples"); 
 tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); 
 tomcat.start(); 
 ByteChunk res = getUrl("http://localhost:" + getPort() + 
               "/examples/servlets/servlet/HelloWorldExample"); 
 assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0);

Der Code in Listing 1 besteht darin, eine Tomcat-Instanz zu erstellen und eine Webanwendung hinzuzufügen, dann Tomcat zu starten und eines der HelloWorldExample-Servlets aufzurufen, um zu sehen, ob die erwarteten Daten korrekt zurückgegeben werden.

Der Code der addWebapp-Methode von Tomcat lautet wie folgt:

Listing 3.Tomcat.addWebapp

 public Context addWebapp(Host host, String url, String path) { 
        silence(url); 
        Context ctx = new StandardContext(); 
        ctx.setPath( url ); 
        ctx.setDocBase(path); 
        if (defaultRealm == null) { 
            initSimpleAuth(); 
        } 
        ctx.setRealm(defaultRealm); 
        ctx.addLifecycleListener(new DefaultWebXmlListener()); 
        ContextConfig ctxCfg = new ContextConfig(); 
        ctx.addLifecycleListener(ctxCfg); 
        ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML"); 
        if (host == null) { 
            getHost().addChild(ctx); 
        } else { 
            host.addChild(ctx); 
        } 
        return ctx; 
 }

Wir haben bereits eingeführt, dass eine Webanwendung einem Kontextcontainer entspricht, der der Servlet-Container ist, wenn das Servlet ausgeführt wird. Beim Hinzufügen einer Webanwendung wird ein StandardContext-Container erstellt und die erforderlichen Parameter werden für den Kontextcontainer festgelegt. Die URL und der Pfad stellen jeweils die Anwendung dar. Der Zugriffspfad in Tomcat und der tatsächliche physische Pfad dieser Anwendung stimmen mit den beiden Parametern in Listing 1 überein. Die wichtigste Konfiguration ist ContextConfig. Diese Klasse ist für das Parsen der gesamten Webanwendungskonfiguration verantwortlich, die später ausführlich vorgestellt wird. Fügen Sie abschließend diesen Kontextcontainer zum übergeordneten Container Host hinzu.

  接下去将会调用 Tomcat 的 start 方法启动 Tomcat,如果你清楚 Tomcat 的系统架构,你会容易理解 Tomcat 的启动逻辑,Tomcat 的启动逻辑是基于观察者模式设计的,所有的容器都会继承 Lifecycle 接口,它管理者容器的整个生命周期,所有容器的的修改和状态的改变都会由它去通知已经注册的观察者(Listener),关于这个设计模式可以参考《 Tomcat 的系统架构与设计模式,第二部分:设计模式》。Tomcat 启动的时序图可以用图 2 表示。

  图 2. Tomcat 主要类的启动时序图(查看大图)

图 2. Tomcat 主要类的启动时序图

  上图描述了 Tomcat 启动过程中,主要类之间的时序关系,下面我们将会重点关注添加 examples 应用所对应的 StandardContext 容器的启动过程。

  当 Context 容器初始化状态设为 init 时,添加在 Contex 容器的 Listener 将会被调用。ContextConfig 继承了 LifecycleListener 接口,它是在调用清单 3 时被加入到 StandardContext 容器中。ContextConfig 类会负责整个 Web 应用的配置文件的解析工作。

  ContextConfig 的 init 方法将会主要完成以下工作:

  1. 创建用于解析 xml 配置文件的 contextDigester 对象


  2. 读取默认 context.xml 配置文件,如果存在解析它


  3. 读取默认 Host 配置文件,如果存在解析它


  4. 读取默认 Context 自身的配置文件,如果存在解析它


  5. 设置 Context 的 DocBase

  ContextConfig 的 init 方法完成后,Context 容器的会执行 startInternal 方法,这个方法启动逻辑比较复杂,主要包括如下几个部分:

  1. 创建读取资源文件的对象


  2. 创建 ClassLoader 对象


  3. 设置应用的工作目录


  4. 启动相关的辅助类如:logger、realm、resources 等


  5. 修改启动状态,通知感兴趣的观察者(Web 应用的配置)


  6. 子容器的初始化


  7. 获取 ServletContext 并设置必要的参数


  8. 初始化“load on startup”的 Servlet

  Web 应用的初始化工作

  Web 应用的初始化工作是在 ContextConfig 的 configureStart 方法中实现的,应用的初始化主要是要解析 web.xml 文件,这个文件描述了一个 Web 应用的关键信息,也是一个 Web 应用的入口。

  Tomcat 首先会找 globalWebXml 这个文件的搜索路径是在 engine 的工作目录下寻找以下两个文件中的任一个 org/apache/catalin/startup/NO_DEFAULT_XML 或 conf/web.xml。接着会找 hostWebXml 这个文件可能会在 System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default,接着寻找应用的配置文件 examples/WEB-INF/web.xml。web.xml 文件中的各个配置项将会被解析成相应的属性保存在 WebXml 对象中。如果当前应用支持 Servlet3.0,解析还将完成额外 9 项工作,这个额外的 9 项工作主要是为 Servlet3.0 新增的特性,包括 jar 包中的 META-INF/web-fragment.xml 的解析以及对 annotations 的支持。

  接下去将会将 WebXml 对象中的属性设置到 Context 容器中,这里包括创建 Servlet 对象、filter、listener 等等。这段代码在 WebXml 的 configureContext 方法中。下面是解析 Servlet 的代码片段:

  清单 4. 创建 Wrapper 实例

 for (ServletDef servlet : servlets.values()) { 
            Wrapper wrapper = context.createWrapper(); 
            String jspFile = servlet.getJspFile(); 
            if (jspFile != null) { 
                wrapper.setJspFile(jspFile); 
            } 
            if (servlet.getLoadOnStartup() != null) { 
                wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue()); 
            } 
            if (servlet.getEnabled() != null) { 
                wrapper.setEnabled(servlet.getEnabled().booleanValue()); 
            } 
            wrapper.setName(servlet.getServletName()); 
            Map<String,String> params = servlet.getParameterMap(); 
            for (Entry<String, String> entry : params.entrySet()) { 
                wrapper.addInitParameter(entry.getKey(), entry.getValue()); 
            } 
            wrapper.setRunAs(servlet.getRunAs()); 
            Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs(); 
            for (SecurityRoleRef roleRef : roleRefs) { 
                wrapper.addSecurityReference( 
                        roleRef.getName(), roleRef.getLink()); 
            } 
            wrapper.setServletClass(servlet.getServletClass()); 
            MultipartDef multipartdef = servlet.getMultipartDef(); 
            if (multipartdef != null) { 
                if (multipartdef.getMaxFileSize() != null && 
                        multipartdef.getMaxRequestSize()!= null && 
                        multipartdef.getFileSizeThreshold() != null) { 
                    wrapper.setMultipartConfigElement(new 
 MultipartConfigElement( 
                            multipartdef.getLocation(), 
                            Long.parseLong(multipartdef.getMaxFileSize()), 
                            Long.parseLong(multipartdef.getMaxRequestSize()), 
                            Integer.parseInt( 
                                    multipartdef.getFileSizeThreshold()))); 
                } else { 
                    wrapper.setMultipartConfigElement(new 
 MultipartConfigElement( 
                            multipartdef.getLocation())); 
                } 
            } 
            if (servlet.getAsyncSupported() != null) { 
                wrapper.setAsyncSupported( 
                        servlet.getAsyncSupported().booleanValue()); 
            } 
            context.addChild(wrapper); 
 }

  这段代码清楚的描述了如何将 Servlet 包装成 Context 容器中的 StandardWrapper,这里有个疑问,为什么要将 Servlet 包装成 StandardWrapper 而不直接是 Servlet 对象。这里 StandardWrapper 是 Tomcat 容器中的一部分,它具有容器的特征,而 Servlet 为了一个独立的 web 开发标准,不应该强耦合在 Tomcat 中。

  除了将 Servlet 包装成 StandardWrapper 并作为子容器添加到 Context 中,其它的所有 web.xml 属性都被解析到 Context 中,所以说 Context 容器才是真正运行 Servlet 的 Servlet 容器。一个 Web 应用对应一个 Context 容器,容器的配置属性由应用的 web.xml 指定,这样我们就能理解 web.xml 到底起到什么作用了。


Servlet-Instanz erstellen

Die Parsing-Arbeit des Servlets wurde bereits abgeschlossen, es wurde in einen StandardWrapper gepackt und dem Kontextcontainer hinzugefügt, aber es funktioniert für uns immer noch nicht, da es nicht instanziiert wurde. Im Folgenden stellen wir vor, wie das Servlet-Objekt erstellt und initialisiert wird.

Servlet-Objekt erstellen

Wenn das Load-on-Startup-Konfigurationselement des Servlets größer als 0 ist, wird es beim Starten des Kontextcontainers instanziiert. Wie bereits erwähnt, wird beim Parsen der Konfigurationsdatei, die sich im Web befindet, die Standardeinstellung globalWebXml gelesen. XML-Datei unter conf Es sind einige Standardkonfigurationselemente definiert, die zwei Servlets definieren, nämlich: org.apache.catalina.servlets.DefaultServlet und org.apache.jasper.servlet.JspServlet. Ihre Last beim Start beträgt 1 bzw. 3. Das heißt, diese beiden Servlets werden beim Start von Tomcat gestartet.

Die Methode zum Erstellen einer Servlet-Instanz beginnt mit Wrapper.loadServlet. Die Aufgabe der Methode „loadServlet“ besteht darin, die ServletClass abzurufen und sie dann an den InstanceManager zu übergeben, um ein Objekt basierend auf servletClass.class zu erstellen. Wenn dieses Servlet mit einer JSP-Datei konfiguriert ist, ist diese Servlet-Klasse org.apache.jasper.servlet.JspServlet, definiert in conf/web.xml.

Das relevante Klassenstrukturdiagramm zum Erstellen eines Servlet-Objekts lautet wie folgt:

Abbildung 3. Verwandte Klassenstruktur zum Erstellen eines Servlet-Objekts

图 3. 创建 Servlet 对象的相关类结构

Servlet initialisieren

Initialisieren Sie das Servlet in der initServlet-Methode von StandardWrapper. Diese Methode dient lediglich dazu, die init-Methode des Servlets aufzurufen und gleichzeitig die StandardWrapperFacade, die das StandardWrapper-Objekt umschließt, als ServletConfig an das Servlet zu übergeben. Warum der Tomcat-Container StandardWrapperFacade an das Servlet-Objekt übergibt, wird später im Detail analysiert.

Wenn das Servlet einer JSP-Datei zugeordnet ist, wird das JspServlet zuvor initialisiert. Als Nächstes wird eine einfache Anforderung zum Aufrufen der JSP-Datei simuliert, um die JSP-Datei in eine Klasse zu kompilieren und die Klasse zu initialisieren.

Auf diese Weise wird das Servlet-Objekt tatsächlich initialisiert, um zu verhindern, dass das Servlet in der Lage ist, unvorhersehbare Fehler zu erkennen und zu steuern. Hier konzentrieren wir uns nur auf die Erklärung einiger wichtiger Links und versuchen, allen einen Gesamtkontext zu bieten.

Das Folgende ist ein vollständiges Zeitdiagramm dieses Prozesses, wobei einige Details weggelassen wurden.

Abbildung 4. Sequenzdiagramm der Initialisierung des Servlets (größeres Bild anzeigen)

图 4. 初始化 Servlet 的时序图


Servlet-Architektur

Wir wissen, dass Java-Webanwendungen auf der Grundlage der Servlet-Spezifikation ausgeführt werden. Wie läuft also das Servlet selbst? Warum eine solche Architektur entwerfen?

Abbildung 5. Servlet-Klassenassoziationsdiagramm der obersten Ebene

图 5.Servlet 顶层类关联图

Wie aus der obigen Abbildung ersichtlich ist, basiert die Servlet-Spezifikation auf diesen Klassen. Es gibt drei Klassen, die automatisch mit Servlet verknüpft sind, nämlich ServletConfig, ServletRequest und ServletResponse. Diese drei Klassen werden über den Container an das Servlet übergeben. ServletConfig wird an das Servlet übergeben, wenn das Servlet initialisiert wird, und die letzten beiden werden übergeben, wenn das Servlet aufgerufen wird, wenn die Anforderung eintrifft. Wir verstehen die Bedeutung von ServletRequest und ServletResponse im Servlet-Betrieb klar, aber welchen Wert haben ServletConfig und ServletContext für Servlet? Ein genauerer Blick auf die in der ServletConfig-Schnittstelle deklarierten Methoden zeigt, dass diese Methoden alle zum Abrufen einiger Konfigurationseigenschaften dieses Servlets verwendet werden und dass diese Konfigurationseigenschaften verwendet werden können, wenn das Servlet ausgeführt wird. Und was macht ServletContext? Der Laufmodus von Servlet ist ein typischer „Handshake-interaktiver“ Laufmodus. Die sogenannte „Handshake-Typ-Interaktion“ bedeutet, dass die beiden Module in der Regel ein Transaktionsszenario zum Datenaustausch vorbereiten und dieses Szenario den Transaktionsprozess bis zum Abschluss der Transaktion verfolgt. Die Initialisierung dieses Transaktionsszenarios wird basierend auf den vom Transaktionsobjekt angegebenen Parametern angepasst. Diese angegebenen Parameter sind normalerweise eine Konfigurationsklasse. Sie haben also Recht, das Transaktionsszenario wird von ServletContext beschrieben und der benutzerdefinierte Parametersatz wird von ServletConfig beschrieben. ServletRequest und ServletResponse sind die spezifischen Objekte, mit denen interagiert werden soll. Sie werden normalerweise als Transportwerkzeuge verwendet, um Interaktionsergebnisse zu liefern.

ServletConfig wird während der Servlet-Initialisierung vom Container übergeben. Was genau ist ServletConfig?

Die folgende Abbildung ist das Klassenbeziehungsdiagramm von ServletConfig und ServletContext im Tomcat-Container.

 Abbildung 6. Klassenzuordnungsdiagramm von ServletConfig im Container

图 6. ServletConfig 在容器中的类关联图

Wie aus der obigen Abbildung ersichtlich ist, implementieren sowohl StandardWrapper als auch StandardWrapperFacade die ServletConfig-Schnittstelle, und StandardWrapperFacade ist die StandardWrapper-Fassadenklasse. Was also an das Servlet übergeben wird, ist das StandardWrapperFacade-Objekt. Diese Klasse kann sicherstellen, dass die von ServletConfig angegebenen Daten vom StandardWrapper abgerufen werden, ohne dass dem Servlet Daten angezeigt werden, die ServletConfig nicht interessieren.

Ebenso hat ServletContext eine ähnliche Struktur wie ServletConfig. Das eigentliche Objekt von ServletContext, das in Servlet abgerufen werden kann, ist ebenfalls ein ApplicationContextFacade-Objekt. ApplicationContextFacade stellt außerdem sicher, dass ServletContex nur die Daten aus dem Container abrufen kann, die es erhalten sollte. Sie alle spielen eine Rolle bei der Kapselung von Daten und verwenden alle das Fassadenentwurfsmuster.

Über ServletContext können Sie einige notwendige Informationen im Kontextcontainer abrufen, z. B. den Arbeitspfad der Anwendung, die vom Container unterstützte Mindestversion von Servlet usw.

Was sind die eigentlichen Objekte der beiden in Servlet definierten ServletRequest und ServletResponse? Wenn wir unsere eigenen Servlet-Klassen erstellen, verwenden wir normalerweise HttpServletRequest und HttpServletResponse, die ServletRequest und ServletResponse erben. Warum können die vom Kontextcontainer übergebenen ServletRequest und ServletResponse in HttpServletRequest und HttpServletResponse konvertiert werden?

Abbildung 7. Anforderungsbezogenes Klassenstrukturdiagramm

图 7.Request 相关类结构图

Das Bild oben ist das von Tomcat erstellte Klassenstrukturdiagramm von Request und Response. Wenn Tomcat eine Anfrage erhält, erstellt es zunächst org.apache.coyote.Request und org.apache.coyote.Response. Diese beiden Klassen werden intern von Tomcat verwendet, um eine Anfrage und entsprechende Informationsklassen zu beschreiben Die Funktion besteht darin, die Anforderung nach dem Empfang der Anforderung durch den Server nach einer einfachen Analyse schnell nachfolgenden Threads zur Verarbeitung zuzuweisen, sodass ihre Objekte sehr klein sind und von der JVM problemlos recycelt werden können. Als nächstes werden bei der Übergabe an einen Benutzerthread zur Bearbeitung der Anfrage die Objekte org.apache.catalina.connector.Request und org.apache.catalina.connector.Response erstellt. Diese beiden Objekte durchlaufen den gesamten Servlet-Container, bis sie an das Servlet übergeben werden. Dabei handelt es sich um die Fassadenklassen RequestFacade und RequestFacade von Request und Response. Der hier verwendete Fassadenmodus basiert auf dem gleichen Zweck wie zuvor Kapseln Sie die Daten im Container. Die einer Anfrage entsprechende Klassentransformation von Request und Response ist in der folgenden Abbildung dargestellt:

Abbildung 8. Der Transformationsprozess von Anfrage und Antwort

图 8.Request 和 Response 的转变过程


So funktioniert Servlet

Wir haben bereits verstanden, wie das Servlet geladen wird, wie das Servlet initialisiert wird und welche Architektur das Servlet hat. Nun stellt sich die Frage, wie es aufgerufen wird.

Wenn der Benutzer eine Anfrage vom Browser an den Server initiiert, enthält diese normalerweise die folgenden Informationen: http://hostname: port /contextpath/servletpath Der Hostname und der Port werden zum Herstellen einer TCP-Verbindung mit dem Server verwendet, und Folgendes Die URL wird verwendet, um auszuwählen, dass der Untercontainer auf dem Server die Anfrage des Benutzers bedient. Wie erreicht der Server also basierend auf dieser URL den richtigen Servlet-Container?

Dieses Problem lässt sich in Tomcat7.0 leicht lösen, da diese Zuordnungsarbeit von einer speziellen Klasse durchgeführt wird, nämlich org.apache.tomcat.util.http.mapper. Diese Klasse speichert alle untergeordneten Informationen im Container von Tomcat. Wenn die Klasse „org.apache.catalina.connector“ in den Container-Container eintritt, setzt der Mapper die Host- und Kontextcontainer auf das Attribut „mappingData“ der Anforderung basierend auf dem Hostnamen und dem Kontextpfad dieser Anforderung. Bevor die Anfrage also in den Container-Container gelangt, wurde bereits festgelegt, auf welchen Untercontainer sie zugreifen möchte.

Abbildung 9. Mapper-Klassendiagramm von Request

图 9.Request 的 Mapper 类关系图

Vielleicht haben Sie Fragen, wie es eine vollständige Beziehung zwischen Containern im Mapper geben kann. Dies geht auf den 19-stufigen Initialisierungsprozess der MapperListener-Klasse in Abbildung 2 zurück. Das Folgende ist der Init-Methodencode von MapperListener:

Listing 5. MapperListener.init

 public void init() { 
        findDefaultHost(); 
        Engine engine = (Engine) connector.getService().getContainer(); 
        engine.addContainerListener(this); 
        Container[] conHosts = engine.findChildren(); 
        for (Container conHost : conHosts) { 
            Host host = (Host) conHost; 
            if (!LifecycleState.NEW.equals(host.getState())) { 
                host.addLifecycleListener(this); 
                registerHost(host); 
            } 
        } 
 }

  这段代码的作用就是将 MapperListener 类作为一个监听者加到整个 Container 容器中的每个子容器中,这样只要任何一个容器发生变化,MapperListener 都将会被通知,相应的保存容器关系的 MapperListener 的 mapper 属性也会修改。for 循环中就是将 host 及下面的子容器注册到 mapper 中。

  图 10.Request 在容器中的路由图

图 10.Request 在容器中的路由图

  上图描述了一次 Request 请求是如何达到最终的 Wrapper 容器的,我们现正知道了请求是如何达到正确的 Wrapper 容器,但是请求到达最终的 Servlet 还要完成一些步骤,必须要执行 Filter 链,以及要通知你在 web.xml 中定义的 listener。

  接下去就要执行 Servlet 的 service 方法了,通常情况下,我们自己定义的 servlet 并不是直接去实现 javax.servlet.servlet 接口,而是去继承更简单的 HttpServlet 类或者 GenericServlet 类,我们可以有选择的覆盖相应方法去实现我们要完成的工作。

  Servlet 的确已经能够帮我们完成所有的工作了,但是现在的 web 应用很少有直接将交互全部页面都用 servlet 来实现,而是采用更加高效的 MVC 框架来实现。这些 MVC 框架基本的原理都是将所有的请求都映射到一个 Servlet,然后去实现 service 方法,这个方法也就是 MVC 框架的入口。

  当 Servlet 从 Servlet 容器中移除时,也就表明该 Servlet 的生命周期结束了,这时 Servlet 的 destroy 方法将被调用,做一些扫尾工作。


 Session 与 Cookie

  前面我们已经说明了 Servlet 如何被调用,我们基于 Servlet 来构建应用程序,那么我们能从 Servlet 获得哪些数据信息呢?

  Servlet 能够给我们提供两部分数据,一个是在 Servlet 初始化时调用 init 方法时设置的 ServletConfig,这个类基本上含有了 Servlet 本身和 Servlet 所运行的 Servlet 容器中的基本信息。根据前面的介绍 ServletConfig 的实际对象是 StandardWrapperFacade,到底能获得哪些容器信息可以看看这类提供了哪些接口。还有一部分数据是由 ServletRequest 类提供,它的实际对象是 RequestFacade,从提供的方法中发现主要是描述这次请求的 HTTP 协议的信息。所以要掌握 Servlet 的工作方式必须要很清楚 HTTP 协议,如果你还不清楚赶紧去找一些参考资料。关于这一块还有一个让很多人迷惑的 Session 与 Cookie。

  Session 与 Cookie 不管是对 Java Web 的熟练使用者还是初学者来说都是一个令人头疼的东西。Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。它们有各自的优点也有各自的缺陷。然而具有讽刺意味的是它们优点和它们的使用场景又是矛盾的,例如使用 Cookie 来传递信息时,随着 Cookie 个数的增多和访问量的增加,它占用的网络带宽也很大,试想假如 Cookie 占用 200 个字节,如果一天的 PV 有几亿的时候,它要占用多少带宽。所以大访问量的时候希望用 Session,但是 Session 的致命弱点是不容易在多台服务器之间共享,所以这也限制了 Session 的使用。

  不管 Session 和 Cookie 有什么不足,我们还是要用它们。下面详细讲一下,Session 如何基于 Cookie 来工作。实际上有三种方式能可以让 Session 正常工作:

  1. 基于 URL Path Parameter,默认就支持


  2. 基于 Cookie,如果你没有修改 Context 容器个 cookies 标识的话,默认也是支持的


  3. 基于 SSL,默认不支持,只有 connector.getAttribute("SSLEnabled") 为 TRUE 时才支持

  第一种情况下,当浏览器不支持 Cookie 功能时,浏览器会将用户的 SessionCookieName 重写到用户请求的 URL 参数中,它的传递格式如 /path/Servlet;name=value;name2=value2? Name3=value3,其中“Servlet;”后面的 K-V 对就是要传递的 Path Parameters,服务器会从这个 Path Parameters 中拿到用户配置的 SessionCookieName。关于这个 SessionCookieName,如果你在 web.xml 中配置 session-config 配置项的话,其 cookie-config 下的 name 属性就是这个 SessionCookieName 值,如果你没有配置 session-config 配置项,默认的 SessionCookieName 就是大家熟悉的“JSESSIONID”。接着 Request 根据这个 SessionCookieName 到 Parameters 拿到 Session ID 并设置到 request.setRequestedSessionId 中。

Bitte beachten Sie, dass Tomcat weiterhin die Sitzungs-ID im Cookie analysiert und die Sitzungs-ID in der URL überschreibt, wenn der Client auch Cookies unterstützt.

Im dritten Fall wird die Sitzungs-ID entsprechend dem Attributwert javax.servlet.request.ssl_session festgelegt.

Mit der Sitzungs-ID kann der Server ein HttpSession-Objekt erstellen. Der erste Auslöser erfolgt über die Methode „getSession()“. Wenn die aktuelle Sitzungs-ID kein entsprechendes HttpSession-Objekt hat, fügen Sie dieses Objekt zu „Gespeichert in“ hinzu Der Sitzungscontainer von org.apache.catalina Manager verwaltet den Lebenszyklus aller Sitzungen, wenn sie ablaufen, der Server wird heruntergefahren und Sitzungen werden auf die Festplatte serialisiert usw. Solange dieses HttpSession-Objekt vorhanden ist, kann der Benutzer dieses Objekt basierend auf der Sitzungs-ID abrufen und so den Status beibehalten.

Abbildung 11. Sitzungsbezogenes Klassendiagramm

图 11.Session 相关类图

Wie aus der obigen Abbildung ersichtlich ist, ist das von request.getSession erhaltene HttpSession-Objekt tatsächlich das Fassadenobjekt des StandardSession-Objekts. Dies ist das gleiche Prinzip wie beim vorherigen Request und Servlet. Die folgende Abbildung ist das Zeitdiagramm der Sitzungsarbeit:

Abbildung 12. Zeitdiagramm der Sitzungsarbeit (großes Bild anzeigen)

图 12.Session 工作的时序图

Ein weiterer Punkt ist, dass sich die mit der Sitzung verknüpften Cookies nicht von anderen Cookies unterscheiden. Die Konfiguration dieser Konfiguration kann über das Konfigurationselement session-config in web.xml angegeben werden.


Listener

im Servlet Listener wird im gesamten Tomcat-Server häufig verwendet. Das Design von Listener bietet eine schnelle Möglichkeit zur Entwicklung von Servlet-Anwendungen und kann problemlos Programme und Daten aus einer anderen vertikalen Dimension steuern. Derzeit stellt Servlet 5 Beobachterschnittstellen für zwei Arten von Ereignissen bereit: 4 EventListeners-Typen, ServletContextAttributeListener, ServletRequestAttributeListener, ServletRequestListener, HttpSessionAttributeListener und 2 LifecycleListeners-Typen, ServletContextListener, HttpSessionListener. Wie im Bild unten gezeigt:

Abbildung 13. Listener im Servlet (größeres Bild anzeigen)

图 13.Servlet 中的 Listener

Sie decken grundsätzlich jedes Ereignis ab, das Sie während des gesamten Servlet-Lebenszyklus interessiert. Die Implementierungsklassen dieser Listener können im Tag eae7f28921fa176b20e23abb843be090 konfiguriert werden. Natürlich können Listener auch dynamisch in der Anwendung hinzugefügt werden. Es ist zu beachten, dass ServletContextListener nach dem Start des Containers keine neuen hinzufügen kann, da die Ereignisse, auf die er lauscht, nicht mehr angezeigt werden. Die Beherrschung der Verwendung dieser Listener kann unser Programmdesign flexibler machen.


Zusammenfassung

Dieser Artikel enthält viele Details. In diesem Artikel wird versucht, einige wichtige Punkte vom Start des Servlet-Containers bis zur Servlet-Architektur zu finden Ich hoffe, dass es für alle hilfreich ist, den Lesern ein umfassendes und vollständiges Strukturdiagramm zur Verfügung zu stellen und auch einige schwierige Themen im Detail zu analysieren.

Das obige ist der detaillierte Inhalt vonAnalyse des Servlet-Funktionsprinzips. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn