博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
How Tomcat Works - A Simple Servlet Container
阅读量:4980 次
发布时间:2019-06-12

本文共 7312 字,大约阅读时间需要 24 分钟。

这一章和第一章的区别就是对servlet的支持。我们看下是怎么做的。

1)首先Response和Request这两个类分别实现了ServletResponse和ServletRequest接口,这两个接口和后面用到的Servlet接口都在javax.servlet这个package下面,需要添加servlet-api这个第三方依赖到classpath才能访问,这里用的是servlet-api-2.5。所有新加的方法除了Response.getWriter() 都没有任何处理,因为暂时没有用到。

Response.getWriter():

@Overridepublic PrintWriter getWriter() throws IOException {     writer = new PrintWriter(output, true);     return writer;}

实例化了一个PrintWriter对象,第一个参数就是request的OutputStream,第二个参数表示是否自动flush。

2)对于静态资源请求和servlet请求分别创建了StaticResourceProcessor和ServletProcessor1。

StaticResourceProcessor:

import java.io.IOException;public class StaticResourceProcessor {    public void process(Request request, Response response) {        try {            response.sendStaticResource();        } catch (IOException e) {            e.printStackTrace();        }    }}

这里比较简单,就是条用response的sendStaticResource这个方法。

ServletProcessor1:

import java.io.File;import java.io.IOException;import java.net.URL;import java.net.URLClassLoader;import java.net.URLStreamHandler;import javax.servlet.Servlet;public class ServletProcessor1 {    public void process(Request request, Response response) {        String uri = request.getUri();        String servletName = uri.substring(uri.lastIndexOf("/") + 1);        URLClassLoader loader = null;        try {            URL[] urls = new URL[1];            URLStreamHandler streamHandler = null;            File classPath = new File(Constants.WEB_ROOT);            String repository = (new URL("file", null,                    classPath.getCanonicalPath() + File.separator)).toString();            urls[0] = new URL(null, repository, streamHandler);            loader = new URLClassLoader(urls);        } catch (IOException e) {            System.out.println(e.toString());        }        Class myClass = null;        try {            myClass = loader.loadClass(servletName);        } catch (ClassNotFoundException e) {            System.out.println(e.toString());        }        Servlet servlet = null;        try {            servlet = (Servlet) myClass.newInstance();            servlet.service(request, response);        } catch (Throwable e) {            System.out.println(e.toString());        }    }}

这段代码的主要目的就是通过Request的uri找到对应的Servlet类,通过URLClassLoader从文件加载这个类并通过反射实例化一个Servlet对象,然后通过Servlet对象的service方法处理请求。servlet文件暂时都是放在webroot这个文件夹。所以很明显,目前只能根据uri是否是以/servlet/开头来判断一个请求是否是servlet请求。而且对于每个请求,都必须创建一个Servlet实例。

3)然后修改HttpServer这个类,对于Servlet和静态资源请求使用不同的processor:

public void await() {        ServerSocket serverSocket = null;        int port = 8080;        try {            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));        } catch (IOException e) {            e.printStackTrace();            System.exit(1);        }        // Loop waiting for a request        while (!shutdown) {            Socket socket = null;            InputStream input = null;            OutputStream output = null;            try {                socket = serverSocket.accept();                input = socket.getInputStream();                output = socket.getOutputStream();                                // create chapter1.Request object and parse                Request request = new Request(input);                request.parse();                // create chapter1.Response object                Response response = new Response(output);                response.setRequest(request);                if (request.getUri().startsWith("/servlet/")) {                    ServletProcessor1 processor = new ServletProcessor1();                    processor.process(request, response);                } else {                    StaticResourceProcessor processor = new StaticResourceProcessor();                    processor.process(request, response);                }                                // Close the socket                socket.close();                //check if the previous URI is a shutdown command                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);            } catch (Exception e) {                e.printStackTrace();                continue;            }        }    }

