深入Tomcat(二)

       对于第一篇文章中的web服务器而言,它是非常简单的,它仅仅能响应一个静态的HTTP请求。在本文中,我们将看到如何一步步的搭建一个可以响应动态Servlet请求的web服务器。


2.1回顾javax.servlet.Servlet接口

2.1.1

我们首先回顾下Servlet接口,它是Servlet编程中最为重要的一个接口,它的定义如下:


当Servlet容器调用某个Servlet类的时候,init()方法被调用,且只执行一次;在Servlet处理任何请求之前,必须保证其被正确初始化;当Servlet的一个客户端请求到达以后,容器调用Servlet的service()方法,并将ServletRequest和ServletResponse两个对象作为参数传给该方法,在Servlet生命周期内,该方法可以被多次调用;在将Servlet实例从服务中移除的时候,destroy()方法被调用。

下面是本章的一个Servlet程序。如下:

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.Servlet;

import javax.servlet.ServletConfig;

importjavax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

 

publicclass PrimitiveServletimplements Servlet {

 

    @Override

    publicvoid destroy() {

       System.out.println("destory");

    }

 

    @Override

    public ServletConfig getServletConfig() {

       returnnull;

    }

 

    @Override

    public String getServletInfo() {

       returnnull;

    }

 

    @Override

    publicvoid init(ServletConfig arg0)throwsServletException {

       System.out.println("init");

    }

 

    @Override

    publicvoid service(ServletRequest request, ServletResponse response)

           throwsServletException,IOException {

       System.out.println("fromservice");

       PrintWriter out=response.getWriter();

       out.println("Hello.Roses are red.");

       out.println("violets areblue.");

    }

}

PrimitiveServlet 非常简单,需要注意的是:我把它和它的class文件放在了webroot目录下。

2.2 应用程序1

 

 

2.1.1HttpServer1

HttpServer1与第一章相比,大的区别就是在awit()方法中加入了对请求动态内容(Servlet请求)的处理。它的示例如下:

package ex02;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.net.UnknownHostException;

 

publicclass HttpServer1 {

   

    privatestaticbooleanshutdown=false;

    publicstaticvoid main(String[] args){

       new HttpServer1().await();

    }

    privatevoid await(){

       int port=7890;

       ServerSocket serverSocket=null;

       try{

           serverSocket=new ServerSocket(port,1,

                  InetAddress.getByName("127.0.0.1"));

       }catch(IOException e){

           e.printStackTrace();

           System.exit(1);

       }

       try {

           Socket socket=null;

           InputStream input=null;

           OutputStream output=null;

           while(!shutdown){

              socket=serverSocket.accept();

              input=socket.getInputStream();

              output=socket.getOutputStream();

              HttpRequest request=new HttpRequest(input);

              request.parse();

              HttpResponse response=new HttpResponse(output);

              response.setRequest(request);

              if(request.getUri().startsWith("/servlet")){

                  ServletProcessor1 processor1=newServletProcessor1();

                  processor1.process(request, response);

              }else{

                  StaticResourceProcessor processor=new StaticResourceProcessor();

                  processor.process(request, response);

              }

              socket.close();

              shutdown=request.getUri().equals("shutdown");

           }

       } catch (UnknownHostException e) {

           e.printStackTrace();

       } catch (IOException e) {

           e.printStackTrace();

       }

    }

}

2.1.2Request

本章的HttpRequest与上一章的区别在于:它实现了ServletRequest接口。如下:

package ex02;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.UnsupportedEncodingException;

import java.util.Enumeration;

import java.util.Locale;

import java.util.Map;

import javax.servlet.RequestDispatcher;

import javax.servlet.ServletInputStream;

import javax.servlet.ServletRequest;

 

