一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 基于Java web服務器簡單實現一個Servlet容器

基于Java web服務器簡單實現一個Servlet容器

2020-05-19 10:40風一樣的碼農 JAVA教程

這篇文章主要為大家詳細介紹了基于Java web服務器簡單實現一個Servlet容器,感興趣的小伙伴們可以參考一下

上篇寫了一個簡單的Java web服務器實現,只能處理一些靜態資源的請求,本篇文章實現的Servlet容器基于前面的服務器做了個小改造,增加了Servlet請求的處理。

 程序執行步驟
 1.創建一個ServerSocket對象;
 2.調用ServerSocket對象的accept方法,等待連接,連接成功會返回一個Socket對象,否則一直阻塞等待;
 3.從Socket對象中獲取InputStream和OutputStream字節流,這兩個流分別對應request請求和response響應;
 4.處理請求:讀取InputStream字節流信息,轉成字符串形式,并解析,這里的解析比較簡單,僅僅獲取uri(統一資源標識符)信息;
 5.處理響應(分兩種類型,靜態資源請求響應或servlet請求響應):如果是靜態資源請求,則根據解析出來的uri信息,從WEB_ROOT目錄中尋找請求的資源資源文件, 讀取資源文件,并將其寫入到OutputStream字節流中;如果是Servlet請求,則首先生成一個URLClassLoader類加載器,加載請求的servlet類,創建servlet對象,執行service方法(往OutputStream寫入響應的數據);
 6.關閉Socket對象;
 7.轉到步驟2,繼續等待連接請求; 

代碼實現:
 添加依賴: 

?
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.3</version>
</dependency>

服務器代碼: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package ex02.pyrmont.first;
 
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
 
import ex02.pyrmont.Request;
import ex02.pyrmont.Response;
import ex02.pyrmont.StaticResourceProcessor;
 
public class HttpServer1 {
 
 // 關閉服務命令
 private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
 
 public static void main(String[] args) {
 HttpServer1 server = new HttpServer1();
 //等待連接請求
 server.await();
 }
 
 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);
 }
 
 // 循環等待請求
 while (true) {
 Socket socket = null;
 InputStream input = null;
 OutputStream output = null;
 try {
 //等待連接,連接成功后,返回一個Socket對象
 socket = serverSocket.accept();
 input = socket.getInputStream();
 output = socket.getOutputStream();
 
 // 創建Request對象并解析
 Request request = new Request(input);
 request.parse();
 // 檢查是否是關閉服務命令
 if (request.getUri().equals(SHUTDOWN_COMMAND)) {
  break;
 }
 
 // 創建 Response 對象
 Response response = new Response(output);
 response.setRequest(request);
 
 if (request.getUri().startsWith("/servlet/")) {
  //請求uri以/servlet/開頭,表示servlet請求
  ServletProcessor1 processor = new ServletProcessor1();
  processor.process(request, response);
 } else {
  //靜態資源請求
  StaticResourceProcessor processor = new StaticResourceProcessor();
  processor.process(request, response);
 }
 
 // 關閉 socket
 socket.close();
 
 } catch (Exception e) {
 e.printStackTrace();
 System.exit(1);
 }
 }
 }
}

常量類: 

?
1
2
3
4
5
6
7
8
9
10
11
package ex02.pyrmont;
 
import java.io.File;
 
public class Constants {
 public static final String WEB_ROOT = System.getProperty("user.dir")
 + File.separator + "webroot";
 public static final String WEB_SERVLET_ROOT = System.getProperty("user.dir")
 + File.separator + "target" + File.separator + "classes";
 
}

Request: 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package ex02.pyrmont;
 
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
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;
 
public class Request implements ServletRequest {
 
 private InputStream input;
 private String uri;
 
 public Request(InputStream input) {
 this.input = input;
 }
 
