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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 女同学高中你下面好紧 | 色就色综合 | 免费日批视频 | 东京干福利 | 欧美香蕉人人人人人人爱 | 99在线精品日韩一区免费国产 | 惩罚狠h调教灌满 | 精品国产成人AV在线看 | 明星ai人脸替换造梦在线播放 | 国产精品欧美亚洲韩国日本99 | 四虎国产| 国产午夜永久福利视频在线观看 | 日本免费在线观看 | 厨房高h | 国产精品videosse | 激情小视频网站 | 91短视频在线观看2019 | 99久久精品免费看国产高清 | 免费高清资源黄网站在线观看 | 色婷在线 | 91高清免费国产自产 | 白丝女仆被啪到深夜漫画 | 国产伦精品一区二区 | 双性np肉文 | 女女同性做爰xxoo亲吻 | 女人用粗大自熨喷水在线视频 | poronovideos极度残酷 | 久久re视频这里精品一本到99 | 国产精品一级视频 | 免费的强动漫人物的 | a色在线| av91在线| 狠狠色狠狠色综合日日小蛇 | 日日射视频 | 亚洲不卡视频 | 西西人体大胆77777视频 | 韩国三级在线高速影院 | 日本九九视频 | 99热久热这里只精品 | 奇米影视77777| 偷拍综合网 |