Tomcat深入剖析学习02

Tomcat深入剖析学习02-实现一个简单的Servlet容器

一.什么是Servlet

  Servlet是sun公司提供的一门用于开发动态web资源的技术。
  Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
  1、编写一个Java类,实现servlet接口。
  2、把开发好的Java类部署到web服务器中。
  按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet

二.Servlet容器

  就是创建、管理servlet规范中相关对象、生命周期的应用程序。Tomcat就是一种Servlet容器。

三.相关类

  javax.servlet.Servlet接口

  所有Servlet类必须实现此Servlet接口,实现关于Servlet接口相关方法。这些方法如下:

    1.init():实例化某个servlet类后,容器调用对应的init方法进行初始化,该方法只会被调用一次;
    2.service():当servlet的一个客户端请求到达后,容器就开始调用对应servlet的service方法。同时需要传入参数servletRequest和servletResponse。

          从字面意思就能知道,servletRequest携带了客户端发送的HTTP请求的信息,而servletResponse则用于封装servlet的响应信息。

    3.destroy():当servlet实例调用完毕要被移除时,destroy方法将被调用。

    4.getServletConfig():该方法用于取得<servlet> <init-param>配置的参数
    5.getServletInfo():该方法提供有关servlet的信息,如作者、版本、版权。

  **其中1,2,3与Servlet类生命周期相关

四.本节应用程序实现类

    本节servlet容器程序包含六个类,与01节对比,HttpServer1主程序已经做了一些功能解耦,具体说明如下。

    1.HttpServer1:servlet容器核心程序,包括:主程序入口,服务器创建与启动,request/response创建,按请求uri调用不同的处理类

    2.Request:实现javax.servlet.ServletRequest接口,以及01节中自己实现的解析方法

    3.Response:实现javax.servlet.ServletResponse接口,

    4.StaticResourceProcessor:静态资源处理类,处理uri是静态资源的请求

    5.ServletProcessor1:Servlet处理类,处理uri形如/servlet/servletname的请求(动态资源请求)

    6.Constants:常量类,将01节中服务器的常量部分功能拆解出来,包含了文根信息。

    此外,既然是Servlet容器,我们还需定义一个Servlet类PrimitiveServlet实现Servlet接口。

 五.具体代码

    1.PrimitiveServlet:主要实现了init,service,destroy方法   

 1 package com.liuwei.ServletServer1;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 
 6 import javax.servlet.Servlet;
 7 import javax.servlet.ServletConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 