4)在webroot下创建测试用的PrimitiveServlet:

import java.io.IOException;import java.io.PrintWriter;import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class PrimitiveServlet implements Servlet {        @Override    public void init(ServletConfig servletConfig) throws ServletException {        System.out.println("init");    }    @Override    public ServletConfig getServletConfig() {        return null;    }    @Override    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {        System.out.println("from service");        PrintWriter out = servletResponse.getWriter();        out.println("Hello, Roses are red");        out.print("Violets are blue");    }    @Override    public String getServletInfo() {        return null;    }    @Override    public void destroy() {        System.out.println("destroy");    }}

启动HttpServelt访问http://localhost:8080/servlet/PrimitiveServlet 会发现(火狐)浏览器打印出了:

Hello, Roses are red

我们会发现下面这一行:

out.print("Violets are blue");

并没有打印出来,这个问题会在后面解决。

5)使用门面模式隐藏request和response的方法

这部分是讲,我们在调用servelt的时候:

Servlet servlet = null;        try {            servlet = (Servlet) myClass.newInstance();            servlet.service(request, response);        } catch (Throwable e) {            System.out.println(e.toString());        }

直接传入了request和response,那么request和response里面一些public但是又不应该被servlet访问的方法可以被访问到。虽然Servlet.service的签名是:

public void service(ServletRequest req, ServletResponse res)	throws ServletException, IOException;

但是在PrimitiveServlet里面还是可以通过向下转型得到一个Request类型的对象:

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {        System.out.println("from service");        PrintWriter out = servletResponse.getWriter();        out.println("Hello, Roses are red");        out.print("Violets are blue");        Request request = (Request) servletRequest;        request.xxx();        // others    }

这样Request内部不希望被Servlet访问但是又必须是public的方法就暴露了。所以作者在这里使用门面模式把request和response做了封装,以request为例,新增了RequestFacade:

public class RequestFacade implements ServletRequest {        private ServletRequest request;    public RequestFacade(ServletRequest request) {        this.request = request;    }    @Override    public Object getAttribute(String s) {        return request.getAttribute(s);    }    @Override    public Enumeration getAttributeNames() {        return request.getAttributeNames();    }       //.....}

同样实现了ServletRequest接口,同时内部保留一个Request类型的引用,所有的方法都转发给真正的request。Response也是类似的处理。重构之后的ServletProcessor1:

Servlet servlet = null;        RequestFacade requestFacade = new RequestFacade(request);        ResponseFacade responseFacade = new ResponseFacade(response);                try {            servlet = (Servlet) myClass.newInstance();            servlet.service(requestFacade, responseFacade);        } catch (Throwable e) {            System.out.println(e.toString());        }

这里传给Servlet.service()的就是Facade了,不必再担心Servlet里面会对request和respone向下转型。

 

转载于:https://www.cnblogs.com/likely/p/7289721.html

你可能感兴趣的文章
U. Apache
查看>>
信号处理
查看>>
delphi 资源文件
查看>>
我想写程序#2 之 「两招(三支程序)就可以出师」
查看>>
索引和索引调整向导
查看>>
linux下的rz sz
查看>>
openfire 学习笔记--转自网易博客
查看>>
编译(树形DP)
查看>>
java微信支付异步回调接收参数
查看>>
test
查看>>
关于Debug certificate expired的问题
查看>>
移动端浏览器有哪些,内核分别是什么
查看>>
利用python实现简单随机验证码
查看>>
ExtJs4 笔记 Ext.Panel 面板控件、 Ext.window.Window 窗口控件、 Ext.container.Viewport 布局控件...
查看>>
Django框架—ORM操作笔记
查看>>
FireDAC如何连接ORACLE数据库
查看>>
(转)logback配置详解
查看>>
解决电脑系统卡、慢 3分钟成为高手!
查看>>
9. Palindrome Number
查看>>
52. N-Queens II
查看>>