 public String getUri() {
 return uri;
 }
 /**
 *
 * requestString形式如下:
 * GET /index.html HTTP/1.1
 * Host: localhost:8080
 * Connection: keep-alive
 * Cache-Control: max-age=0
 * ...
 * 該函數目的就是為了獲取/index.html字符串
 */
 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);
 }
 return null;
 }
 //從InputStream中讀取request信息,并從request中獲取uri值
 public void parse() {
 // Read a set of characters from the socket
 StringBuffer request = new StringBuffer(2048);
 int i;
 byte[] buffer = new byte[2048];
 try {
 i = input.read(buffer);
 } catch (IOException e) {
 e.printStackTrace();
 i = -1;
 }
 for (int j = 0; j < i; j++) {
 request.append((char) buffer[j]);
 }
 System.out.print(request.toString());
 uri = parseUri(request.toString());
 }
 
 /* implementation of the ServletRequest */
 public Object getAttribute(String attribute) {
 return null;
 }
 
 public Enumeration<?> getAttributeNames() {
 return null;
 }
 
 public String getRealPath(String path) {
 return null;
 }
 
 public RequestDispatcher getRequestDispatcher(String path) {
 return null;
 }
 
 public boolean isSecure() {
 return false;
 }
 
 public String getCharacterEncoding() {
 return null;
 }
 
 public int getContentLength() {
 return 0;
 }
 
 public String getContentType() {
 return null;
 }
 
 public ServletInputStream getInputStream() throws IOException {
 return null;
 }
 
 public Locale getLocale() {
 return null;
 }
 
 public Enumeration<?> getLocales() {
 return null;
 }
 
 public String getParameter(String name) {
 return null;
 }
 
 public Map<?, ?> getParameterMap() {
 return null;
 }
 
 public Enumeration<?> getParameterNames() {
 return null;
 }
 
 public String[] getParameterValues(String parameter) {
 return null;
 }
 
 public String getProtocol() {
 return null;
 }
 
 public BufferedReader getReader() throws IOException {
 return null;
 }
 
 public String getRemoteAddr() {
 return null;
 }
 
 public String getRemoteHost() {
 return null;
 }
 
 public String getScheme() {
 return null;
 }
 
 public String getServerName() {
 return null;
 }
 
 public int getServerPort() {
 return 0;
 }
 
 public void removeAttribute(String attribute) {
 }
 
 public void setAttribute(String key, Object value) {
 }
 
 public void setCharacterEncoding(String encoding)
 throws UnsupportedEncodingException {
 }
 
}

Response: 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package ex02.pyrmont;
 
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
 
public class Response implements ServletResponse {
 
 private static final int BUFFER_SIZE = 1024;
 Request request;
 OutputStream output;
 PrintWriter writer;
 
 public Response(OutputStream output) {
 this.output = output;
 }
 
 public void setRequest(Request request) {
 this.request = request;
 }
 
 //將web文件寫入到OutputStream字節流中
 public void sendStaticResource() throws IOException {
 byte[] bytes = new byte[BUFFER_SIZE];
 FileInputStream fis = null;
 try {
 /* request.getUri has been replaced by request.getRequestURI */
 File file = new File(Constants.WEB_ROOT, request.getUri());
 fis = new FileInputStream(file);
 /*
 * HTTP Response = Status-Line(( general-header | response-header |
 * entity-header ) CRLF) CRLF [ message-body ] Status-Line =
 * HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 */
 int ch = fis.read(bytes, 0, BUFFER_SIZE);
 while (ch != -1) {
 output.write(bytes, 0, ch);
 ch = fis.read(bytes, 0, BUFFER_SIZE);
 }
 } catch (FileNotFoundException e) {
 String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
  + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n"
  + "\r\n" + "<h1>File Not Found</h1>";
 output.write(errorMessage.getBytes());
 } finally {
 if (fis != null)
 fis.close();
 }
 }
 
 /** implementation of ServletResponse */
 public void flushBuffer() throws IOException {
 }
 
 public int getBufferSize() {
 return 0;
 }
 
 public String getCharacterEncoding() {
 return null;
 }
 
 public Locale getLocale() {
 return null;
 }
 
 public ServletOutputStream getOutputStream() throws IOException {
 return null;
 }
 
 public PrintWriter getWriter() throws IOException {
 // autoflush is true, println() will flush,
 // but print() will not.
 writer = new PrintWriter(output, true);
 return writer;
 }
 
 public boolean isCommitted() {
 return false;
 }
 
 public void reset() {
 }
 
 public void resetBuffer() {
 }
 
 public void setBufferSize(int size) {
 }
 
 public void setContentLength(int length) {
 }
 
 public void setContentType(String type) {
 }
 
 public void setLocale(Locale locale) {
 }
}

靜態資源請求處理: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ex02.pyrmont;
 
import java.io.IOException;
 
public class StaticResourceProcessor {
 
