Maison  >  Article  >  Java  >  Analyse du principe de fonctionnement du Servlet

Analyse du principe de fonctionnement du Servlet

巴扎黑
巴扎黑original
2017-04-30 10:01:161164parcourir

La technologie Web est devenue aujourd'hui l'une des technologies d'application Web Internet les plus répandues, et Servlet constitue le fondement de la technologie Web Java. Par conséquent, maîtriser le principe de fonctionnement de Servlet est une condition de base pour devenir un développeur qualifié de technologie Web Java. Cet article vous amènera à comprendre comment fonctionne la technologie Web Java basée sur les Servlets. Vous saurez : Prendre Tomcat comme exemple pour comprendre le fonctionnement des conteneurs de Servlets ? Comment démarre-t-on un projet Web dans un conteneur Servlet ? Comment le conteneur de servlet analyse-t-il le servlet que vous définissez dans web.xml ? Comment les requêtes des utilisateurs sont-elles affectées aux servlets spécifiés ? Comment le conteneur Servlet gère-t-il le cycle de vie du Servlet ? Vous découvrirez également la hiérarchie des classes de la dernière API Servlet, ainsi que l'analyse de certains problèmes difficiles dans Servlet.

À partir du conteneur Servlet

Pour présenter Servlet, nous devons d'abord expliquer clairement le conteneur Servlet. La relation entre Servlet et le conteneur Servlet est un peu comme la relation entre une arme à feu et des balles. L'arme est faite pour les balles, et les balles rendent l'arme mortelle. Bien qu’interdépendants, ils se développent indépendamment les uns des autres, tous résultant de l’adaptation à la production industrielle. D'un point de vue technique, il s'agit de se découpler et de collaborer les uns avec les autres via des interfaces standardisées. Puisque les interfaces sont la clé pour connecter les servlets et les conteneurs de servlets, commençons par leurs interfaces.

Comme mentionné précédemment, le conteneur Servlet est un produit standardisé développé de manière indépendante. Il en existe actuellement de nombreux types, mais ils ont tous leur propre positionnement sur le marché. Il est difficile de dire qui est le meilleur et qui est le pire. Par exemple, le populaire Jetty a fait de bons progrès dans le domaine de la personnalisation et du mobile. Nous prendrons ici Tomcat, qui est le plus connu de tous, comme exemple pour présenter comment le conteneur Servlet gère les Servlets. Tomcat lui-même est également très complexe. Nous commencerons uniquement par l'interface entre le servlet et le conteneur de servlet. Pour une introduction détaillée à Tomcat, veuillez vous référer à mon autre article "Architecture du système Tomcat et analyse de la conception de modèles".

Au niveau du conteneur de Tomcat, le conteneur Context est la classe wrapper Wrapper qui gère directement le servlet dans le conteneur, donc la façon dont le conteneur Context s'exécute affectera directement le fonctionnement du servlet.

Figure 1. Modèle de conteneur Tomcat

图 1 . Tomcat 容器模型

Comme le montre la figure ci-dessus, les conteneurs de Tomcat sont divisés en quatre niveaux. Le conteneur qui gère réellement les Servlets est le conteneur de contexte. Un contexte correspond à un projet Web. Celui-ci peut être facilement trouvé dans le fichier de configuration de Tomcat, comme suit :

Liste 1 Paramètres de configuration du contexte

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

Ce qui suit est une introduction détaillée au processus d'analyse du conteneur de contexte par Tomcat, y compris comment créer un servlet.

Processus de démarrage du conteneur de servlets

Tomcat7 a également commencé à prendre en charge les fonctions intégrées et a ajouté une classe de démarrage org.apache.catalina.startup.Tomcat. Tomcat peut être facilement démarré en créant un objet instance et en appelant la méthode start. Nous pouvons également utiliser cet objet pour ajouter et modifier les paramètres de configuration de Tomcat, comme l'ajout dynamique de Contexte, de Servlet, etc. Ensuite, nous utiliserons cette classe Tomcat pour gérer un nouveau conteneur de contexte. Nous sélectionnerons les exemples de projet Web fournis avec Tomcat7 et verrons comment il est ajouté à ce conteneur de contexte.

Liste 2. Ajouter un projet Web à Tomcat

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

Le code du listing 1 consiste à créer une instance Tomcat et à ajouter une application Web, puis à démarrer Tomcat et à appeler l'un des servlets HelloWorldExample pour voir si les données attendues sont renvoyées correctement.

Le code de la méthode addWebapp de Tomcat est le suivant :

Liste 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; 
 }