12 /**
13  * 
14  * @author liuwei 
15  * 继承了servlet接口,重写5个方法
16  */
17 public class PrimitiveServlet implements Servlet{
18 
19     @Override
20     public void destroy() {
21         System.out.println("destroy");
22     }
23 
24     @Override
25     public ServletConfig getServletConfig() {
26         return null;
27     }
28 
29     @Override
30     public String getServletInfo() {
31         return null;
32     }
33 
34     @Override
35     public void init(ServletConfig servletConfig) throws ServletException {
36         System.out.println("init");
37     }
38    //service方法,浏览器显示相关信息
39     @Override
40     public void service(ServletRequest arg0, ServletResponse arg1)
41             throws ServletException, IOException {
42         System.out.println("from service");
43         PrintWriter pw = arg1.getWriter();
44         pw.println("this is primitiveServlet!");
45     }
46 
47 }

  2.Request类:接口的类没有具体实现,保留了01节中的解析方法parse,parseuri

  1 package com.liuwei.ServletServer1;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.UnsupportedEncodingException;
  7 import java.util.Enumeration;
  8 import java.util.Locale;
  9 import java.util.Map;
 10 
 11 import javax.servlet.AsyncContext;
 12 import javax.servlet.DispatcherType;
 13 import javax.servlet.RequestDispatcher;
 14 import javax.servlet.ServletContext;
 15 import javax.servlet.ServletInputStream;
 16 import javax.servlet.ServletRequest;
 17 import javax.servlet.ServletResponse;
 18 
 19 //HTTP请求类
 20 public class Request implements ServletRequest{
 21     //请求对象中的输入流对象
 22     private InputStream input;
 23     
 24     private String uri;
 25     //构造函数
 26     public Request(InputStream input){
 27         this.input = input;
 28     }
 29     //解析请求的函数
 30     public void parse(){
 31         StringBuffer request = new StringBuffer();
 32         byte[] buffer = new byte[2038];
 33         int i=-1;
 34         try{
 35             i = input.read(buffer);
 36         }catch(Exception e){
 37             e.printStackTrace();
 38         }
 39         for(int j=0;j<i;j++){
 40             request.append((char)buffer[j]);
 41         }
 42         System.out.println(request.toString());
 43         this.uri = parseUri(request.toString());
 44     }
 45     //分析请求的静态资源uri,从两个空格间获取
 46     public String parseUri(String request){
 47         int index1,index2;
 48         index1 = request.indexOf(" ");
 49         if (index1 != -1){
 50             index2 = request.indexOf(" ", index1+1);
 51             if (index2>index1){
 52                 return request.substring(index1+1,index2);
 53             }
 54         }
 55         return null;
 56     }
 57     public String getUri(){
 58         return uri;
 59     }
 60     @Override
 61     public AsyncContext getAsyncContext() {
 62         // TODO Auto-generated method stub
 63         return null;
 64     }
 65     @Override
 66     public Object getAttribute(String arg0) {
 67         // TODO Auto-generated method stub
 68         return null;
 69     }
 70     @Override
 71     public Enumeration<String> getAttributeNames() {
 72         // TODO Auto-generated method stub
 73         return null;
 74     }
 75     @Override
 76     public String getCharacterEncoding() {
 77         // TODO Auto-generated method stub
 78         return null;
 79     }
 80     @Override
 81     public int getContentLength() {
 82         // TODO Auto-generated method stub
 83         return 0;
 84     }
 85     @Override
 86     public String getContentType() {
 87         // TODO Auto-generated method stub
 88         return null;
 89     }
 90     @Override
 91     public DispatcherType getDispatcherType() {
 92         // TODO Auto-generated method stub
 93         return null;
 94     }
 95     @Override
 96     public ServletInputStream getInputStream() throws IOException {
 97         // TODO Auto-generated method stub
 98         return null;
 99     }
100     @Override
101     public String getLocalAddr() {
102         // TODO Auto-generated method stub
103         return null;
104     }
105     @Override
106     public String getLocalName() {
107         // TODO Auto-generated method stub
108         return null;
109     }
110     @Override
111     public int getLocalPort() {
112         // TODO Auto-generated method stub
113         return 0;
114     }
115     @Override
116     public Locale getLocale() {
117         // TODO Auto-generated method stub
118         return null;
119     }
120     @Override
121     public Enumeration<Locale> getLocales() {
122         // TODO Auto-generated method stub
123         return null;
124     }
125     @Override
126     public String getParameter(String arg0) {
127         // TODO Auto-generated method stub
128         return null;
129     }
130     @Override
131     public Map<String, String[]> getParameterMap() {
132         // TODO Auto-generated method stub
133         return null;
134     }
135     @Override
136     public Enumeration<String> getParameterNames() {
137         // TODO Auto-generated method stub
138         return null;
139     }
140     @Override
141     public String[] getParameterValues(String arg0) {
142         // TODO Auto-generated method stub
143         return null;
144     }
145     @Override
146     public String getProtocol() {
147         // TODO Auto-generated method stub
148         return null;
149     }
150     @Override
151     public BufferedReader getReader() throws IOException {
152         // TODO Auto-generated method stub
153         return null;
154     }
155     @Override
156     public String getRealPath(String arg0) {
157         // TODO Auto-generated method stub
158         return null;
159     }
160     @Override
161     public String getRemoteAddr() {
162         // TODO Auto-generated method stub
163         return null;
164     }
165     @Override
166     public String getRemoteHost() {
167         // TODO Auto-generated method stub
168         return null;
169     }
170     @Override
171     public int getRemotePort() {
172         // TODO Auto-generated method stub
173         return 0;
174     }
175     @Override
176     public RequestDispatcher getRequestDispatcher(String arg0) {
177         // TODO Auto-generated method stub
178         return null;
179     }
180     @Override
181     public String getScheme() {
182         // TODO Auto-generated method stub
183         return null;
184     }
185     @Override
186     public String getServerName() {
187         // TODO Auto-generated method stub
188         return null;
189     }
190     @Override
191     public int getServerPort() {
192         // TODO Auto-generated method stub
193         return 0;
194     }
195     @Override
196     public ServletContext getServletContext() {
197         // TODO Auto-generated method stub
198         return null;
199     }
200     @Override
201     public boolean isAsyncStarted() {
202         // TODO Auto-generated method stub
203         return false;
204     }
205     @Override
206     public boolean isAsyncSupported() {
207         // TODO Auto-generated method stub
208         return false;
209     }
210     @Override
211     public boolean isSecure() {
212         // TODO Auto-generated method stub
213         return false;
214     }
215     @Override
216     public void removeAttribute(String arg0) {
217         // TODO Auto-generated method stub
218         
219     }
220     @Override
221     public void setAttribute(String arg0, Object arg1) {
222         // TODO Auto-generated method stub
223         
224     }
225     @Override
226     public void setCharacterEncoding(String arg0)
227             throws UnsupportedEncodingException {
228         // TODO Auto-generated method stub
229         
230     }
231     @Override
232     public AsyncContext startAsync() {
233         // TODO Auto-generated method stub
234         return null;
235     }
236     @Override
237     public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1) {
238         // TODO Auto-generated method stub
239         return null;
240     }
241 }

  3.Response类:

  1 package com.liuwei.ServletServer1;
  2 
  3 import java.io.File;
  4 import java.io.FileInputStream;
  5 import java.io.IOException;
  6 import java.io.OutputStream;
  7 import java.io.PrintWriter;
  8 import java.util.Locale;
  9 
 10 import javax.servlet.ServletOutputStream;
 11 import javax.servlet.ServletResponse;
 12 
 13 public class Response implements ServletResponse{
 14     
 15     private Request request;
 16     public OutputStream output;
 17     private static final int BufferLength = 1024;
 18     private PrintWriter pw;
 19     
 20     public Response(OutputStream output){
 21         this.output = output;
 22     }
 23     public void setRequest(Request request){
 24         this.request = request;
 25     }
 26     //发送静态资源方法
 27     public void sendStaticResource() throws IOException {
 28         byte[] buffer = new byte[BufferLength];
 29         FileInputStream fis = null;
 30         try{
 31             //查找请求中资源是否在root文件夹中存在,存在则写到output中,未存在在output写404信息
 32             File file = new File(Constants.WEB_ROOT,request.getUri());
 33             if(file.exists()){
 34                 fis = new FileInputStream(file);
 35                 int num = fis.read(buffer,0,BufferLength);
 36                 if(num!=-1){
 37                     output.write(buffer,0,num);
 38                     fis.read(buffer,0,BufferLength);
 39                 }
 40             }else{
 41                 String errorMsg = "HTTP/1.1 404 File Not Found\r\n" +
 42                                 "Content-Type:text/html\r\n" +
 43                                 "Content-Length:23\r\n" +
 44                                 "\r\n" +
 45                                 "<h1>File Not Found</h1>";
 46                 output.write(errorMsg.getBytes());
 47             }
 48         }catch(Exception ex){
 49             ex.printStackTrace();
 50         }finally{
 51             if(null!=fis){
 52                 fis.close();
 53             }
 54         }
 55     }
 56     @Override
 57     public void flushBuffer() throws IOException {
 58         // TODO Auto-generated method stub
 59         
 60     }
 61     @Override
 62     public int getBufferSize() {
 63         // TODO Auto-generated method stub
 64         return 0;
 65     }
 66     @Override
 67     public String getCharacterEncoding() {
 68         // TODO Auto-generated method stub
 69         return null;
 70     }
 71     @Override
 72     public String getContentType() {
 73         // TODO Auto-generated method stub
 74         return null;
 75     }
 76     @Override
 77     public Locale getLocale() {
 78         // TODO Auto-generated method stub
 79         return null;
 80     }
 81     @Override
 82     public ServletOutputStream getOutputStream() throws IOException {
 83         // TODO Auto-generated method stub
 84         return null;
 85     }
 86     @Override
 87     public PrintWriter getWriter() throws IOException {
 88         pw = new PrintWriter(output,true);
 89         return pw;
 90     }
 91     @Override
 92     public boolean isCommitted() {
 93         // TODO Auto-generated method stub
 94         return false;
 95     }
 96     @Override
 97     public void reset() {
 98         // TODO Auto-generated method stub
 99         
100     }
101     @Override
102     public void resetBuffer() {
103         // TODO Auto-generated method stub
104         
105     }
106     @Override
107     public void setBufferSize(int arg0) {
108         // TODO Auto-generated method stub
109         
110     }
111     @Override
112     public void setCharacterEncoding(String arg0) {
113         // TODO Auto-generated method stub
114         
115     }
116     @Override
117     public void setContentLength(int arg0) {
118         // TODO Auto-generated method stub
119         
120     }
121     @Override
122     public void setContentType(String arg0) {
123         // TODO Auto-generated method stub
124         
125     }
126     @Override
127     public void setLocale(Locale arg0) {
128         // TODO Auto-generated method stub
129         
130     }
131 }
Response.java

  4.ServletProcessor类:负责根据请求uri创建对应的servlet类,初始化并调用其service方法,由此可见,对应请求来临时,才会加载对应类实例

 1 package com.liuwei.ServletServer1;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.net.URL;
 6 import java.net.URLClassLoader;
 7 import java.net.URLStreamHandler;
 8 
 9 import javax.servlet.Servlet;
10 import javax.servlet.ServletRequest;
11 import javax.servlet.ServletResponse;
12 
13 /**
14  * servlet加载类
15  * 处理uri形如/servlet/servletName请求
16  * @author DELL
17  *
18  */
19 public class ServletProcessor1 {
20     //分析request中的uri,并用类加载器加载
21     public void process(Request request, Response response){
22         //解析uri中的servletName
23         String uri = request.getUri();
24         String servletName = "com.liuwei.ServletServer1."+uri.substring(uri.lastIndexOf('/')+1);
25         URLClassLoader loader = null;
26         try{
27             //**创建类载入器loader
28             URL[] urls = new URL[1];
29             URLStreamHandler streamHandler = null;
30             File classPath = new File(Constants.WEB_ROOT);
31             //servlet容器中,类载入器查找servlet类的目录称为仓库(repository)
32             String repository = (new URL("file",null,classPath.getCanonicalPath()+File.separator)).toString();
33             urls[0] = new URL(null,repository,streamHandler);
34             loader = new URLClassLoader(urls);
35         }catch(IOException ex){
36             ex.printStackTrace();
37         }
38         //载入对应的servlet类
39         Class myClass = null;
40         try{
41             myClass = loader.loadClass(servletName);
42         }catch(ClassNotFoundException ex){
43             ex.printStackTrace();
44         }
45         //实例化载入的servlet类
46         Servlet servlet = null;
47         try{
48             servlet = (Servlet)myClass.newInstance();
49             servlet.service((ServletRequest)request, (ServletResponse)response);
50         }catch(Exception e){
51             System.out.println(e.toString());
52         }catch(Throwable e){
53             System.out.println(e.toString());
54         }
55     }
56     
57 }
ServletProcessor.java

  5.StaticResourceProcessor类:静态资源处理类

 1 package com.liuwei.ServletServer1;
 2 
 3 import java.io.IOException;
 4 
 5 /**
 6  * 静态资源处理类
 7  * @author DELL
 8  *
 9  */
10 public class StaticResourceProcessor {
11     //实质还是调用了response的sendStaticResource方法
12     public void process(Request request,Response response){
13         try{
14             response.sendStaticResource();
15         }catch(IOException e){
16             System.out.println(e.toString());
17         }
18     }
19 }
StaticResourceProcessor

  6.Constants类:

 1 package com.liuwei.ServletServer1;
 2 
 3 import java.io.File;
 4 
 5 
 6 public class Constants {
 7     //服务器的web项目所在目录
 8         public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
 9     //servlet所在地址
10         public static final String SERVLET_ROOT = System.getProperty("user.dir") + File.separator + "webroot" +File.separator+"com"+File.separator+"liuwei"+File.separator+"ServletServer1";
11 }
Constants.java

  7.HttpServer1类:核心容器

 1 package com.liuwei.ServletServer1;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.OutputStream;
 7 import java.net.InetAddress;
 8 import java.net.ServerSocket;
 9 import java.net.Socket;
10 
11 public class HttpServer1 {
12 
13     //shutdown command
14     private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
15     
16     //the shutdown command received
17     private static boolean shutdown = false;
18     
19     public static void main(String[] args) {
20         HttpServer1 server = new HttpServer1();
21         server.await();
22     }
23     //服务器启动方法
24     public void await(){
25         ServerSocket serverSocket = null;
26         int port = 8888;
27         try{
28             serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
29         }catch(IOException ex){
30             ex.printStackTrace();
31             System.exit(1);
32         }
33         //此处判断shutdown标志
34         while(!shutdown){
35             Socket socket = null;
36             InputStream input = null;
37             OutputStream output = null;
38             try{
39                 //阻塞式等待一个请求,并返回请求端的socket对象
40                 socket = serverSocket.accept();
41                 //获取socket对象中的输入输出流对象
42                 input = socket.getInputStream();
43                 output = socket.getOutputStream();
44                 
45                 //create request object and parse
46                 Request request = new Request(input);
47                 request.parse();
48                 System.out.println(request.getUri());
49                 
50                 //create response object
51                 Response response = new Response(output);
52                 response.setRequest(request);
53                 //response.sendStaticResource();
54                 
55                 //check if request is a servletRequest or a staticResourceRequest
56                 //简单的http请求分发,分为servlet处理器和静态资源处理器
57                 if(request.getUri().startsWith("/servlet/")){
58                     ServletProcessor1 processor = new ServletProcessor1();
59                     processor.process(request,response);
60                 }else{
61                     StaticResourceProcessor processor = new StaticResourceProcessor();
62                     processor.process(request,response);
63                 }
64                 //socket close
65                 socket.close();
66                 
67                 //check if requesturl is shutdown command
68                 shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
69             }catch(Exception e){
70                 e.printStackTrace();
71                 continue;
72             }
73         }
74     }
75 }
HttpServer1.java

六.工作流程

如下图所示:

此时,02节实现的servlet容器仍然负责一系列核心流程,包括:程序入口,服务创建,请求-响应对象创建,处理类分发。

注意处理类分发是02节的servlet容器所多出来的部分,由于除了静态资源的处理,还要对servlet请求处理,容器按照不同的请求uri,调用了不同的处理类。

此时对比01节,将如下两部分解耦,服务器可访问常量保存在了特定类Constants,处理逻辑分别形成了2个类StaticResourceProcessor和ServletProcessor。是功能的第一次拆分解耦。

此处注意ServletProcessor类,它在第8步会自动根据请求uri加载对应的servlet类,并调用其service方法。如果我们把02这样一个简单的servlet容器提供出来,那么编程人员就可以编写不同的servlet类来实现自己的功能,返回对应的结果。这就是tomcat服务器的雏形。

posted on 2017-07-11 15:00  坚持就是胜利啊  阅读(376)  评论(0编辑  收藏  举报

导航