 public void process(Request request, Response response) {
 try {
 response.sendStaticResource();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
}

Servlet請求處理: 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package ex02.pyrmont.first;
 
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.IOException;
 
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
import ex02.pyrmont.Constants;
import ex02.pyrmont.Request;
import ex02.pyrmont.Response;
 
public class ServletProcessor1 {
 
 public void process(Request request, Response response) {
 
 String uri = request.getUri();
 String servletName = uri.substring(uri.lastIndexOf("/") + 1);
 
 //類加載器,用于從指定JAR文件或目錄加載類
 URLClassLoader loader = null;
 try {
 URLStreamHandler streamHandler = null;
 //創建類加載器
 loader = new URLClassLoader(new URL[]{new URL(null, "file:" + Constants.WEB_SERVLET_ROOT, streamHandler)});
 } catch (IOException e) {
 System.out.println(e.toString());
 }
 
 Class<?> myClass = null;
 try {
 //加載對應的servlet類
 myClass = loader.loadClass(servletName);
 } catch (ClassNotFoundException e) {
 System.out.println(e.toString());
 }
 
 Servlet servlet = null;
 
 try {
 //生產servlet實例
 servlet = (Servlet) myClass.newInstance();
 //執行ervlet的service方法
 servlet.service((ServletRequest) request,(ServletResponse) response);
 } catch (Exception e) {
 System.out.println(e.toString());
 } catch (Throwable e) {
 System.out.println(e.toString());
 }
 
 }
}

Servlet類: 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
 
public class PrimitiveServlet implements Servlet {
 
 public void init(ServletConfig config) throws ServletException {
 System.out.println("init");
 }
 
 public void service(ServletRequest request, ServletResponse response)
 throws ServletException, IOException {
 System.out.println("from service");
 PrintWriter out = response.getWriter();
 out.println("Hello. Roses are red.");
 out.print("Violets are blue.");
 }
 
 public void destroy() {
 System.out.println("destroy");
 }
 
 public String getServletInfo() {
 return null;
 }
 
 public ServletConfig getServletConfig() {
 return null;
 }
 
}

結果測試:
 靜態資源請求:

基于Java web服務器簡單實現一個Servlet容器

servlet請求(因為只是第一個字符串被刷新到瀏覽器,所以你不能看到第二個字符串Violets are blue。我們將在后續完善該容器):

基于Java web服務器簡單實現一個Servlet容器

改進

前面實現的Servlet容器有一個嚴重的問題,用戶在servlet里可以直接將ServletRequest、ServletResponse向下轉 型為Request和Response類型,并直接調用其內部的public方法,這是一個不好的設計,改進方法是給Request、Response 增加外觀類,這樣,用戶只能訪問外觀類里定義的public方法。
 Request外觀類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package ex02.pyrmont.second;
 
import java.io.IOException;
import java.io.BufferedReader;
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;
 
import ex02.pyrmont.Request;
 
public class RequestFacade implements ServletRequest {
 
 private ServletRequest request = null;
 
 public RequestFacade(Request request) {
 this.request = request;
 }
 
 /* implementation of the ServletRequest */
 public Object getAttribute(String attribute) {
 return request.getAttribute(attribute);
 }
 
 public Enumeration<?> getAttributeNames() {
 return request.getAttributeNames();
 }
 
 @SuppressWarnings("deprecation")
 public String getRealPath(String path) {
 return request.getRealPath(path);
 }
 
 public RequestDispatcher getRequestDispatcher(String path) {
 return request.getRequestDispatcher(path);
 }
 
 public boolean isSecure() {
 return request.isSecure();
 }
 
 public String getCharacterEncoding() {
 return request.getCharacterEncoding();
 }
 
 public int getContentLength() {
 return request.getContentLength();
 }
 
 public String getContentType() {
 return request.getContentType();
 }
 
 public ServletInputStream getInputStream() throws IOException {
 return request.getInputStream();
 }
 
 public Locale getLocale() {
 return request.getLocale();
 }
 
 public Enumeration<?> getLocales() {
 return request.getLocales();
 }
 
 public String getParameter(String name) {
 return request.getParameter(name);
 }
 
 public Map<?, ?> getParameterMap() {
 return request.getParameterMap();
 }
 
 public Enumeration<?> getParameterNames() {
 return request.getParameterNames();
 }
 
 public String[] getParameterValues(String parameter) {
 return request.getParameterValues(parameter);
 }
 
 public String getProtocol() {
 return request.getProtocol();
 }
 
 public BufferedReader getReader() throws IOException {
 return request.getReader();
 }
 
 public String getRemoteAddr() {
 return request.getRemoteAddr();
 }
 
 public String getRemoteHost() {
 return request.getRemoteHost();
 }
 
 public String getScheme() {
 return request.getScheme();
 }
 
 public String getServerName() {
 return request.getServerName();
 }
 
 public int getServerPort() {
 return request.getServerPort();
 }
 
 public void removeAttribute(String attribute) {
 request.removeAttribute(attribute);
 }
 
 public void setAttribute(String key, Object value) {
 request.setAttribute(key, value);
 }
 
 public void setCharacterEncoding(String encoding)
  throws UnsupportedEncodingException {
 request.setCharacterEncoding(encoding);
 }
 
}

Response外觀類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package ex02.pyrmont.second;
 
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
 
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
 
import ex02.pyrmont.Response;
 
public class ResponseFacade implements ServletResponse {
 
 private ServletResponse response;
 
 public ResponseFacade(Response response) {
 this.response = response;
 }
 
 public void flushBuffer() throws IOException {
 response.flushBuffer();
 }
 
 public int getBufferSize() {
 return response.getBufferSize();
 }
 
 public String getCharacterEncoding() {
 return response.getCharacterEncoding();
 }
 
 public Locale getLocale() {
 return response.getLocale();
 }
 
 public ServletOutputStream getOutputStream() throws IOException {
 return response.getOutputStream();
 }
 
 public PrintWriter getWriter() throws IOException {
 return response.getWriter();
 }
 
 public boolean isCommitted() {
 return response.isCommitted();
 }
 
 public void reset() {
 response.reset();
 }
 
 public void resetBuffer() {
 response.resetBuffer();
 }
 
 public void setBufferSize(int size) {
 response.setBufferSize(size);
 }
 
 public void setContentLength(int length) {
 response.setContentLength(length);
 }
 
 public void setContentType(String type) {
 response.setContentType(type);
 }
 
 public void setLocale(Locale locale) {
 response.setLocale(locale);
 }
 
}

處理Servlet請求類: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package ex02.pyrmont.second;
 
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.IOException;
 
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
import ex02.pyrmont.Constants;
import ex02.pyrmont.Request;
import ex02.pyrmont.Response;
 
public class ServletProcessor2 {
 
 public void process(Request request, Response response) {
 
 String uri = request.getUri();
 String servletName = uri.substring(uri.lastIndexOf("/") + 1);
 // 類加載器,用于從指定JAR文件或目錄加載類
 URLClassLoader loader = null;
 try {
  URLStreamHandler streamHandler = null;
  // 創建類加載器
  loader = new URLClassLoader(new URL[] { new URL(null, "file:"
   + Constants.WEB_SERVLET_ROOT, streamHandler) });
 } catch (IOException e) {
  System.out.println(e.toString());
 }
 
 Class<?> myClass = null;
 try {
  // 加載對應的servlet類
  myClass = loader.loadClass(servletName);
 } catch (ClassNotFoundException e) {
  System.out.println(e.toString());
 }
 
 Servlet servlet = null;
 //給request、response增加外觀類,安全性考慮,防止用戶在servlet里直接將ServletRequest、ServletResponse向下轉型為Request和Response類型,
 //并直接調用其內部的public方法,因為RequestFacade、ResponseFacade里不會有parse、sendStaticResource等方法;
 RequestFacade requestFacade = new RequestFacade(request);
 ResponseFacade responseFacade = new ResponseFacade(response);
 try {
  servlet = (Servlet) myClass.newInstance();
  servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade);
 } catch (Exception e) {
  System.out.println(e.toString());
 } catch (Throwable e) {
  System.out.println(e.toString());
 }
 
 }
}

其它代碼與前面實現的Servlet容器基本一致。
 驗證程序,分別請求靜態資源和Servlet,發現結果與前面實現的容器一致;

