【Tomcat】《How Tomcat Works》英文版GPT翻译(第十一章)

2024-01-19 17:19:28 浏览数 (3)

Part1Chapter 11: StandardWrapper( 第 11 章:标准包装器)

You have learned in Chapter 5 that there are four types of containers: engine, host, context, and wrapper. You have also built your own simple contexts and wrappers in previous chapters. A context normally has one or more wrappers, in which each wrapper represents a servlet definition. This chapter will now look at the standard implementation of the Wrapper interface in Catalina. It starts by explaining the sequence of methods that get invoked for each HTTP request and continues with the javax.servlet.SingleThreadModel interface. The chapter concludes by explaining the StandardWrapper and StandardWrapperValve classes. The application that accompanies this chapter uses StandardWrapper instances to represents servlets.

在第5章中,您已经了解到有四种类型的容器:引擎(engine)、主机(host)、上下文(context)和包装器(wrapper)。

在之前的章节中,您还构建了自己的简单上下文和包装器。

一个上下文通常有一个或多个包装器,其中每个包装器代表一个servlet定义。

本章将介绍CatalinaWrapper接口的标准实现。

它从解释每个HTTP请求调用的方法序列开始,并继续介绍 javax.servlet.SingleThreadModel 接口。

本章最后解释了StandardWrapperStandardWrapperValve类。

本章附带的应用程序使用 StandardWrapper 实例来表示 servlet

Part2Sequence of Methods Invocation(方法调用顺序)

For each incoming HTTP request, the connector calls the invoke method of the associated container. The container will then call the invoke methods of all its child containers. For example, if the connector is associated with an instance of StandardContext, the connector will call the invoke method of the StandardContext instance, which then call the invoke methods of all its child containers (in this case, the child containers will be of type StandardWrapper). Figure 11.1 explains what happens when the connector receives an HTTP request. (Recall from Chapter 5 that a container has a pipeline with one or more valves.)

对于每个传入的HTTP请求,连接器调用关联容器的 invoke 方法。

容器将调用其所有子容器的 invoke 方法。

例如,如果连接器与 StandardContext 实例相关联,则连接器将调用 StandardContext 实例的 invoke 方法,然后调用其所有子容器的 invoke 方法(在这种情况下,子容器的类型将为 StandardWrapper)。

图11.1解释了当连接器接收到HTTP请求时会发生什么。

(回想一下第5章,容器具有一个具有一个或多个阀门的管道。)

image.png

Figure 11.1: The collaboration diagram of methods invocation

图11.1:方法调用的协作图

  • The connector creates the request and response objects
  • The connector calls the StandardContext instance's invoke method.
  • The StandardContext's invoke method in turn calls the invoke method of the context's pipeline. The basic valve in a StandardContext's pipeline is a StandardContextValve, therefore the StandardContext's pipeline calls the StandardContextValve's invoke method.
  • The StandardContextValve's invoke method obtains the appropriate wrapper to serve the request and calls the wrapper's invoke method.
  • The StandardWrapper is the standard implementation of a wrapper. The StandardWrapper instance's invoke method calls its pipeline's invoke method.  The basic valve in the StandardWrapper's pipeline is a StandardWrapperValve. Therefore, the invoke method of the StandardWrapperValve is called. The invoke method of the StandardWrapperValve calls the wrapper's allocate method to obtain an instance of the servlet.
  • The allocate method calls the load method to load the servlet, if the servlet needs to be loaded.
  • The load method calls the servlet's init method.
  • The StandardWrapperValve calls the servlet's service method.
  • 连接器创建请求和响应对象
  • 连接器调用 StandardContext 实例的invoke方法。
  • StandardContext 的i nvoke 方法再调用上下文的管道的invoke方法。
    • StandardContext 的管道中的基本阀门是 StandardContextValve,因此 StandardContext 的管道调用 StandardContextValveinvoke 方法。
  • StandardContextValveinvoke 方法获取适当的包装器来处理请求,并调用包装器 的 invoke 方法。
  • StandardWrapper 是包装器的标准实现。StandardWrapper 实例的 invoke 方法调用其管道的 invoke 方法。
  • StandardWrapper 的管道中的基本阀门是 StandardWrapperValve
    • 因此,调用 StandardWrapperValveinvoke 方法。StandardWrapperValveinvoke 方法调用包装器的 allocate 方法来获取一个 servlet 的实例。
  • allocate 方法调用 load 方法来加载 servlet,如果需要加载 servlet 的话。
  • load 方法调用 servletinit 方法。
  • StandardWrapperValve 调用 servletservice 方法。

Note The StandardContext class's constructor sets an instance of StandardContextValve as its basic valve:

注意:StandardContext 类的构造函数将 StandardContextValve 的实例设置为其基本阀门。

代码语言:javascript复制
publicStandardContext() {
super();
pipeline.setBasic(new StandardContextValve());
namingResources.setContainer(this);
}
Note The StandardWrapper class's constructor sets an instance of 
StandardWrapperValve as its basic valve:
public StandardWrapper() {
super();
pipeline.setBasic(new StandardWrapperValve());
}

In this chapter we are interested in the details on how a servlet is invoked. We will therefore look at the StandardWrapper and StandardWrapperValve classes. Before we do that, however, let's look at the javax.servlet.SingleThreadModel interface first. Understanding this interface is crucial in understanding how a wrapper loads a servlet.

在本章中,我们关注的是如何调用 servlet 的细节。

因此,我们将研究 StandardWrapper 和 StandardWrapperValve 类。

不过,在此之前,我们先来看看javax.servlet.SingleThreadModel接口。

了解这个接口对于理解封装程序如何加载 servlet 至关重要。

Part3SingleThreadModel(单线程模型)

A servlet can implement the javax.servlet.SingleThreadModel interface, and a servlet implementing this interface is colloquially called a SingleThreadModel (STM) servlet. According to the Servlet specification, the purpose of implementing this interface is to guarantee that the servlet handles only one request at a time. Quoting the section SRV.14.2.24 of the Servlet 2.4 specification (Servlet 2.3 has a similar explanation on the SingleThreadModel interface):

一个Servlet可以实现 javax.servlet.SingleThreadModel 接口,实现了这个接口的 Servlet 通常被称为 SingleThreadModelSTMServlet

根据 Servlet 规范,实现这个接口的目的是确保 Servlet 一次只处理一个请求。

引用Servlet 2.4规范(Servlet 2.3对SingleThreadModel接口有类似的解释)的SRV.14.2.24节:

If a servlet implements this interface, you are guaranteed that no two threads will execute concurrently in a servlet's service method. The servlet container can guarantee this by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet. This interface does not prevent synchronization problems that result from servlets accessing shared resources such as static class variables or classes outside the scope of the servlet.

如果一个 Servlet 实现了这个接口,你可以保证在 Servletservice 方法中不会有两个线程同时执行。

Servlet 容器可以通过同步访问 Servlet 的单个实例或维护一个 Servlet 实例池并将每个新请求分派给一个空闲的 Servlet 来保证这一点。

这个接口无法防止由于 Servlet 访问共享资源(如静态类变量或 Servlet 范围之外的类)而导致的同步问题。