publicclass HttpRequestimplements ServletRequest {

 

    private InputStreaminput;

    private Stringuri;

    public String getUri() {

       returnuri;

    }

 

    publicvoid setUri(String uri) {

       this.uri = uri;

    }

    public InputStream getInput() {

       returninput;

    }

    publicvoid setInput(InputStream input) {

       this.input = input;

    }

    public HttpRequest(){

 

    }

    public HttpRequest(InputStream input){

       this.input=input;

    }

    //解析HTTP请求的原始数据

    publicvoid parse(){

       byte[] b=newbyte[2048];

       StringBuffer request=new StringBuffer(2048);

       try {

           int i=input.read(b);

           for(int j=0;j<i;j++){

              request.append((char)b[j]);

           }

       } catch (IOException e) {

           e.printStackTrace();

       }

       this.uri=parseUri(request.toString());

       System.out.println("HTTP请求的原始数据是:"+request.toString());

       System.out.println("uri:"+uri);

    }

    //解析HTTP请求的URL

    private String parseUri(String requestString){

       int index1,index2;

       index1=requestString.indexOf(' ');

       if(index1!=-1){

           index2=requestString.indexOf(' ', index1+1);

           if(index2>index1){

              return requestString.substring(index1+1, index2);

           }

       }

       returnnull

    }

   

   

    @Override

    public Object getAttribute(String arg0) {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicEnumeration getAttributeNames() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getCharacterEncoding() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicint getContentLength() {

       //TODO Auto-generatedmethod stub

       return 0;

    }

 

    @Override

    public String getContentType() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public ServletInputStream getInputStream()throws IOException {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getLocalAddr() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getLocalName() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicint getLocalPort() {

       //TODO Auto-generatedmethod stub

       return 0;

    }

 

    @Override

    public Locale getLocale() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicEnumeration getLocales() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getParameter(String arg0) {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicMap getParameterMap() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicEnumeration getParameterNames() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String[] getParameterValues(String arg0) {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getProtocol() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public BufferedReader getReader()throws IOException {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getRealPath(String arg0) {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getRemoteAddr() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getRemoteHost() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicint getRemotePort() {

       //TODO Auto-generatedmethod stub

       return 0;

    }

 

    @Override

    public RequestDispatcher getRequestDispatcher(String arg0) {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getScheme() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    public String getServerName() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

 

    @Override

    publicint getServerPort() {

       //TODO Auto-generatedmethod stub

       return 0;

    }

 

    @Override

    publicboolean isSecure() {

       //TODO Auto-generatedmethod stub

       returnfalse;

    }

 

    @Override

    publicvoid removeAttribute(String arg0) {

       //TODO Auto-generatedmethod stub

 

    }

 

    @Override

    publicvoid setAttribute(String arg0, Object arg1) {

       //TODO Auto-generatedmethod stub

 

    }

 

    @Override

    publicvoid setCharacterEncoding(String arg0)

           throws UnsupportedEncodingException {

       //TODO Auto-generatedmethod stub

 

    }

 

}

2.1.3Response

与HttpRequest类似,除了getWriter()方法外,其他方法都是空实现。

package ex02;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.util.Locale;

import javax.servlet.ServletOutputStream;

import javax.servlet.ServletResponse;

publicclass HttpResponseimplements ServletResponse {

 

    privatestaticfinalintBUFFER_SIZE=1024;

    HttpRequest request;

    OutputStream output;

    PrintWriter writer;

    public HttpResponse(){

 

    }

    public HttpResponse(OutputStream output){

       this.output=output;

    }

    public HttpRequest getRequest() {

       returnrequest;

    }

    publicvoid setRequest(HttpRequest request) {

       this.request = request;

    }

    public OutputStream getOutput() {

       returnoutput;

    }

    publicvoid setOutput(OutputStream output) {

       this.output = output;

    }

    publicvoid sendStaticResource(){

       byte[] bytes=newbyte[BUFFER_SIZE];

       FileInputStream fis=null;

       File file=new File(Constant.WEB_ROOT,request.getUri());

       try {

           if(file.exists()){

              fis=new FileInputStream(file);

              int ch=fis.read(bytes,0,BUFFER_SIZE);

              while(ch!=-1){

                  output.write(bytes, 0, ch);

                  ch=fis.read(bytes,0,BUFFER_SIZE);

              }

           }

           else{

              output.write("file notfound".getBytes());

           }

 

       } catch (Exception e) {

           e.printStackTrace();

       }

       finally{

           if(fis!=null){

              try{

                  fis.close();

              }catch(Exception e){

                  e.printStackTrace();

              }

           }

       }

    }

    @Override

    publicvoid flushBuffer()throws IOException {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicint getBufferSize() {

       //TODO Auto-generatedmethod stub

       return 0;

    }

    @Override

    public String getCharacterEncoding() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

    @Override

    public String getContentType() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

    @Override

    public Locale getLocale() {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

    @Override

    public ServletOutputStream getOutputStream()throws IOException {

       //TODO Auto-generatedmethod stub

       returnnull;

    }

    @Override

    public PrintWriter getWriter()throws IOException {

       writer=new PrintWriter(output,true);

       returnwriter;

    }

    @Override

    publicboolean isCommitted() {

       //TODO Auto-generatedmethod stub

       returnfalse;

    }

    @Override

    publicvoid reset() {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicvoid resetBuffer() {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicvoid setBufferSize(int arg0) {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicvoid setCharacterEncoding(String arg0) {

        // TODO Auto-generated method stub

 

    }

    @Override

    publicvoid setContentLength(int arg0) {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicvoid setContentType(String arg0) {

       //TODO Auto-generatedmethod stub

 

    }

    @Override

    publicvoid setLocale(Locale arg0) {

       //TODO Auto-generatedmethod stub

 

    }

}

2.1.4 Processor

原书中没有这个类,这个类是一个抽象类(用它的好处是:可以面对接口编程,代码可以更容易扩展)

package ex02;

 

publicabstractclass Processor {

    publicabstractvoid process(HttpRequest request,HttpResponse response);

}

2.1.5StaticResourceProcessor

StaticResourceProcessor,顾名思义,就是处理静态资源的。

package ex02;

 

publicclass StaticResourceProcessorextends Processor {

    publicvoid process(HttpRequest request,HttpResponse response){

       response.sendStaticResource();

    }

}

2.1.6ServletProcessor1

这个类呢,就是处理Servlet请求的。需要注意的是:我们可以请求其他类似PrimitiveServlet的Servlet类,前提是需要将其class文件放在webroot目录下,在后续的章节中,会解决之歌问题。

假设客户端的请求是:http://localhost:7890/servlet/PrimitiveServlet,那么这个类的process()方法做的事情是:

1.      根据请求的url获取servletName,此例servletName的值为PrimitiveServlet

2.      接下来,该方法会载入以serverName为名称的类,为了载入该类,需要创建一个

一个类载入器,类载入器的构造函数如下图:


对于本章的应用程序而言,类载入器会到Contants.WEB_ROOT指定的目录下查找需要载入的类。

3.      在servlet中,类载入器查找servlet类的目录称为仓库(repository),在2中,URLClassLoader需要一个URL数组,可以使用下面的语句new:

URLStreamHandlerstreamHandler=null;

   String repository = (new URL("file",null,lassPath.getCanonicalPath()

                              + File.separator)).toString();

  URL[] urls=new URL[1];

  urls[0]=new URL(null,repository,streamHandler);

4.于是我们创建了URLClassLoader的实例,然后可以调用它的loadClass(String name)

   方法加载我们的Servlet类:

  Class myClass=null;  

Class=loader.loadClass(servletName);

Servlet servlet=null;

servlet=(Servlet) myClass.newInstance();

servlet.service((ServletRequest)request, (ServletResponse)response);

package ex02;

 

import java.io.File;

import java.io.IOException;

import java.net.URL;

import java.net.URLClassLoader;

import java.net.URLStreamHandler;

import javax.servlet.Servlet;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

publicclass ServletProcessor1extends Processor {

    publicvoid process(HttpRequest request,HttpResponse response){

       String uri=request.getUri();

       String servletName=uri.substring(uri.lastIndexOf("/")+1);

       URLClassLoader loader=null;

       try{

             URL[] urls = new URL[1];

             URLStreamHandlerstreamHandler =null;

             File classPath = new File(Constant.WEB_ROOT);

             String repository= (new URL("file",null, classPath.getCanonicalPath() + File.separator)).toString();

             urls[0] = new URL(null, repository,streamHandler);

             loader = newURLClassLoader(urls);

       }catch(Exception e){

           e.printStackTrace();

       }

       Class myClass=null;

       try{

           myClass=loader.loadClass(servletName);

       }catch(ClassNotFoundException e){

       e.printStackTrace();

       }

       Servlet servlet=null;

       try {

           servlet=(Servlet) myClass.newInstance();

           try {

              servlet.service((ServletRequest)request,(ServletResponse)response);

           } catch (ServletException e) {

              e.printStackTrace();

           } catch (IOException e) {

              e.printStackTrace();

           }

       } catch (InstantiationException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       }

    }

}

2.1.7Conants

package ex02;

 

import java.io.File;

 

publicclass Constants{

 

    publicstaticfinal StringWEB_ROOT = System.getProperty("user.dir")

                                         +File.separator+"webroot";

}

2.1.8运行

要想该程序达到预期目标,除了coding之外,还需将资源放在webroot目录下,例如:

 

我们可以使用下图命令对TestServlet.java编译:

 

2.3应用程序2

应用程序1存在一个严重的问题,请看如下代码:

Public voidprocess(HttpRequest request,HttpResponse response){

  //…

servlet=(Servlet) myClass.newInstance();

servlet.service((ServletRequest)request,(ServletResponse)response);

  //….

}

上段代码将HttpRequest和HttpResponse类的实例分别向上转型为ServletRequest和ServletResponse类的实例,并将它们作为参数传递给service方法。熟悉应用程序1的程序员可以这么做:

javax.servlet.ServletRequestmyServlet=new ex02.HttpRequest();//向上转型

       ex02.HttpRequest request=(ex02.HttpRequest) myServlet;//向下转型

       request.parse();

       request.getUri();

有了HttpRequest对象后,这里可以调用它的parsegeUri方法,同理可以调用HttpResponsesendStaticResource方法。我们可以使用外观类来解决这个问题。

HttpRequestFacade.java关键代码如下:

publicclass HttpRequestFacade implements ServletRequest {

 

    private ServletRequest request;

    public HttpRequestFacade(ServletRequest request) {

       this.request = request;

    }

//override ……

}

HttpResponseFacade.java与上述代码极为相似。

ServletProcessor2.java与应用程序1中的ServletProcessor1.java的区别在于:

 

servlet=(Servlet)myClass.newInstance();

HttpRequestFacaderequestFacade=new HttpRequestFacade(request);

HttpResponseFacaderesponseFacade=new HttpResponseFacade(response);

servlet.service((ServletRequest)requestFacade,(ServletResponse)responseFacade);

 

除此之外,并无不同之处。

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值