Nous avons déjà introduit qu'une application Web correspond à un conteneur de contexte, qui est le conteneur de servlet lorsque le servlet est en cours d'exécution. Lors de l'ajout d'une application Web, un conteneur StandardContext sera créé et les paramètres nécessaires seront définis pour le conteneur de contexte. L'URL et le chemin représentent respectivement l'application. Le chemin d'accès dans Tomcat et le chemin physique réel de cette application sont cohérents avec les deux paramètres du listing 1. La configuration la plus importante est ContextConfig. Cette classe sera chargée d'analyser l'intégralité de la configuration de l'application Web, qui sera présentée en détail ultérieurement. Enfin, ajoutez ce conteneur Context au conteneur parent Host.

  接下去将会调用 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 到底起到什么作用了。


Créer une instance de servlet

Le travail d'analyse du servlet a été terminé précédemment, et il a été empaqueté dans un StandardWrapper et ajouté au conteneur Context, mais il ne peut toujours pas fonctionner pour nous car il n'a pas été instancié. Ci-dessous, nous présenterons comment l'objet Servlet est créé et comment il est initialisé.

Créer un objet Servlet

Si l'élément de configuration de chargement au démarrage du servlet est supérieur à 0, il sera instancié au démarrage du conteneur de contexte. Comme mentionné précédemment, le globalWebXml par défaut sera lu lors de l'analyse du fichier de configuration, qui se trouve dans le Web. fichier xml sous conf Certains éléments de configuration par défaut sont définis, qui définissent deux servlets, à savoir : org.apache.catalina.servlets.DefaultServlet et org.apache.jasper.servlet.JspServlet. Leur charge au démarrage est respectivement de 1 et 3. Autrement dit, ces deux servlets seront démarrés au démarrage de Tomcat.

La méthode de création d'une instance de Servlet commence par Wrapper.loadServlet. Ce que la méthode loadServlet doit accomplir est d'obtenir la servletClass, puis de la donner à InstanceManager pour créer un objet basé sur servletClass.class. Si ce servlet est configuré avec un fichier jsp, alors cette servletClass est org.apache.jasper.servlet.JspServlet défini dans conf/web.xml.

Le diagramme de structure de classe pertinent pour créer un objet Servlet est le suivant :

Figure 3. Structure de classe associée pour la création d'un objet Servlet

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

Initialiser le servlet

Initialisez le servlet dans la méthode initServlet de StandardWrapper. Cette méthode consiste simplement à appeler la méthode init du servlet et en même temps à transmettre le StandardWrapperFacade qui encapsule l'objet StandardWrapper au servlet en tant que ServletConfig. La raison pour laquelle le conteneur Tomcat transmet StandardWrapperFacade à l'objet Servlet sera analysée en détail ultérieurement.

Si le Servlet est associé à un fichier jsp, alors le JspServlet est initialisé au préalable. Ensuite, une simple requête sera simulée pour appeler le fichier jsp afin de compiler le fichier jsp dans une classe et initialiser la classe.

De cette façon, l'objet Servlet est initialisé. En fait, le processus est analysé par le Servlet dans les comportements de contrôle et de jugement où des erreurs imprévisibles se produisent, etc. Ici, nous nous concentrons uniquement sur quelques liens clés à expliquer, en essayant de donner à chacun un contexte global.

Ce qui suit est un chronogramme complet de ce processus, avec certains détails omis.

Figure 4. Diagramme de séquence d'initialisation du servlet (voir l'image agrandie)

图 4. 初始化 Servlet 的时序图


Architecture des servlets

Nous savons que les applications Web Java s'exécutent sur la base de la spécification Servlet, alors comment le Servlet lui-même s'exécute-t-il ? Pourquoi concevoir une telle architecture.

Figure 5. Diagramme d'association de classes de niveau supérieur de servlet

图 5.Servlet 顶层类关联图