Many programmers do not read this carefully, and think that implementing SingleThreadModel will make their servlet thread-safe. This is not the case. Read the quotation above once again.

很多程序员没有仔细阅读这一点,认为实现 SingleThreadModel 会使他们的 Servlet 线程安全。

事实并非如此,请再次阅读上面的引述。

It is true that by implementing SingleThreadModel no two threads will execute a servlet's service method at the same time. However, to enhance performance the servlet container can create multiple instances of an STM servlet. That means, the STM servlet's service method can be executed concurrently in different instances. This will introduce synchronization problems if the servlet need to access static class variables or other resources outside the class.

实现 SingleThreadModel 确实可以保证在同一时间内没有两个线程执行一个 Servletservice 方法。

然而,为了提高性能,Servlet 容器可以创建多个STM Servlet的实例。

这意味着,在不同的实例中,STM Servletservice 方法可以并发执行。

如果 Servlet 需要访问静态类变量或其他类之外的资源,这将引入同步问题。

False Sense of Multi-Thread Safety

错误的多线程安全意识

The SingleThreadModel interface is deprecated in Servlet 2.4 because it gives servlet programmers false sense of multi-thread safety. However, both Servlet 2.3 and Servlet 2.4 containers must still support this interface.

Servlet 2.4中已经弃用了 SingleThreadModel 接口,因为它给 Servlet 程序员带来了多线程安全的虚假感觉。

然而,无论是Servlet 2.3还是Servlet 2.4的容器都必须继续支持这个接口。

Note You can find an interesting discussion about SingleThreadModel on

注意:你可以在链接中找到关于 SingleThreadModel 的有趣讨论。

http://w4.metronet.com/~wjm/tomcat/ToFeb11/msg02655.html.

(已失效)

Part4StandardWrapper(标准包装)

The main responsibilities of a StandardWrapper object are to load the servlet it represents and allocates an instance of it. The StandardWrapper, however, does not call the servlet's service method. This task is left to the StandardWrapperValve object, the basic valve in the StandardWrapper instance's pipeline. The StandardWrapperValve object obtains the servlet instance from StandardWrapper by calling its allocate method. Upon receiving the servlet instance, the StandardWrapperValve calls the servlet's service method.

StandardWrapper 对象的主要职责是加载它所代表的 servlet 并分配一个实例。

然而,StandardWrapper 不会调用 servletservice 方法,这个任务留给了 StandardWrapperValve 对象,它是 StandardWrapper 实例管道中的基本阀门。

StandardWrapperValve 对象通过调用 StandardWrapperallocate 方法来获取 servlet 实例。

在接收到servlet实例后,StandardWrapperValve 调用 servletservice 方法。

The StandardWrapper loads the servlet class when the servlet is requested the first time. The StandardWrapper instance loads a servlet dynamically, therefore it needs to know the fully qualified name of the servlet class. You tell the StandardWrapper this by passing the servlet class name to the setServletClass method of the StandardWrapper. In addition, you calls its setName method to pass the name the servlet will be referred to.

当第一次请求 servle t时,StandardWrapper 会加载 servlet 类。

StandardWrapper 实例以动态方式加载 servlet,因此需要知道servlet类的完全限定名称。

通过将 servlet 类名传递给 StandardWrappersetServletClass 方法,告诉 StandardWrapper 这一点。

此外,通过调用其 setName 方法传递 servlet 将被引用的名称。

With regard to allocating the servlet instance when the StandardWrapperValve requests it, the StandardWrapper must take into account whether or not the servlet implements the SingleThreadModel interface.

关于在 StandardWrapperValve 请求时分配 servlet 实例,StandardWrapper 必须考虑 servlet 是否实现了 SingleThreadModel 接口。

For a servlet that does not implement the SingleThreadModel interface, StandardWrapper will load the servlet class once and keep returning the same instance for subsequent requests. The StandardWrapper instance does not need multiple instances of the servlet because it is assumed safe to call the servlet's service method from many threads. It is the responsibility of the servlet programmer to synchronize access to a common resource, if necessary.

对于不实现 SingleThreadModel 接口的 servletStandardWrapper 将加载 servlet 类一次,并为后续的请求保持返回相同的实例。

StandardWrapper 实例不需要多个 servlet 实例,因为假设可以从多个线程调用 servletservice 方法是安全的。

如果需要,servlet 程序员有责任同步访问共享资源。

For an STM servlet, things are different. The StandardWrapper instance must guarantee that no two threads will execute concurrently in the STM servlet's service method. If the StandardWrapper were to maintain a single instance of the STM servlet, here is how it would invoke the STM servlet's service method:

对于 STMSingleThreadModelservlet,情况就不同了。

StandardWrapper 实例必须保证在STM servletservice方法中没有两个线程同时执行。

如果 StandardWrapper 维护单个STM servlet的实例,它将如何调用STM servletservice 方法:

代码语言:javascript复制
Servlet instance = <get an instance of the servlet>;
 if ((servlet implementing SingleThreadModel>) {
 synchronized (instance) {
 instance.service(request, response);
 }
 }
 else {
 instance.service(request, response);
 }

However, for the sake of performance, StandardWrapper maintains a pool of STM servlet instances.

不过,为了提高性能,StandardWrapper 会维护一个 STM servlet 实例池。

A wrapper is also responsible for preparing a javax.servlet.ServletConfig instance that can be obtained from inside the servlet. The two next sections discuss the allocation and loading of the servlet.

封装器还负责准备一个 javax.servlet.ServletConfig 实例,该实例可从 servlet 内部获取。

接下来的两节将讨论 servlet 的分配和加载。

1Allocating the Servlet(分配 Servlet)

As mentioned at the beginning of this section, the StandardWrapperValve's invoke method calls the wrapper's allocate method to obtain an instance of the requested servlet. The StandardWrapper class therefore must have the implementation of this method.

如本节开头所述,StandardWrapperValveinvoke 方法调用包装器的 allocate 方法来获取请求的 servlet 实例。

因此,StandardWrapper 类必须实现该方法。

The signature of the allocate method is as follows:

allocate 方法的签名如下:

代码语言:javascript复制
public javax.servlet.Servlet allocate() throws ServletException;

Notice that the allocate method returns an instance of the requested servlet.

请注意,allocate 方法返回的是请求的 servlet 的实例。

The necessity for supporting STM servlets makes the allocate method a bit more complex. In fact, there are two parts in the allocate method, one to cater for non-STM servlets and the other for STM servlets. The first part has the following skeleton.

由于必须支持 STM servlets,分配方法就变得复杂了一些。

事实上,allocate 方法有两个部分,一个是非 STM servlets,另一个是 STM servlets。第一部分的骨架如下。

代码语言:javascript复制
if (!singleThreadModel) {
 // returns a non-STM servlet instance
}

The singleThreadModel is a boolean that indicates whether the servlet represented by this StandardWrapper is an STM servlet. The initial value for singleThreadModel is false, but the loadServlet method tests the servlet it is loading and set this boolean if the servlet is a STM servlet. The loadServlet method is explained in the section "Loading the Servlet" below.

singleThreadModel 是一个布尔值,用于指示此 StandardWrapper 所代表的 servlet 是否是 STM servlet

singleThreadModel 的初始值是 false,但 loadServlet 方法会测试正在加载的 servlet,如果该 servletSTM servlet,就会设置该布尔值。

loadServlet 方法将在下文 "加载 Servlet "一节中解释。

The second part of the allocate method is executed if singleThreadModel is true. The skeleton of the second part is as follows:

如果 singleThreadModeltrue,则执行 allocate 方法的第二部分。第二部分的骨架如下:

代码语言:javascript复制
synchronized (instancepool) {
 // returns an instance of the servlet from the pool
}

We'll take a look at the first and the second parts now.

我们现在来看看第一部分和第二部分。

For non-STM servles, the StandardWrapper defines a variable named instance of type javax.servlet.Servlet:

对于非 STM 服务,StandardWrapper 定义了一个变量,名为javax.servlet.Servlet类型的实例:

代码语言:javascript复制
private Servlet instance = null;

The allocate method checks if instance is null. If it is, the allocate method calls the loadServlet method to load the servlet. It then increments the countAllocated integer and returns the instance.

allocate 方法会检查实例是否为空。如果为空,allocate 方法会调用 loadServlet 方法加载 servlet

然后递增 countAllocated 整数并返回实例。

代码语言:javascript复制
if (!singleThreadModel) {
 // Load and initialize our instance if necessary
 if (instance == null) {
 synchronized (this) {
 if (instance == null) {
 try {
 instance = loadServlet();
 }
 catch (ServletException e) {
 throw e;
 }
 catch (Throwable e) {
 throw new ServletException
 (sm.getString("standardWrapper.allocate"), e);
 }
 }
 }
 }
 if (!singleThreadModel) {
 if (debug >= 2)
 log(" Returninq non-STM instance");
 countAllocated  ;
 return (instance);
 }
 }

If the servlet represented by StandardWrapper is a STM servlet, the allocate method attempt to return an instance from a pool. The instancePool variable of type java.util.Stack references to a stack of STM servlet instances.

如果 StandardWrapper 所代表的 servlet STM servlet,则分配方法会尝试从池中返回一个实例。

instancePool 变量的类型为 java.util.Stack,指向 STM servlet 实例堆栈。

代码语言:javascript复制
private Stack instancePool = null;

This variable is instantiated inside the loadServlet method, which we -will discuss in the next section.

我们将在下一节讨论 loadServlet 方法,该变量将在 loadServlet 方法中实例化。

The allocate method will allocate an instance of the STM servlet as long as the number of instances does not exceed the specified maximum number. The maxInstances integer holds the maximum number of STM instances and by default its value is 20.

只要实例数量不超过指定的最大数量,分配方法就会分配一个 STM servlet 实例。

maxInstances 整数表示 STM 实例的最大数量,默认值为 20。

代码语言:javascript复制
private int maxInstances = 20;

To track down the current number of STM instances, the StandardWrapper class uses the nInstances integer:

为了追踪当前 STM 实例的数量,StandardWrapper 类使用了 nInstances 整数:

代码语言:javascript复制
private int nInstances = 0;

Here is the second part of the allocate method.

下面是分配方法的第二部分。

代码语言:javascript复制
    synchronized (instancePool) {
        while (countAllocated >= nInstances) {
            // Allocate a new instance if possible, or else wait
            if (nInstances < maxInstances) {
                try {
                    instancePool.push(loadServlet());
                    nInstances  ;
                }
                catch (ServletException e) {
                    throw e;
                }
                catch (Throwable e) {
                    throw new ServletException
                            (sm.getString("StandardWrapper.allocate"), e);
                }
            }
            else {
                try {
                    instancePool.wait();
                }
                catch (InterruptedException e) {
                    ;
                }
            }
        }
        if (debug >= 2)
            log(" Returning allocated STM instance");
        countAllocated  ;
        return (Servlet) instancePool.pop();
    }

The code above uses a while loop to wait until nInstances is less than or equal to the value of countAllocated. Inside the while loop, the allocate method checks the value of nInstance, and if it is lower than maxInstances, the allocate method calls the loadServlet method and pushes the new instance to the pool and increments nInstances. If the number of nInstance is equal to maxInstance or greater, it waits by calling the instancePool stack's wait method, waiting for an instance to be returned to the stack.

上面的代码使用 while 循环来等待,直到 nInstances 小于或等于 countAllocated 的值。

while 循环内部,allocate 方法检查 nInstance 的值,如果它小于 maxInstances,则调用 loadServlet 方法并将新实例推入池中,并增加 nInstances 的值。

如果nInstance的数量等于或大于maxInstance,则通过调用instancePool堆栈的wait方法进行等待,等待实例返回到堆栈中。

2Loading the Servlet(加载 Servlet

The StandardWrapper class implements the load method of the Wrapper interface. The load method calls the loadServlet method that loads the servlet and calls its init method, passing a javax.servlet.ServletConfig instance. Here is how the loadServlet method works.

StandardWrapper 类实现了 Wrapper 接口的load方法。

load 方法调用 loadServlet 方法来加载 servlet 并调用其 init 方法,传递一个javax.servlet.ServletConfig实例。以下是loadServlet方法的工作原理。

The loadServlet method starts by checking if the current StandardWrapper represents an STM servlet. If it does not and the variable instance is not null (which means the servlet has been previously loaded), it simply returns the instance.

loadServlet 方法首先检查当前的 StandardWrapper 是否表示一个 STM servlet

如果不是,并且变量 instance 不为 null(这意味着 servlet 已经被预先加载),它简单地返回该实例。

代码语言:javascript复制
// Nothing to do if we already have an instance or an instance pool
 if (!singleThreadModel && (instance != null))
 return instance;

If instance is null or it is a STM servlet, it runs the rest of the method.

如果实例为空或为 STM servlet,则运行该方法的其余部分。

First, it captures the output of System.out and System.err, so that it can log any message later by using the log method of javax.servlet.ServletContext.

首先,它会捕获 System.out System.err 的输出,以便以后使用 javax.servlet.ServletContext 的日志方法记录任何信息。

代码语言:javascript复制
PrintStream out = System.out;
 SystemLogHandler.startCapture();

It then defines the variable servlet of type javax.servlet.Servlet. This represents the instance of the loaded servlet that will be returned by the loadServlet method.

然后,它定义了javax.servlet.Servlet类型的变量 servlet

这代表了 loadServlet 方法将返回的已加载 Servlet 的实例。

代码语言:javascript复制
Servlet servlet = null;

The loadServlet method is responsible for loading the servlet class. The name of the class should have been assigned to the servletClass class variable. The method assigns the value of this variable to the String actualClass.

loadServlet 方法负责加载 servlet 类。类的名称应已分配给 servletClass 类变量。

该方法会将该变量的值赋值给字符串 actualClass

代码语言:javascript复制
String actualClass = servletclass;

However, since Catalina is also a JSP container, the loadServlet method must also find out if the request servlet is a JSP page. If it is, the loadServlet method attempts to obtain the actual class for the JSP page.

不过,由于 Catalina 也是一个 JSP 容器,因此 loadServlet 方法还必须查明请求的 servlet 是否是一个 JSP 页面。

如果是,则 loadServlet 方法会尝试获取 JSP 页面的实际类。

代码语言:javascript复制
if ((actualClass == null) && (jspFile != null)) {
 Wrapper jspWrapper = (Wrapper)
 ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
 if (jspWrapper != null)
 actualClass = jspWrapper.getServletClass();
 }

If the name of the servlet for the JSP page cannot be found, the value of the servletclass variable will be used. However, if servletclass has not been set by calling the StandardWrapper class's setServletClass method, an exception will be thrown and the rest of the method will not be executed

如果找不到 JSP 页面的 servlet 名称,将使用 servletclass 变量的值。

但是,如果尚未通过调用 StandardWrapper 类的 setServletClass 方法设置 servletclass,则会出现异常,该方法的其余部分将不会执行

代码语言:javascript复制
// Complain if no servlet class has been specified
 if (actualClass == null) {
 unavailable(null);
 throw new ServletException
 (sm.getString("StandardWrapper.notClass", getName()));
 }

At this stage, the servlet class name has been resolved, so the loadServlet method obtains the loader. If no loader is found, it throws an exception and the method stops here.

在此阶段,Servlet 类名已被解析,因此 loadServlet 方法会获取加载器。

如果找不到加载器,就会抛出异常,方法到此为止。

代码语言:javascript复制
// Acquire an instance of the class loader to be used
 Loader loader = getLoader();
 if (loader == null) {
 unavailable(null);
 throw new ServletException
 (sm.getString("StandardWrapper.missingLoader", getName()));
 }

If a loader can be found, the loadServlet method calls its getClassLoader method to obtain a ClassLoader

如果能找到加载器,则 loadServlet 方法会调用其 getClassLoader 方法来获取 ClassLoader

代码语言:javascript复制
ClassLoader classLoader = loader.getClassLoader();

Catalina provides special servlets that belong to the org.apache.catalina package. These special servlets have access to the internal body of the servlet container. If the servlet is a special servlet, i.e. if the isContainerProvidedServlet method returns true, the classLoader variable is assigned another ClassLoader instance, so access to the internal part of Catalina is possible.

Catalina 提供属于 org.apache.catalina 包的特殊 servlet

这些特殊的 servlet 可以访问 servlet 容器的内部主体。

如果 servlet 是特殊 servlet,即 isContainerProvidedServlet 方法返回 trueclassLoader 变量就会被分配给另一个 ClassLoader 实例,这样就可以访问 Catalina 的内部部分。

代码语言:javascript复制
// Special case class loader for a container provided servlet
 if (isContainerProvidedServlet(actualClass)) {
 ClassLoader = this.getClass().getClassLoader();
 log(sm.getString
 ("standardWrapper.containerServlet", getName()));
 }

Having a class loader and the servlet name to load, the loadServlet method can now load the servlet.

有了类加载器和要加载的 servlet 名称,loadServlet 方法就可以加载 servlet 了。

代码语言:javascript复制
// Load the specified servlet class from the appropriate class  
   // loader   Class classClass = null;  
try {  
       if (ClassLoader != null) {  
           System.out.println("Using classLoader.loadClass");  
           classClass = classLoader.loadClass(actualClass);  
       }  
       else {  
           System.out.println("Using forName");  
           classClass = Class.forName(actualClass);  
       }  
   }  
catch (ClassNotFoundException e) {  
       unavailable(null);  
       throw new ServletException  
               (sm.getstring("standardWrapper.missingClass", actualClass), e);  
   }  
if (classClass == null) {  
       unavailable(null);  
       throw new ServletException  
               (sm.getString("standardWrapper.missingClass", actualClass));  
   }

Then, it can instantiate the servlet.

然后,它就可以实例化 servlet

代码语言:javascript复制
// Instantiate and initialize an instance of the servlet class
    // itself
 try {
        servlet = (Servlet) classClass.newInstance();
    }
 catch (ClassCastException e) {
        unavailable(null);
        // Restore the context ClassLoader
        throw new ServletException
                (sm.getString("standardWrapper.notServlet", actualClass), e);
    }
 catch (Throwable e) {
        unavailable(null);
        // Restore the context ClassLoader
        throw new ServletException
                (sm.getstring("standardWrapper.instantiate", actualClass), e);
    }

However, before the loadServlet method initializes the servlet, it checks if loading this servlet is allowed by calling the isServletAllowed method.

不过,在 loadServlet 方法初始化 servlet 之前,它会通过调用 isServletAllowed 方法来检查是否允许加载此 servlet。

代码语言:javascript复制
// Check if loading the servlet in this web application should be
 // allowed
 if (!isServletAllowed(servlet)) {
 throw new SecurityException
 (sm.getString("standardWrapper.privilegedServlet",
 actualClass));
 }

If the security check was passed, it checks if the servlet is a ContainerServlet. A ContainerServlet is a servlet that implements the org.apache.catalina.ContainerServlet interface and has access to Catalina internal functionality. If the servlet is a ContainerServlet, the loadServlet method calls the ContainerServlet's setWrapper method, passing this StandardWrapper instance

如果安全检查通过,则检查 servlet 是否是 ContainerServlet

ContainerServlet 是一种实现了 org.apache.catalina.ContainerServlet 接口并能访问 Catalina 内部功能的 servlet

如果 servletContainerServlet,则 loadServlet 方法会调用 ContainerServletsetWrapper 方法,并传递此 StandardWrapper 实例

代码语言:javascript复制
// Special handling for ContainerServlet instances
 if ((servlet instanceof ContainerServlet) &&
 isContainerProvidedServlet(actualClass)) {
 ((ContainerServlet) servlet).setWrapper(this);
 }

Next, the loadServlet method fires the BEFORE_INIT_EVENT and calls the sender's init method.

接下来,loadServlet 方法会触发 BEFORE_INIT_EVENT,并调用发送者的 init 方法。

代码语言:javascript复制
try {
 instanceSupport.fireInstanceEvent(
 InstanceEvent.BEFORE_INIT_EVENT, servlet);
 servlet.init(facade);


Note that the init method is passed the facade variable that references a javax.servlet.ServletConfig object. Find out how the ServletConfig object is created in the section "Creating ServletConfig" later in this chapter.

请注意,init 方法传递的是引用 javax.servlet.ServletConfig 对象的门面变量。

本章后面的 "创建 ServletConfig "一节将介绍如何创建 ServletConfig 对象。

If the loadOnStartup variable is assigned an integer value and the servlet is a JSP page, call the service method of the servlet as well.

如果 loadOnStartup 变量被赋值为整数且 servlet 是一个 JSP 页面,则也要调用 servletservice 方法。

代码语言:javascript复制
// Invoke jspInit on JSP pages
 if ((loadOnStartup > 0) && (jspFile != null)) {
 // Invoking jspInit
 HttpRequestBase req = new HttpRequestBase();
 HttpResponseBase res = new HttpResponseBase();
 req.setServletPath(jspFile};
 req.setQueryString("jsp_precompile=true");
 servlet.service(req, res);
 }

Next, the loadServlet method fires the AFTER_INIT_EVENT event.

接下来,loadServlet 方法会触发 AFTER_INIT_EVENT 事件。

代码语言:javascript复制
instanceSupport.firelnstanceEvent (InstanceEvent.AFTER_INIT_EVENT,
  servlet);

If the servlet represented by this StandardWrapper object is a STM servlet, the servlet instance will be added to the instance pool. Therefore, if the instancePool variable is still null, it is assigned a Stack object.

如果该 StandardWrapper 对象所代表的 servletSTM servlet,该 servlet 实例将被添加到实例池中。

因此,如果 instancePool 变量仍为空,它将被分配给一个 Stack 对象。

代码语言:javascript复制
// Register our newly initialized instance
 singleThreadModel = servlet instanceof SingleThreadModel;
 if (singleThreadModel) {
 if (instancePool == null)
 instancePool = new Stack();
 }
 fireContainerEvent("load", this);
 }

In the finally block, the loadServlet stops the capturing of the System.out and System.err and logs any message during the loading process to the log method of the ServletContext.

finally 代码块中,loadServlet 停止捕获 System.out System.err,并将加载过程中的任何信息记录到 ServletContext 的日志方法中。

代码语言:javascript复制
finally {
 String log = SystemLogHandler.stopCapture();
 if (log != null && log.length() > 0) {
 if (getServletContext() != null) {
 getServletContext().log(log);
 }
 else {
 out.println(log);
 }
 }
 }

And, finally, the loadServlet method returns the servlet instance.

最后,loadServlet 方法会返回 servlet 实例。

代码语言:javascript复制
 return servlet;

3The ServletConfig Object(ServletConfig 对象)

The loadServlet method of the StandardWrapper class calls the sender's init method after the servlet is loaded. The init method is passed an instance of javax.servlet.ServletConfig. You may be wondering how the StandardWrapper object obtains the ServletConfig object.

StandardWrapper 类的 loadServlet 方法在加载servlet后调用发送者的init方法。

init 方法接收一个javax.servlet.ServletConfig的实例。

你可能想知道 StandardWrapper 对象是如何获取 ServletConfig 对象的。

Look no further than the StandardWrapper class itself. This class implements the javax.servlet.ServletConfig interface, in addition to the Wrapper interface.

不用再往前看了,就在 StandardWrapper 类本身。

这个类实现了javax.servlet.ServletConfig接口,除了Wrapper接口。

The ServletConfig interface has the following four methods: getServletContext, getServletName, getInitParameter, and getInitParameterNames. Let's now see how these methods are implemented in the StandardWrapper class.

ServletConfig 接口有以下四个方法:getServletContextgetServletNamegetInitParametergetInitParameterNames

现在让我们看看这些方法在 StandardWrapper 类中是如何实现的。

Note The StandardWrapper class does not pass itself to the servlet's init method, however. Instead, it wraps itself in a StandardWrapperFacade instance to hide most of its public method from a servlet programmer. See the section "StandardWrapperFacade" after this section. 注意:StandardWrapper类并不直接将自身传递给servlet的init方法。相反,它将自身包装在一个StandardWrapperFacade实例中,以隐藏大部分公共方法,使其对servlet程序员不可见。请参阅本节后面的"StandardWrapperFacade"部分。

getServletContext

The signature of this method is as follows:

该方法的签名如下

代码语言:javascript复制
public ServletContext getServletContext()

A StandardWrapper instance must be a child container of a StandardContext. This is to say, a StandardWrapper's parent is a StandardContext. From the StandardContext object, you can obtain a ServletContext object by calling its getServletContext method. Here is the implementation of the getServletContext method in the StandardWrapper class:

StandardWrapper 实例必须是 StandardContext 的子容器。

也就是说,StandardWrapper 的父对象是 StandardContext

通过调用 StandardContextgetServletContext 方法,可以从 StandardContext 对象获得 ServletContext 对象。

下面是 StandardWrapper 类中 getServletContext 方法的实现:

代码语言:javascript复制
public ServletContext getServletContext() {
 if (parent == null)
 return (null);
 else if (!(parent instanceof Context))
 return (null);
 else
 return (((Context) parent).getServletContext());
}

Note Now you know that it is not possible to deploy a stand alone wrapper that represents a servlet definition. The wrapper must reside in a context so that the ServletConfig object can return a ServletContext instance when its getServletContext method is called. 注意 现在你知道了,不可能部署一个独立的封装器来表示 Servlet 定义。封装器必须位于上下文中,这样 ServletConfig 对象才能在调用其 getServletContext 方法时返回 ServletContext 实例。

getServletName

This method returns the name of the servlet. Its signature is as follows:

该方法返回 servlet 的名称。其签名如下

代码语言:javascript复制
public java.lang.String getServletName()

Here is the implementation of the getServletName method in the StandardWrapper class:

下面是 StandardWrapper 类中 getServletName 方法的实现:

代码语言:javascript复制
public String getServletName() {
 return (getName());
}

It simply calls the getName method of the ContainerBase class, the parent class of StandardWrapper. The getName method is implemented as follows in ContainerBase:

它只需调用 StandardWrapper 的父类 ContainerBase 类的 getName 方法。ContainerBase 中的 getName 方法实现如下:

代码语言:javascript复制
public String getName() {
 return (name);
}

You can set the value of name by calling the setName method. Recall that you call the setName method of the StandardWrapper instance by passing the name of the servlet?

你可以通过调用 setName 方法来设置 name 的值。

还记得通过传递 servlet 的名称来调用 StandardWrapper 实例的 setName 方法吗?

getInitParameter

This method returns the value of the specified initialization parameter. Its signature is as follows:

该方法返回指定初始化参数的值。

其签名如下

代码语言:javascript复制
public java.lang.String getInitParameter(java.lang.String nam

In StandardWrapper, the initialization parameters are stored in a HashMap named parameters.

StandardWrapper 中,初始化参数存储在名为 parametersHashMap 中。

代码语言:javascript复制
private HashMap parameters = new HashMap();

You populate parameters by calling the StandardWrapper class's addInitParameter method, passing the name and the value of the parameter:

您可以通过调用 StandardWrapper 类的 addInitParameter 方法来填充参数,并传递参数的名称和值:

代码语言:javascript复制
public void addInitParameter(String name, String value) {
 synchronized (parameters) {
 parameters.put(name, value);
 }
 fireContainerEvent("addInitParameter", name);
}

Here is the implementation of getInitParameter in StandardWrapper:

下面是 StandardWrappergetInitParameter 的实现:

代码语言:javascript复制
public String getInitParameter(String name) {
 return (findInitParameter(name));
}


The findInitParameter method accepts the parameter name and calls the get method of the parameters HashMap. Here is the implementation of findInitParameter.

findInitParameter 方法接受参数名,并调用参数 HashMapget 方法。以下是 findInitParameter 的实现。

代码语言:javascript复制
public String findInitParameter(String name) {
 synchronized (parameters) {
 return ((String) parameters.get(name));
 }
}

getInitParameterNames

This method returns an Enumeration containing all the names of the initialization parameters. Its signature is as follows:

该方法返回一个枚举,其中包含所有初始化参数的名称。其签名如下:

代码语言:javascript复制
public java.util.Enumeration getInitParameterNames()

Here is the implementation of the getInitParameterNames in StandardWrapper:

下面是 StandardWrappergetInitParameterNames 的实现:

代码语言:javascript复制
public Enumeration getInitParameterNames() {
 synchronized (parameters) {
 return (new Enumerator(parameters.keyset()));
 }
}

The Enumerator class implements java.util.Enumeration and is part of the org.apache.catalina.util package.

Enumerator 类实现了 java.util.Enumeration,是 org.apache.catalina.util 包的一部分。

Part5Parent and Children(父子节点)

A wrapper represents a container for each individual servlet. As such, a wrapper cannot have a child, and its addChild method should not be called. If you did, you would get a java.lang.IllegalStateException. Here is the implementation of the addChild method in the StandardWrapper class:

封装器代表每个单独 servlet 的容器。因此,封装器不能有子节点,也不应调用其 addChild 方法。

如果调用,就会出现 java.lang.IllegalStateException 异常。下面是 StandardWrapper 类中 addChild 方法的实现:

代码语言:javascript复制
public void addChild(Container child) {
 throw new IllegalStateException
 (sm.getString("StandardWrapper.notChild"));
}

A wrapper's parent can only be an implementation of Context. Its setParent method throws a java.lang.IllegalArgumentException if you pass a non-Context container to it.

包装器的父类只能是 Context 的实现。如果向其传递一个非 Context 容器,则其 setParent 方法会抛出 java.lang.IllegalArgumentException 异常。

代码语言:javascript复制
public void setParent(Container container) {
 if ((container != null) && !(container instanceof Context))
 throw new IllegalArgumentException
 (sm.getString("standardWrapper.notContext"));
 super.setParent(container);
}

Part6StandardWrapperFacade(标准外观类)

The StandardWrapper instance calls the init method of the servlet it loads. The init method requires a javax.servlet.ServletConfig and the StandardWrapper class itself implements the ServletConfig interface, so in theory a StandardWrapper object could pass itself to the init method. However, the StandardWrapper class needs to hide most of its public method from the servlet. To achieve this, the StandardWraper class wraps itself in a StandardWrapperFacade instance. Figure 11.2 shows the relationship between StandardWrapper and StandardWrapperFacade. Both implement the javax.servlet.ServletConfig interface.

StandardWrapper 实例调用加载的 servletinit 方法。

init 方法需要一个javax.servlet.ServletConfig对象,而StandardWrapper类本身实现了ServletConfig接口,因此理论上一个StandardWrapper对象可以将自身传递给 init 方法。

然而,StandardWrapper类需要隐藏大部分公共方法,以防止servlet访问。

为了实现这一点,StandardWrapper类将自身封装在一个 StandardWrapperFacade 实例中。

图11.2展示了 StandardWrapperStandardWrapperFacade 之间的关系。

两者都实现了javax.servlet.ServletConfig接口。

Figure 11.2: The relationship between StandardWrapper and StandardWrapperFacade

Figure 11.2: The relationship between StandardWrapper and StandardWrapperFacade

图 11.2:StandardWrapperStandardWrapperFacade 之间的关系

The following line can be found in the StandardWrapper class in which the StandardWrapper passes itself to the constructor of StandardWrapperFacade:

StandardWrapper 类中可以找到以下一行,其中 StandardWrapper 将自身传递给 StandardWrapperFacade 的构造函数:

代码语言:javascript复制
private StandardWrapperFacade facade = new StandardWrapperFacade(this);

The StandardWrapperFacade class provides the class-level variable config of type ServletConfig:

StandardWrapperFacade 类提供了 ServletConfig 类型的类级变量 config

代码语言:javascript复制
private ServletConfig config = null;

When an instance of StandardWrapperFacade is created from inside a StandardWrapper object, the StandardWrapperFacade class's constructor is passed the containing StandardWrapper object, which the constructor assigns to the config variable:

当从 StandardWrapper 对象内部创建 StandardWrapperFacade 实例时,StandardWrapperFacade 类的构造函数会传递包含 StandardWrapper 的对象,构造函数会将该对象赋值给 config 变量:

代码语言:javascript复制
public StandardWrapperFacade(StandardWrapper config) {
 super();
 this.config = (ServletConfig) config;
}

Thus, when the StandardWrapper object calls the init method of a servlet instance, it passes an instance of StandardWrapperFacade. Calls to the ServletConfig's getServletName, getInitParameter, and getInitParameterNames methods from inside the servlet are simply passed to the corresponding methods implemented by the StandardWrapper class.

因此,当 StandardWrapper 对象调用 servlet 实例的 init 方法时,它会传递一个 StandardWrapperFacade 实例。

Servlet 内部调用 ServletConfiggetServletNamegetInitParametergetInitParameterNames 方法时,只需将其传递给 StandardWrapper 类实现的相应方法即可。

代码语言:javascript复制
public String getServletName() {
 return config.getServletName();
}
public String getInitParameter(String name) {
 return config.getInitParameter(name);
}
public Enumeration getInitParameterNames() {
 return config.getInitParameterNames();
}

Calls to the getServletContext method is a bit more complex:

getServletContext 方法的调用要复杂一些:

代码语言:javascript复制
public ServletContext getServletContext() {
 ServletContext theContext = config.getServletContext();
 if ((theContext != null) && (theContext instanceof
ApplicationContext))
 theContext = ((ApplicationContext) theContext).getFacade();
 return (theContext);
}

The method calls the getServletContext method in the StandardWrapper class, but it returns the ServletContext's façade, instead of the ServletContext object itself.

该方法调用了 StandardWrapper 类中的 getServletContext 方法,但返回的是 ServletContext 的外观,而不是 ServletContext 对象本身。

Part7StandardWrapperValve(标准包装值)

The StandardWrapperValve class is the basic valve in a StandardWrapper instance. This valve does two things:

StandardWrapperValve类是StandardWrapper实例中的基本阀门。该阀门有两个作用:

  • Executes all filters associated with the servlet
  • Calls the sender's service method.
  • 执行与 servlet 关联的所有过滤器
  • 调用发送者的 service 方法。

To do these, here are what the StandardWrapperValve must do in its invoke method implementation:

为了实现这些功能,StandardWrapperValve在其invoke方法中必须执行以下操作:

  • Obtain an instance of the servlet that the containing StandardWrapper represents by calling the StandardWrapper's allocate method.
  • Create a filter chain by calling the private createFilterChain method.
  • Call the filter chain's doFilter method. This includes calling the servlet's service method.
  • Release the filter chain.
  • Call the wrapper's deallocate method.
  • Call the wrapper's unload method if the servlet is permanently unavailable.
  • 通过调用 StandardWrapperallocate 方法获取包含的 StandardWrapper 表示的 servlet 的实例。
  • 通过调用私有的 createFilterChain 方法创建一个过滤器链。
  • 调用过滤器链的 doFilter 方法。这包括调用 servletservice 方法。
  • 释放过滤器链。
  • 调用包装器的 deallocate 方法。
  • 如果 servlet 永久不可用,则调用包装器的 unload 方法。

The following is the highlight of the invoke method:

以下是 invoke 方法的重点:

代码语言:javascript复制
  
  
// Allocate a servlet instance to process this request  
 try {  
        if (!unavailable) {  
            servlet = wrapper.allocate();  
        }  
    }  
 ...  
         // Acknowlege the request  
         try {  
        response.sendAcknowledgement();  
    }  
 ...  
    // Create the filter chain for this request  
    ApplicationFilterChain filterChain = createFilterChain(request,  
            servlet);  
    // Call the filter chain for this request  
    // This also calls the servlet's servicet() method try {  
        String jspFile = wrapper.getJspFile();  
        if (jspFile != null)  
            sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);  
        else            sreq.removeAttribute(Globals.JSP_FILE_ATTR);  
        if ((servlet != null) && (filterChain != null)) {  
            filterChain.doFilter(sreq, sres);  
        }  
        sreq.removeAttribute(Globals.JSP_FILE_ATTR);  
    }  
 ...  
         // Release the filter chain (if any) for this request  
         try {  
        if (filterChain != null)  
            filterChain.release();  
    }  
 ...  
         // Deallocate the allocated servlet instance  
         try {  
        if (servlet != null) {  
            wrapper.deallocate(servlet);  
        }  
    }  
 ...  
         // If this servlet has been marked permanently unavailable,  
         // unload it and release this instance         try {  
        if ((servlet != null) && (wrapper.getAvailable() ==  
                Long.MAX_VALUE)) {  
            wrapper.unload();  
        }  
    }  
 ...

