Servlet
一、Servlet简介
二、Servlet编写一个简单的程序
三、Servlet的生命周期
四、Servlet的请求流程
五、Servlet的初始化参数
六、Servlet的继承体系
1.MyGenericServlet
如上图,每一个自定义Servlet类都要编写红色方框中的代码,当有多个Servlet类时,会造成严重的代码冗余。所以我们进行代码重构以消除冗余。
解决方案:定义MyGenericServlet,定义上述三行红框中的代码:
abstract public class MyGenericServlet implements Servlet {
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
abstract public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException;
//把ServletConfig对象暴露给子类访问
public ServletConfig getServletConfig() {
return this.config;
}
public void destroy() {
}
public String getServletInfo() {
return null;
}
}
然后Servlet1和Servlet2继承该类:
public class Servlet2 extends MyGenericServlet {
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
String encoding = super.getServletConfig().getInitParameter("encoding");
System.out.println(encoding);
}
}
但是看这样一行代码:
String encoding = super.getServletConfig().getInitParameter("encoding");
我觉得这样写太麻烦,代码太长,希望写成这样:
String encoding = super.getInitParameter("encoding");
我们在MyGenericServlet类中实现ServletConfig接口,在父类中替子类实现getServletConfig方法,完整代码如下:
abstract public class MyGenericServlet implements Serializable, Servlet, ServletConfig {
private static final long serialVersionUID = 8151798658022325157L;
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
abstract public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException;
//把ServletConfig对象暴露给子类访问
public ServletConfig getServletConfig() {
return this.config;
}
public void destroy() {
}
public String getServletInfo() {
return null;
}
//-------------------------------------------
public String getInitParameter(String arg0) {
return config.getInitParameter(arg0);
}
public Enumeration<String> getInitParameterNames() {
return config.getInitParameterNames();
}
public ServletContext getServletContext() {
return config.getServletContext();
}
public String getServletName() {
return config.getServletName();
}
}
用这样的继承体系之后,虽然设计有点难度,但是开发的时候却变得非常简单,但是Servlet的继承体系还远不止这些。
2.init
如果子类要做自身的初始化操作,此时必须覆盖父类的init方法,并编写自己的初始化方法。
public class Servlet1 extends MyGenericServlet{
public void init(ServletConfig config) throws ServletException {
super.init(config);
System.out.println("初始化...");
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
}
}
但是若忘掉调用父类的init方法,那么父类中的config便会是空指针,服务器会报500错误。,
为了解决开发者在做自身初始化操作时忘记 super.init() ,Java在MyGenericServlet,专门提供了一个无参数的初始化方法init(),专门暴露给子类做初始化操作。
然后Servlet1中init()变成了:
public class Servlet1 extends MyGenericServlet {
public void init() {
System.out.println("无参数初始化...");
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
}
}
是不是简单了许多,这就是多态思想,调用了子类中的 init()
这是Sun公司一个人性化的设计。
3.MyHttpServlet
上述的Servlet1只能处理一般的响应,但是做BS开发,都是基于浏览器的,而基于浏览器都遵循HTTP协议。
//只能处理一般的请求
public void service(ServletRequest req, ServletResponse resp)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
service(request,response);
}
//专门处理HTTP请求
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
上述代码没有问题,但是在处理http的service方法,会统一处理GET/POST请求,那如果GET和POST方法需要不同的处理,所以我们分成两个方法:doGet(), doPost():
//专门处理HTTP请求
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if ("GET".equals(method)) {
doGet(req,resp);
} else if ("POST".equals(method)){
doPost(req,resp);
}
}
//专门处理POST请求
private void doPost(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doPost()");
}
//专门处理GET请求
private void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doGet()");
}
但是又出现了问题:每一个Servlet都要编写那么多的代码,因此我们又想到了封装,提取出一个专门用于处理HTTP协议的Servlet出来。
public class MyHttpServlet extends MyGenericServlet {
//只能处理一般的请求
public void service(ServletRequest req, ServletResponse resp)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
service(request, response);
}
//专门处理HTTP请求
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if ("GET".equals(method)) {
doGet(req, resp);
} else if ("POST".equals(method)) {
doPost(req, resp);
}
}
//专门处理POST请求
private void doPost(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doPost()");
}
//专门处理GET请求
private void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("Servlet1.doGet()");
}
}
抽取出MyHttpServlet,以后只要继承MyHttpServlet即可,再提供处理请求的service/doGet/doPost方法。
其实这里也体现了模板方法设计模式(含有doXXX方法通常要在子类调用,称为钩子方法)
简要总结如下:
编写Servlet的方式:
public class Servlet1 extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
//编写处理所有请求的代码
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
}
并且在自己写Servlet的service方法时,应该去掉super.service()方法。