Comme le montre la figure ci-dessus, la spécification Servlet fonctionne sur la base de ces classes. Il existe trois classes automatiquement associées à Servlet, à savoir ServletConfig, ServletRequest et ServletResponse. Ces trois classes sont transmises au Servlet via le conteneur. ServletConfig est transmis au Servlet lorsque le Servlet est initialisé, et les deux dernières sont transmises lorsque le Servlet est appelé lorsque la requête arrive. Nous comprenons clairement l'importance de ServletRequest et ServletResponse dans le fonctionnement du Servlet, mais quelle est la valeur de ServletConfig et ServletContext pour Servlet ? Un examen plus approfondi des méthodes déclarées dans l'interface ServletConfig montre que ces méthodes sont toutes utilisées pour obtenir certaines propriétés de configuration de ce servlet, et que ces propriétés de configuration peuvent être utilisées lorsque le servlet est en cours d'exécution. Et que fait ServletContext ? Le mode d'exécution de Servlet est un mode d'exécution typique de type « poignée de main interactive ». Ce que l'on appelle « l'interaction de type poignée de main » signifie que les deux modules préparent généralement un scénario de transaction afin d'échanger des données, et ce scénario suit le processus de transaction jusqu'à ce que la transaction soit terminée. L'initialisation de ce scénario de transaction est personnalisée en fonction des paramètres spécifiés par l'objet de transaction. Ces paramètres spécifiés sont généralement une classe de configuration. Vous avez donc raison, le scénario de transaction est décrit par ServletContext et le jeu de paramètres personnalisés est décrit par ServletConfig. ServletRequest et ServletResponse sont les objets spécifiques avec lesquels interagir. Ils sont généralement utilisés comme outils de transport pour fournir des résultats d'interaction.

ServletConfig est transmis depuis le conteneur lors de l'initialisation du servlet, alors qu'est-ce que ServletConfig exactement ?

La figure suivante est le diagramme des relations de classes de ServletConfig et ServletContext dans le conteneur Tomcat.

 Figure 6. Schéma d'association de classes de ServletConfig dans le conteneur

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

Comme le montre la figure ci-dessus, StandardWrapper et StandardWrapperFacade implémentent l'interface ServletConfig, et StandardWrapperFacade est la classe de façade StandardWrapper. Ainsi, ce qui est transmis au Servlet est l'objet StandardWrapperFacade. Cette classe peut garantir que les données spécifiées par ServletConfig sont obtenues à partir du StandardWrapper sans exposer les données dont ServletConfig ne se soucie pas au Servlet.

De même, ServletContext a également une structure similaire à ServletConfig. L'objet réel de ServletContext qui peut être obtenu dans Servlet est également un objet ApplicationContextFacade. ApplicationContextFacade garantit également que ServletContex ne peut obtenir que les données qu'il doit obtenir du conteneur. Ils jouent tous un rôle dans l'encapsulation des données et utilisent tous le modèle de conception de façade.

Grâce à ServletContext, vous pouvez obtenir certaines informations nécessaires dans le conteneur Context, telles que le chemin de travail de l'application, la version minimale de Servlet prise en charge par le conteneur, etc.

Quels sont les objets réels des deux ServletRequest et ServletResponse définis dans Servlet ? , lorsque nous créons nos propres classes Servlet, nous utilisons généralement HttpServletRequest et HttpServletResponse, qui héritent de ServletRequest et ServletResponse. Pourquoi les ServletRequest et ServletResponse transmis par le conteneur Context peuvent-ils être convertis en HttpServletRequest et HttpServletResponse ?

Figure 7. Diagramme de structure de classe associé à la demande

图 7.Request 相关类结构图

L'image ci-dessus est le diagramme de structure de classe de Request et Response créé par Tomcat. Une fois que Tomcat reçoit une requête, il créera d'abord org.apache.coyote.Request et org.apache.coyote.Response. Ces deux classes sont utilisées en interne par Tomcat pour décrire une requête et les classes d'informations correspondantes. La fonction consiste à allouer rapidement la requête aux threads suivants pour traitement après une simple analyse après que le serveur a reçu la requête, de sorte que leurs objets sont très petits et peuvent être facilement recyclés par la JVM. Ensuite, lorsqu'il est transmis à un thread utilisateur pour gérer la demande, les objets org.apache.catalina.connector.Request et org.apache.catalina.connector.Response sont créés. Ces deux objets parcourent tout le conteneur du servlet jusqu'à ce qu'ils soient transmis au servlet. Ce sont les classes de façade RequestFacade et RequestFacade de Request et Response. Le mode façade utilisé ici est basé sur le même objectif qu'auparavant : to. encapsuler les données dans le conteneur. La transformation de classe de Request et Response correspondant à une requête est comme indiqué dans la figure ci-dessous :

Figure 8. Le processus de transformation de la demande et de la réponse

图 8.Request 和 Response 的转变过程


Comment fonctionne Servlet

Nous avons déjà compris comment le Servlet est chargé, comment le Servlet est initialisé et l'architecture du Servlet. Maintenant, la question est de savoir comment il est appelé.

Lorsque l'utilisateur lance une requête du navigateur vers le serveur, elle contient généralement les informations suivantes : http://hostname : port /contextpath/servletpath Le nom d'hôte et le port sont utilisés pour établir une connexion TCP avec le serveur, ainsi que les éléments suivants. L'URL est utilisée pour sélectionner Ce sous-conteneur sur le serveur répond à la demande de l'utilisateur. Alors, comment le serveur atteint-il le bon conteneur de servlet en fonction de cette URL ?

