《How tomcat works》读书笔记_容器(2)

Wrapper应用

这个应用创建了一个最小的容器模型。
这里写图片描述
类图如上。

SimpleLoader

在容器Container中加载一个servlet类的任务由Loader的实现类来完成。这里的实现类为SimpleLoader,这个类知道servlet类的class文件的位置,getClassLoader方法返回一个java.lang.ClassLoader类的实例。SimpleLoader类定义了三个变量,第一个是WEB_ROOT,指向存在servlet class文件的目录。

public static final String WEB_ROOT = 
System.getProperty("user.dir") + File.separator + "webroot";

其它两个变量是ClassLoader和Container类型的引用对象:

ClassLoader classLoader = null;
Container container = null;

SimplePipeline

SimplePipeline实现了org.apache.catalina.Pipeline接口。在这个类中最重要的就是invoke方法,这个方法中使用了一个名为SimplePipelineValveContext的内部类,这个内部类实现了org.apache.catalina.ValveContext接口,这些都在前面论述过。

SimpleWrapper

这个类实现了org.apache.catalina.Wrapper接口,并提供了allocate和load方法的实现。其中有两个变量:

private Loader loader;
protected Container parent = null;

loader变量用于加载servlet类。parent变量表示这个wrapper的父容器,也就是说wrapper可以是其它容器的一个子容器,比如,Context。
这里我们需要特别注意一个getLoader方法:

    public Loader getLoader() {
        if (loader != null)
            return (loader);
        if (parent != null)
            return (parent.getLoader());
        return (null);
    }

getLoader方法返回一个Loader类型来加载servlet类。如果wrapper已经关联了一个Loader,那直接返回这个Loader;如果没有,则返回它父容器的Loader;如果没有父容器,那么getLoader方法直接返回一个null。

SimpleWrapper拥有一个pipeline实例,同时它还会为这个pipeline设置它的基本Value。这一切都是在SimpleWrapper类的构造方法中完成的。

    public SimpleWrapper() {
        pipeline.setBasic(new SimpleWrapperValve());
    }

pipeline是SimplePipeline类的一个实例。

SimpleWrapperValve

SimpleWrapperValve是一个基本Value,它主要为SimpleWrapper类处理request请求。它实现了org.apache.catalina.Valve接口和org.apache.catalina.Contained接口。SimpleWrapperValve类最主要的方法就是invoke。

    public void invoke(Request request, Response response,
            ValveContext valveContext) throws IOException, ServletException {

        SimpleWrapper wrapper = (SimpleWrapper) getContainer();
        ServletRequest sreq = request.getRequest();
        ServletResponse sres = response.getResponse();
        Servlet servlet = null;
        HttpServletRequest hreq = null;
        if (sreq instanceof HttpServletRequest)
            hreq = (HttpServletRequest) sreq;
        HttpServletResponse hres = null;
        if (sres instanceof HttpServletResponse)
            hres = (HttpServletResponse) sres;

        // Allocate a servlet instance to process this request
        try {
            servlet = wrapper.allocate();
            if (hres != null && hreq != null) {
                servlet.service(hreq, hres);
            } else {
                servlet.service(sreq, sres);
            }
        } catch (ServletException e) {
        }
    }

由于SimpleWrapperValve是一个基本Value,所以它的invoke方法不需要调用传递给它的ValueContext中的invokeNext方法。invoke方法SimpleWrapper类的allocate方法获取一个由wrapper表示的servlet类。然后调用servlet的service方法。需要注意的是,servlet的service方法是由wrapper管道中的基本Value来调用,而不是wrapper本身来调用。

ClientIPLoggerValve

ClientIPLoggerValve类是一个Value,它用来在终端输出客户端的IP地址。invoke是它主要的方法。

    public void invoke(Request request, Response response,
            ValveContext valveContext) throws IOException, ServletException {

        // Pass this request on to the next valve in our pipeline
        valveContext.invokeNext(request, response);
        System.out.println("Client IP Logger Valve");
        ServletRequest sreq = request.getRequest();
        System.out.println(sreq.getRemoteAddr());
        System.out.println("------------------------------------");
    }

在invoke方法中的第一件事就是调用ValueContext的invokeNext方法来请求管道中的下一个Value,然后它会打印request类中getRemotAddr方法返回的字符串。

HeaderLoggerValve

这个类同上一个类十分相似。HeaderLoggerValve也是一个Value,它用来打印request请求的header到终端。

    public void invoke(Request request, Response response,
            ValveContext valveContext) throws IOException, ServletException {

        // Pass this request on to the next valve in our pipeline
        valveContext.invokeNext(request, response);

        System.out.println("Header Logger Valve");
        ServletRequest sreq = request.getRequest();
        if (sreq instanceof HttpServletRequest) {
            HttpServletRequest hreq = (HttpServletRequest) sreq;
            Enumeration headerNames = hreq.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement().toString();
                String headerValue = hreq.getHeader(headerName);
                System.out.println(headerName + ":" + headerValue);
            }

        } else
            System.out.println("Not an HTTP Request");

        System.out.println("------------------------------------");
    }

同上一个Vaule一样,invoke方法做的第一件事就是调用传递给的ValueContext中的invokeNext方法来请求下一个Value。然后再打印请求的header。

Bootstrap1

public final class Bootstrap1 {
    public static void main(String[] args) {

        /*
         * call by using http://localhost:8080/ModernServlet, but could be
         * invoked by any name
         */

        HttpConnector connector = new HttpConnector();
        Wrapper wrapper = new SimpleWrapper();
        wrapper.setServletClass("ModernServlet");
        Loader loader = new SimpleLoader();
        Valve valve1 = new HeaderLoggerValve();
        Valve valve2 = new ClientIPLoggerValve();

        wrapper.setLoader(loader);
        ((Pipeline) wrapper).addValve(valve1);
        ((Pipeline) wrapper).addValve(valve2);

        connector.setContainer(wrapper);

        try {
            connector.initialize();
            connector.start();

            // make the application wait until we press a key.
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在创建HttpConnector和SimpleWrapper的实例之后,main方法调用SimpleWrapper类的setServletClass方法传递参数“ModernServlet”,通知wrapper需要加载的类。

wrapper.setServletClass("ModernServlet");

然后创建Loader和两个Value,将Loader分配给wrapper,两个Value分配给wrapper的管道。

        Loader loader = new SimpleLoader();
        Valve valve1 = new HeaderLoggerValve();
        Valve valve2 = new ClientIPLoggerValve();

        wrapper.setLoader(loader);
        ((Pipeline) wrapper).addValve(valve1);
        ((Pipeline) wrapper).addValve(valve2);

最后,将wrapper设置为connector的容器(Container),初始化并启动connector.

        connector.setContainer(wrapper);

        try {
            connector.initialize();
            connector.start();

            // make the application wait until we press a key.
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();
        }

运行程序

运行Bootstrap1类中的main方法,在浏览器中输入

localhost:8080

终端Console输出如下:

Client IP Logger Valve
0:0:0:0:0:0:0:1
------------------------------------
Header Logger Valve
host:localhost:8080
connection:keep-alive
accept:*/*
user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
accept-encoding:gzip,deflate,sdch
accept-language:zh-CN,zh;q=0.8
------------------------------------

我们可以看到两个Value分别都被执行了。
浏览器显示ModernServlet的返回结果如下:

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值