 參考資料:《深入剖析Tomcat》

@author   風一樣的碼農

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/chenpi/archive/2016/06/21/5603072.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 女王脚奴vk | 国产福利视频一区二区微拍 | 亚洲AV国产国产久青草 | www.99热| 肉搏潘金莲三级18春 | 香蕉在线播放 | 国产91第一页| 国产精品主播在线 | 波多野结衣同性系列698 | 好大好粗好舒服 | 91视频国产一区 | 羞羞视频免费观看网站 | 香蕉免费看一区二区三区 | 日韩日日操 | 成熟女人50岁一级毛片不卡 | 动漫jk美女被爆羞羞漫画 | 国产精品边做边接电话在线观看 | 国产精亚洲视频 | 星空无限传媒xk8129 | 日本视频在线播放 | 干操视频| 激情图片 激情小说 | 91久久偷偷做嫩草影院免费 | 皇上撞着太子妃的秘密小说 | 国产日韩精品一区二区在线观看播放 | 欧美一级视频免费观看 | 毛片资源 | www亚洲视频| 天天色天天舔 | 欧美日韩专区国产精品 | 6080窝窝理论 | 魔镜号中文字幕 | 爱情岛论坛亚洲永久入口口 | 欧美操大逼视频 | 五月色综合婷婷综合俺来也 | 日本大尺度动漫在线观看缘之空 | 久久精品亚洲热综合一本 | 色欧美在线 | 性夜a爽黄爽 | 免费草比视频 | 草草草草视频 |