Of utmost importance is the call to the createFilterChain method and the call to the doFilter method of the filter chain. The createFilterChain method creates an instance of ApplicationFilterChain and adds all filters that should be applied to the servlet represented by the StandardWrapper. The ApplicationFilterChain class is explained in detail in the section, "ApplicationFilterChain", later in this chapter. To fully understand this class, however, you need to understand the FilterDef and the ApplicationFilterConfig classes. Both are given in the sections "FilterDef" and "ApplicationFilterConfig", respectively.

最重要的是调用 createFilterChain 方法和过滤器链的 doFilter 方法。

createFilterChain 方法创建一个 ApplicationFilterChain 的实例,并将所有应用于由 StandardWrapper 表示的servlet的过滤器添加到其中。

ApplicationFilterChain 类将在本章后面的 "ApplicationFilterChain" 一节中详细介绍。

然而,要完全理解这个类,您需要了解 FilterDefApplicationFilterConfig 类。

它们分别在"FilterDef"和"ApplicationFilterConfig"两节中给出。

Part8FilterDef(过滤器定义)

The org.apache.catalina.deploy.FilterDef class represents a filter definition as defined by the filter element in the deployment descriptor. Listing 11.1 presents this class.

org.apache.catalina.deploy.FilterDef 类表示部署描述符中filter元素定义的过滤器定义。

清单11.1展示了这个类。

Listing 11.1: The FilterDef class

清单11.1:FilterDef

代码语言:javascript复制
  
package org.apache.catalina.deploy;  
import java.util.HashMap;  
import java.util.Map;  
    public final class FilterDef {  
        /**  
         * The description of this filter.         */        private String description = null;  
        public String getDescription() {  
            return (this.description);  
        }  
        public void setDescription(String description) {  
            this.description = description;  
        }  
        /**  
         * The display name of this filter.         */        private String displayName = null;  
        public String getDisplayName() {  
            return (this.displayName);  
        }  
        public void setDisplayName(String displayName) {  
            this.displayName = displayName;  
        }  
        /**  
         * The fully qualified name of the Java class that implements this         * filter.         */        private String filterClass = null;  
        public String getFilterClass() {  
            return (this.filterClass);  
        }  
        public void setFilterclass(String filterClass) {  
            this.filterClass = filterClass;  
        }  
        /**  
         * The name of this filter, which must be unique among the filters         * defined for a particular web application.         */        private String filterName = null;  
        public String getFilterName() {  
            return (this.filterName);  
        }  
        public void setFilterName(String filterName) {  
            this.filterName = filterName;  
        }  
        /**  
         * The large icon associated with this filter.         */        private String largeIcon = null;  
        public String getLargeIcon() {  
            return (this.largeIcon);  
        }  
        public void setLargeIcon(String largeIcon) {  
            this.largeIcon = largeIcon;  
        }  
        /**  
         * The set of initialization parameters for this filter, keyed by         * parameter name.         */        private Map parameters = new HashMap();  
        public Map getParameterMap() {  
            return (this.parameters);  
        }  
        /**  
         * The small icon associated with this filter.         */        private String smallIcon = null;  
        public String getSmallIcon() {  
            return (this.smallIcon);  
        }  
        public void setSmallIcon(String smallIcon) {  
            this.smallIcon = smallIcon;  
        }  
        public void addInitParameter(String name, String value) {  
            parameters.put(name, value);  
        }  
        /**  
         * Render a String representation of this object.         */        public String toString() {  
            StringBuffer sb = new StringBuffer("FilterDef[");  
            sb.append("filterName=");  
            sb.append(this.filterName);  
            sb.append(", filterClass=");  
            sb.append(this.filterClass);  
            sb.append("]");  
            return (sb.toString());  
        }  
    }

Each property in the FilterDef class represents a sub-element that can appear under the filter element. The class also has a Map named parameters that represents a Map containing all the initial parameters for this filter. The addInitParameter method adds a pair of initial parameter name/value

FilterDef 类中的每个属性都代表过滤器元素下的一个子元素。

该类还有一个名为 parameters 的 Map,表示包含该过滤器所有初始参数的 Map。addInitParameter 方法会添加一对初始参数名/值

Part9ApplicationFilterConfig(应用程序过滤器配置)

The org.apache.catalina.core.ApplicationFilterConfig implements the javax.servlet.FilterConfig interface. ApplicationFilterConfig manages the filter instances that are created when the web application is first started.

org.apache.catalina.core.ApplicationFilterConfig实现了javax.servlet.FilterConfig接口。

ApplicationFilterConfig在Web应用程序首次启动时管理创建的过滤器实例。

You create an ApplicationFilterConfig object bypassing an org.apache.catalina.Context object and a FilterDef object to the ApplicationFilterConfig class's constructor:

您可以通过将org.apache.catalina.Context对象和FilterDef对象传递给ApplicationFilterConfig类的构造函数来创建ApplicationFilterConfig对象:

代码语言:javascript复制
public ApplicationFilterConfig(Context context, FilterDef filterDef)
 throws ClassCastException, ClassNotFoundException,
 IllegalAccessException, InstantiationException, ServletException

The Context object represents a web application and the FilterDef object is the filter definition. The ApplicationFilterConfig class has the getFilter method that returns a javax.servlet.Filter object. This method loads the filter class and instantiates it.

Context对象表示一个Web应用程序,FilterDef对象是过滤器定义。

ApplicationFilterConfig类具有getFilter方法,该方法返回一个javax.servlet.Filter对象。此方法加载过滤器类并实例化它。

代码语言:javascript复制
    Filter getFilter() throws ClassCastException, ClassNotFoundException,
            IllegalAccessException, InstantiationException, ServletException {
        // Return the existing filter instance, if any
        if (this.filter != null)
            return (this.filter);
        // Identify the class loader we will be using
        String filterClass = filterDef.getFilterClass();
        ClassLoader classLoader = null;
        if (filterClass.startsWith("org.apache.catalina."))
            classLoader = this.getClass().getClassLoader();
        else
            classLoader = context.getLoader().getClassLoader();
        ClassLoader oldCtxClassLoader =
                Thread.currentthread().getContextClassLoader();
        // Instantiate a new instance of this filter and return it
        Class clazz = classLoader.loadClass(filterClass);
        this.filter = (Filter) clazz.newInstance();
        filter.init(this);
        return (this.filter);
    }

Part10ApplicationFilterChain(应用程序过滤链)

The org.apache.catalina.core.ApplicationFilterChain class is the implementation of the javax.servlet.FilterChain interface. The invoke method in the StandardWrapperValve class creates an instance of this class and calls its doFilter method. The ApplicationFilterChain class's doFilter method calls the doFilter method of the first filter in the chain. The Filter interface's doFilter method has the following signature:

org.apache.catalina.core.ApplicationFilterChain类是javax.servlet.FilterChain接口的实现。

StandardWrapperValve 类中的i nvoke 方法创建了这个类的一个实例,并调用它的 doFilter 方法。

ApplicationFilterChain 类的 doFilter 方法调用了链中第一个过滤器的 doFilter 方法。

Filter 接口的 doFilter 方法具有以下签名:

代码语言:javascript复制
public void doFilter(ServletRaquest request, ServletResponse response,
 FilterChain chain) throws java.io.IOException, ServletException

The doFilter method of the ApplicationFilterChain class passes itself as the third argument to the filter's doFilter method.

ApplicationFilterChain 类的 doFilter 方法将自身作为第三个参数传递给过滤器的 doFilter 方法。

From its doFilter method, a filter can cause the invocation of another filter by explicitly calling the doFilter method of the FilterChain object. Here is an example of the doFilter method implementation in a filter.

从它的 doFilter 方法中,过滤器可以通过显式调用 FilterChain 对象的 doFilter 方法来引发对另一个过滤器的调用。

以下是一个过滤器中 doFilter 方法的实现示例。

代码语言:javascript复制
public void doFilter(ServletRequest request, ServletResponse response,
 FilterChain chain) throws IOException, ServletException {
 // do something here
 ...
 chain.doFilter(request, response);
}

As you can see, the last line of the doFilter method is a call to the doFilter method of the FilterChain. If the filter is the last filter in the chain, this will cause the invocation of the requested servlet's service method. If the filter does not call chain.doFilter, the next filter will not be invoked.

如您所见,doFilter 方法的最后一行是对 FilterChaindoFilter 方法的调用。

如果过滤器是链中的最后一个过滤器,这将导致调用请求的 servlet 服务方法。

如果过滤器没有调用 chain.doFilter,则不会调用下一个过滤器。

Part11The Application(应用)

The application consists of two classes, ex11.pyrmont.core.SimpleContextConfig and ex11.pyrmont.startup.Bootstrap. The SimpleContextConfig class is a copy of the previous chapter's application. The Bootstrap class is given in Listing 11.2.

应用程序由两个类组成:ex11.pyrmont.core.SimpleContextConfig ex11.pyrmont.startup.Bootstrap

SimpleContextConfig 类是上一章应用程序的副本。

清单 11.2 给出了 Bootstrap 类。

Listing 11.2: The Bootstrap class

清单 11.2:Bootstrap

代码语言:javascript复制
  
package ex11.pyrmont.startup;  
//use StandardWrapper  
import ex11.pyrmont.core.SimpleContextConfig;  
import org.apache.catalina.Connector;  
import org.apache.catalina.Context;  
import org.apache.catalina.Lifecycle;  
import org.apache.catalina.LifecycleListener;  
import org.apache.catalina.Loader;  
import org.apache.catalina.Wrapper;  
import org.apache.catalina.connector.http.HttpConnector;  
import org.apache.catalina.core.StandardContext;  
import org.apache.catalina.core.StandardWrapper;  
import org.apache.catalina.loader.WebappLoader;  
    public final class Bootstrap {  
        public static void main(String[] args) {  
            System.setProperty("catalina.base",  
                    System.getProperty("user.dir"));  
            Connector connector = new HttpConnector();  
            Wrapper wrapper1 = new StandardWrapper();  
            wrapper1.setName("Primitive");  
            wrapper1.setServletClass("PrimitiveServlet");  
            Wrapper wrapper2 = new StandardWrapper();  
            wrapper2.setName("Modern");  
            wrapper2.setServletClass("ModernServlet");  
            Context context = new StandardContext();  
            // StandardContext's start method adds a default mapper  
            context.setPath("/myApp");  
            context.setDocBase("myApp");  
            LifecycleListener listener = new SimpleContextConfig();  
            ((Lifecycle) context).addLifecycleListener(listener);  
            context.addChild(wrapper1);  
            context.addChild(wrapper2);  
            // for simplicity, we don't add a valve, but you can add  
            // valves to context or wrapper just as you did in Chapter 6            Loader loader = new WebappLoader();  
            context.setLoader(loader);  
            // context.addServletMapping(pattern, name);  
            context.addServletMapping("/Primitive", "Primitive");  
            context.addServletMapping("/Modern", "Modern");  
            // add ContextConfig. This listener is important because it  
            // configures StandardContext (sets configured to true), otherwise            // StandardContext won't start            connector.setContainer(context);  
            try {  
                connector.initialize();  
                ((Lifecycle) connector).start();  
                ((Lifecycle) context).start();  
                // make the application wait until we press a key.  
                System.in.read();  
                ((Lifecycle) context).stop();  
            }  
            catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }

The Bootstrap class creates an instance of StandardContext and calls it myApp. To the StandardContext, the Bootstrap class adds two StandardWrapper instances: Primitive and Modern

Bootstrap类创建了StandardContext的一个实例,并将其命名为myApp。在StandardContext中,Bootstrap类添加了两个 StandardWrapper 实例:PrimitiveModern

1Running the Applications 运行应用程序

To run the application in Windows, from the working directory, type the following:

Windows 中,从工作目录中输入以下命令来运行应用程序:

代码语言:javascript复制
java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./
ex11.pyrmont.startup.Bootstrap

In Linux, you use a colon to separate two libraries.

在Linux中,使用冒号来分隔两个库。

代码语言:javascript复制
java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./

ex11.pyrmont.startup.Bootstrap

To invoke PrimitiveServlet, use the following URL in your browser.

要调用 PrimitiveServlet,请在浏览器中使用以下 URL。

http://localhost:8080/Primitive

To invoke ModernServlet, use the following URL.

要调用 ModernServlet,请使用以下 URL。

http://localhost:8080/Modern

Part12Summary(摘要)

In this chapter you learned about the StandardWrapper class, the standard implementation of the Wrapper interface in Catalina. Also discussed was the filter and filter-related classes. An application that uses the StandardWrapper class was presented at the end of the chapter.

在本章中,您将了解到 StandardWrapper 类,它是 CatalinaWrapper 接口的标准实现。

此外,还讨论了过滤器和过滤器相关类。

本章最后介绍了一个使用 StandardWrapper 类的应用程序。

0 人点赞