Ce problème est facile à résoudre dans Tomcat7.0, car il existe une classe spéciale pour effectuer ce travail de mappage. Il s'agit de org.apache.tomcat.util.http.mapper. Cette classe enregistre tous les enfants dans le conteneur Container de Tomcat. , lorsque la classe org.apache.catalina.connector.Request entre dans le conteneur Container, le mappeur définit les conteneurs d'hôte et de contexte sur l'attribut mappingData de la requête en fonction du nom d'hôte et du chemin de contexte de cette requête. Ainsi, avant que la requête n'entre dans le conteneur Container, le sous-conteneur auquel elle souhaite accéder a déjà été déterminé.

Figure 9. Diagramme de classes du mappeur de Request

图 9.Request 的 Mapper 类关系图

Peut-être avez-vous des questions : comment peut-il y avoir une relation complète entre les conteneurs dans le mappeur ? Cela remonte au processus d'initialisation en 19 étapes de la classe MapperListener dans la figure 2. Voici le code de la méthode d'initialisation de MapperListener :

Liste 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 中。

Veuillez noter que si le client prend également en charge les cookies, Tomcat analysera toujours l'ID de session dans le cookie et écrasera l'ID de session dans l'URL.

Si c'est le troisième cas, l'ID de session sera défini en fonction de la valeur de l'attribut javax.servlet.request.ssl_session.

Avec l'ID de session, le serveur peut créer un objet HttpSession. Le premier déclencheur se fait via la méthode getSession(). Si l'ID de session actuel n'a pas d'objet HttpSession correspondant, créez-en un nouveau et ajoutez cet objet à Saved in. le conteneur de sessions de org.apache.catalina. Manager, la classe Manager gérera le cycle de vie de toutes les sessions qui seront recyclées à leur expiration, le serveur sera arrêté et les sessions seront sérialisées sur le disque, etc. Tant que cet objet HttpSession existe, l'utilisateur peut obtenir cet objet en fonction de l'ID de session, conservant ainsi l'état.

Figure 11. Diagramme de classes lié à la session

图 11.Session 相关类图

Comme le montre la figure ci-dessus, l'objet HttpSession obtenu à partir de request.getSession est en fait l'objet de façade de l'objet StandardSession. C'est le même principe que les Request et Servlet précédents. La figure suivante est le chronogramme du travail de la Session :

Figure 12. Chronogramme des travaux de la session (voir l'image en grand)

图 12.Session 工作的时序图

Un autre point est que les cookies associés à la session ne sont pas différents des autres cookies. La configuration de cette configuration peut être spécifiée via l'élément de configuration session-config dans web.xml.


Auditeur

dans Servlet Listener est largement utilisé dans l'ensemble du serveur Tomcat. Il est conçu sur la base du modèle d'observateur. La conception de Listener fournit un moyen rapide de développer des applications Servlet et peut facilement contrôler des programmes et des données depuis une autre dimension verticale. Actuellement, Servlet fournit 5 interfaces d'observateur pour deux types d'événements, à savoir : 4 types EventListeners, ServletContextAttributeListener, ServletRequestAttributeListener, ServletRequestListener, HttpSessionAttributeListener et 2 types LifecycleListeners, ServletContextListener, HttpSessionListener. Comme indiqué ci-dessous :

Figure 13. Écouteur dans le servlet (voir l'image agrandie)

图 13.Servlet 中的 Listener

Ils couvrent essentiellement tous les événements qui vous intéressent pendant tout le cycle de vie du servlet. Ces classes d'implémentation d'écouteur peuvent être configurées dans la balise eae7f28921fa176b20e23abb843be090 Bien entendu, des Listeners peuvent également être ajoutés dynamiquement dans l'application. Il est à noter que ServletContextListener ne peut pas en ajouter de nouveaux après le démarrage du conteneur, car les événements qu'il écoute n'apparaîtront plus. Maîtriser l'utilisation de ces auditeurs peut rendre la conception de notre programme plus flexible.


Résumé

Cet article implique beaucoup de contenu. Il semble impossible d'expliquer clairement chaque détail. Cet article tente de trouver quelques points clés depuis le démarrage du conteneur Servlet jusqu'à l'initialisation du Servlet, ainsi que l'architecture du Servlet. aux lecteurs d'avoir un diagramme de structure global et complet, et analyse également certaines questions difficiles en détail. J'espère qu'il sera utile à tout le monde.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn