Java Web Servlet 体系结构(一)

Servlet 的体系结构中的 Servlet 接口、GenericServlet 抽象类和 HttpServlet 抽象类。


通过实现 Servlet 接口

先来看看通过 Servlet 接口需要实现哪些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package javax.servlet;

import java.io.IOException;

public interface Servlet {
void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();
}

共 5 个抽象方法,试着实现一个最简单的 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
@WebServlet(urlPatterns = "/api")
public class ServletImpl implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("ServletImpl.init");
}

@Override
public ServletConfig getServletConfig() {
System.out.println("ServletImpl.getServletConfig");
return null;
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello world");
}

@Override
public String getServletInfo() {
System.out.println("ServletImpl.getServletInfo");
return null;
}

@Override
public void destroy() {
System.out.println("ServletImpl.destroy");
}
}

代码不少,但是通常我们最关心是 service 方法,较关心 initdestroy 方法,基本不关心 getServletConfiggetServletInfo 方法。

自然而然的想到可以创建一个抽象类实现 Servlet 接口,只保留 service 抽象方法,其余四个方法采用“空实现”。将来要创建 Servlet 只需要继承这个抽象类来实现 service 方法即可。这就引入了 Servlet 体系结构中的 GenericServlet 抽象类。


通过继承抽象类 GenericServlet

翻看 GenericServlet 源码(有删减),关于 Servlet 接口的 5 个方法,除了 service 仍然是 abstract 以外,其余 4 个均才有我们希望的“空实现”:

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
package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private transient ServletConfig config;

public GenericServlet() {
}

public void destroy() {
}

public ServletConfig getServletConfig() {
return this.config;
}

public String getServletInfo() {
return "";
}

public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}

public void init() throws ServletException {
}

public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}

有了这么一个类,意味着我们可以通过继承 GenericServlet 的方式来实现 Servlet ,而无须关心 service 以外的 Servlet 方法:

1
2
3
4
5
6
7
@WebServlet(urlPatterns = "/api")
public class ServletImpl extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello world");
}
}

现在剩下的问题是如何让 Servlet 针对特定的 HTTP Method 进行处理,目前的实现是能够无区别处理所有的 HTTP Method 。一种不太现实的方法是先将传入 ServletRequest 对象转换为 HttpServletRequest 对象,再通过 getMethod() 方法进行获取到当前的 HTTP Method 再区别处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@WebServlet(urlPatterns = "/api")
public class ServletImpl extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if ("GET".equals(method)) {
System.out.println("GET hello world");
} else if ("POST".equals(method)) {
System.out.println("POST hello world");
} else {
System.out.println("Other hello world");
}
}
}
}

与当初期望 GenericServlet 抽象类一样,我们希望能有一个针对 HTTP 协议的抽象类,能够帮我们处理 ServletRequestServletResponse 的转换问题,以及不同 HTTP Method 的区别处理问题,这就引入了 Servlet 体系结构中另一个抽象类 HttpServlet


通过继承抽象类 HttpServlet

HttpServlet 继承自 GenericServlet ,是针对 HTTP 协议的对 Servlet 的进一步封装。

翻看 HttpServlet 源码:

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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package javax.servlet.http;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.DispatcherType;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public abstract class HttpServlet extends GenericServlet {
private static final long serialVersionUID = 1L;
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
private static final String HEADER_IFMODSINCE = "If-Modified-Since";
private static final String HEADER_LASTMOD = "Last-Modified";
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static final ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");

public HttpServlet() {
}

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

protected long getLastModified(HttpServletRequest req) {
return -1L;
}

protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
this.doGet(req, resp);
} else {
NoBodyResponse response = new NoBodyResponse(resp);
this.doGet(req, response);
response.setContentLength();
}

}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_put_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_delete_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}

}

private static Method[] getAllDeclaredMethods(Class<?> c) {
if (c.equals(HttpServlet.class)) {
return null;
} else {
Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
Method[] thisMethods = c.getDeclaredMethods();
if (parentMethods != null && parentMethods.length > 0) {
Method[] allMethods = new Method[parentMethods.length + thisMethods.length];
System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length);
System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length);
thisMethods = allMethods;
}

return thisMethods;
}
}

protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Method[] methods = getAllDeclaredMethods(this.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false;
boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
Class clazz = null;

try {
clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
Method getAllowTrace = clazz.getMethod("getAllowTrace", (Class[])null);
ALLOW_TRACE = (Boolean)getAllowTrace.invoke(req, (Object[])null);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException var14) {
}

for(int i = 0; i < methods.length; ++i) {
Method m = methods[i];
if (m.getName().equals("doGet")) {
ALLOW_GET = true;
ALLOW_HEAD = true;
}

if (m.getName().equals("doPost")) {
ALLOW_POST = true;
}

if (m.getName().equals("doPut")) {
ALLOW_PUT = true;
}

if (m.getName().equals("doDelete")) {
ALLOW_DELETE = true;
}
}

String allow = null;
if (ALLOW_GET) {
allow = "GET";
}

if (ALLOW_HEAD) {
if (allow == null) {
allow = "HEAD";
} else {
allow = allow + ", HEAD";
}
}

if (ALLOW_POST) {
if (allow == null) {
allow = "POST";
} else {
allow = allow + ", POST";
}
}

if (ALLOW_PUT) {
if (allow == null) {
allow = "PUT";
} else {
allow = allow + ", PUT";
}
}

if (ALLOW_DELETE) {
if (allow == null) {
allow = "DELETE";
} else {
allow = allow + ", DELETE";
}
}

if (ALLOW_TRACE) {
if (allow == null) {
allow = "TRACE";
} else {
allow = allow + ", TRACE";
}
}

if (ALLOW_OPTIONS) {
if (allow == null) {
allow = "OPTIONS";
} else {
allow = allow + ", OPTIONS";
}
}

resp.setHeader("Allow", allow);
}

protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String CRLF = "\r\n";
StringBuilder buffer = (new StringBuilder("TRACE ")).append(req.getRequestURI()).append(" ").append(req.getProtocol());
Enumeration reqHeaderEnum = req.getHeaderNames();

while(reqHeaderEnum.hasMoreElements()) {
String headerName = (String)reqHeaderEnum.nextElement();
buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName));
}

buffer.append(CRLF);
int responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
}

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}

if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}

}

private void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
if (!resp.containsHeader("Last-Modified")) {
if (lastModified >= 0L) {
resp.setDateHeader("Last-Modified", lastModified);
}

}
}

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}

this.service(request, response);
}
}

可以看到 HttpServlet 虽然是抽象类,但是没有必须我们实现的抽象方法。同时针对 7 个不同的 HTTP Method 分别实现了默认的 doHttpMethod 方法,里面做了 HTTP 协议版本的检查;同时在 service 方法中做了我们期望的类型转换工作…这意味着我们要针对不同 HTTP Method 做处理的时候,只需要继承 HttpServlet 覆写相应的 doHttpMethod 即可(别忘了复写的时候调用父类的 doHttpMethod 方法做 HTTP 协议版本检查)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(urlPatterns = "/api")
public class ServletImpl extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
System.out.println("GET hello world");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
System.out.println("POST hello world");
}
}


ServletConfig

当 Servlet 容器初始化一个 Servlet 对象时,会为这个 Servlet 对象创建一个 ServletConfig 对象。

在 ServletConfig 对象中包含了 Servlet 的初始化参数信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface ServletConfig {

// 返回 Servlet 的名称
String getServletName();

// 返回一个代表当前 Web 应用的 ServletContext 对象
ServletContext getServletContext();

// 根据初始化参数名返回对应的初始化参数值
String getInitParameter(String var1);

// 返回一个 Enumeration 对象,其中包含了所有的初始化参数名
Enumeration<String> getInitParameterNames();

}

Java Web Servlet 体系结构(二) 强类型语言 & 弱类型语言、动态语言 & 静态语言

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×