Servlet,JSP(3)(GenericServlet和HttpServlets)
1,GenericServlet
通过实现Servlet接口来编写Servlet。
必须给Servlet中的所有方法都提供实现,即便其中有一些根本就没有包含任何代码。
此外,还需要将ServletConfig对象保存到类级变量中。
GenericServlet抽象类本着尽可能使代码简单的原则,
GenericServlet实现了Servlet和ServletConfig接口,并完成以下任务:
1,将init方法中的ServletConfig赋给一个类级变量,以便可以通过调用getServletConfig获取。
2,为Servlet接口中的所有方法提供默认的实现。
3,提供方法,包围ServletConfig中的方法。
GenericServlet通过将ServletConfig赋给init方法中的类级变量servletConfig,来保存ServletConfig。
下面就是GenericServlet中的init实现: public void init(ServletConfig servletConfig) throws ServletException { this.servletConfig = servletConfig; this.init(); }
如果在类中覆盖了这个方法,就会调用Servlet中的init方法,
并且还必须调用super.init(servletConfig)来保存ServletConfig。
为了避免上述麻烦,GenericServlet提供了第二个init方法,它不带参数。
这个方法是在ServletConfig被赋给servletConfig后,由第一个init方法调用
可以通过覆盖没有参数的init方法来添加初始化代码,ServletConfig则仍然由GenericServlet实例保存。
ps:init方法设计思路:
如果想在GenericServlet实现的自定义类,添加另外的初始化数据: 那么必须重写servlet接口中的init(ServletConfig servletConfig)方法,并且还要复用已有的对servletConfig的赋值。 为了简化这种操作,在init(ServletConfig servletConfig)方法中添加一个init()方法即可,
想添加自定义初始化数据,重写init()即可。 为什么会出现:init(ServletConfig servletConfig)——>init()的调用过程,
这是因为面向接口的设计的原则,这些调用接口在容器调用中已经被确定了。
清单1.3中的GenericServletDemoServlet类扩展了GenericServlet类
package app01a; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.GenericServlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; @WebServlet(name = "GenericServletDemoServlet", urlPatterns = { "/generic" }, initParams = { @WebInitParam(name="admin", value="Harry Taciak"), @WebInitParam(name="email", value="admin@example.com") } ) public class GenericServletDemoServlet extends GenericServlet { private static final long serialVersionUID = 62500890L; @Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { ServletConfig servletConfig = getServletConfig(); String admin = servletConfig.getInitParameter("admin"); String email = servletConfig.getInitParameter("email"); response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.print("<html><head></head><body>" + "Admin:" + admin + "<br/>Email:" + email + "</body></html>"); } }
通过扩展GenericServlet,就不要覆盖没有需要的方法,代码变得更加整洁。
GenericServlet是对Servlet一个很好的加强,但不常用,因为它毕竟不像HttpServlet那么高级
2,Http Servlets
javax.servlet.http包是Servlet API中的第二个包,其中包含了用于编写Servlet应用程序的类和接口。
javax.servlet.http中的许多类型都覆盖了javax.servlet中的类型。
HttpServlet类覆盖了javax.servlet.GenericServlet类。
使用HttpServlet时,
HttpServletRequest接口扩展javax.servlet.ServletRequest,
HttpServletResponse扩展javax.servlet.ServletResponse。
HttpServlet覆盖GenericServlet中的Service方法,并且添加一个Service方法:
protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException, java.io.IOException public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response); }
原始的Service方法将Servlet容器的request和response对象分别转换成HttpServletRequest和HttpServletResponse,
并调用新的Service方法。然后,HttpServlet中的Service方法会检验用来发送
请求的HTTP方法(通过调用request.getMethod),并调用以下方法之一:
doGet、doPost、doHead、doPut、doTrace、doOptions和doDelete。
这7种方法中,每一种方法都表示一个HTTP方法。
doGet和doPost是最常用的。
因此,不再需要覆盖Service方法了,只要覆盖doGet和doPost即可。
HttpServlet有两个特性是GenericServlet所不具备的:
1,不用覆盖Service方法,而是覆盖doGet或者doPost,或者覆盖doGet和doPost。 在少数情况下,还会覆盖以下任意方法:doHead、doPut、doTrace、doOptions和doDelete。 2,使用HttpServletRequest和HttpServletResponse,而不是ServletRequest和ServletResponse。
ps:总结:
继承HttpServlet的好处是只需要重写doGet, doPost
2.1,HttpServletRequest
HttpServletRequest表示HTTP环境中的Servlet请求。
它扩展javax.servlet.ServletRequest接口,新增的部分方法如下:
java.lang.String getContextPath()
返回表示请求上下文的请求URI部分。
Cookie[] getCookies()
返回一个Cookie对象数组。
java.lang.String getHeader(java.lang.String name)
返回指定HTTP标题的值。
java.lang.String getMethod()
返回生成这个请求的HTTP方法名称。
java.lang.String getQueryString()
返回请求URL中的查询字符串。
HttpSession getSession()
返回与这个请求相关的会话对象。如果没有,将创建一个新的会话对象。
HttpSession getSession(boolean create)
返回与这个请求相关的会话对象。
如果有,并且create参数为True,将创建一个新的会话对象。
2.2,HttpServletResponse
部分方法:
void addCookie(Cookie cookie) 给这个响应对象添加一个cookie。 void addHeader(java.lang.String name, java.lang.String value) 给这个响应对象添加一个header。 void sendRedirect(java.lang.String location) 发送一条响应码,将浏览器跳转到指定的位置。
3,处理HTML表单
HTML输入域(文本域、隐藏域或者密码域)或者文本区的值,会被当作字符串发送到服务器。 空的输入域或者文本区会发送空的字符串。 因此,有输入域名称的,ServletRequest.getParameter绝对不会返回null。 HTML的select元素也向header发送了一个字符串。 如果select元素中没有任何选项被选中,那么就会发出 所显示的这个选项值。 包含多个值的select元素(允许选择多个选项并且用<select multiple>表示的select元素)
发出一个字符串数组,并且必须通过SelectRequest.getParameterValues进行处理。 复选框比较奇特。 核查过的复选框会发送字符串“on”到服务器。 未经核查的复选框则不向服务器发送任何内容,ServletRequest.getParameter(fieldName)返回null。 单选框将被选中按钮的值发送到服务器。 如果没有选择任何按钮,将没有任何内容被发送到服务器,并且 ServletRequest.getParameter(fieldName)返回null。 如果一个表单中包含多个输入同名的元素,那么所有值都会被提交,并且必须利用 ServletRequest.getParameterValues来获取它们。 ServletRequest.getParameter将只返回最后一个值。
清单1.4中的FormServlet类示范了如何处理HTML表单。
它的doGet方法将一个Order表单发送到浏览器。 它的doPost方法获取到所输入的值,并将它们输出。 package app01b; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "FormServlet", urlPatterns = { "/form" }) public class FormServlet extends HttpServlet { private static final long serialVersionUID = 54L; private static final String TITLE = "Order Form"; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>" + TITLE + "</title></head>"); writer.println("<body><h1>" + TITLE + "</h1>"); writer.println("<form method='post'>"); writer.println("<table>"); writer.println("<tr>"); writer.println("<td>Name:</td>"); writer.println("<td><input name='name'/></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Address:</td>"); writer.println("<td><textarea name='address' " + "cols='40' rows='5'></textarea></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Country:</td>"); writer.println("<td><select name='country'>"); writer.println("<option>United States</option>"); writer.println("<option>Canada</option>"); writer.println("</select></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Delivery Method:</td>"); writer.println("<td><input type='radio' " + "name='deliveryMethod'" + " value='First Class'/>First Class"); writer.println("<input type='radio' " + "name='deliveryMethod' " + "value='Second Class'/>Second Class</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Shipping Instructions:</td>"); writer.println("<td><textarea name='instruction' " + "cols='40' rows='5'></textarea></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td> </td>"); writer.println("<td><textarea name='instruction' " + "cols='40' rows='5'></textarea></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Please send me the latest " + "product catalog:</td>"); writer.println("<td><input type='checkbox' " + "name='catalogRequest'/></td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td> </td>"); writer.println("<td><input type='reset'/>" + "<input type='submit'/></td>"); writer.println("</tr>"); writer.println("</table>"); writer.println("</form>"); writer.println("</body>"); writer.println("</html>"); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>" + TITLE + "</title></head>"); writer.println("</head>"); writer.println("<body><h1>" + TITLE + "</h1>"); writer.println("<table>"); writer.println("<tr>"); writer.println("<td>Name:</td>"); writer.println("<td>" + request.getParameter("name") + "</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Address:</td>"); writer.println("<td>" + request.getParameter("address") + "</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Country:</td>"); writer.println("<td>" + request.getParameter("country") + "</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Shipping Instructions:</td>"); writer.println("<td>"); String[] instructions = request .getParameterValues("instruction"); if (instructions != null) { for (String instruction : instructions) { writer.println(instruction + "<br/>"); } } writer.println("</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Delivery Method:</td>"); writer.println("<td>" + request.getParameter("deliveryMethod") + "</td>"); writer.println("</tr>"); writer.println("<tr>"); writer.println("<td>Catalog Request:</td>"); writer.println("<td>"); if (request.getParameter("catalogRequest") == null) { writer.println("No"); } else { writer.println("Yes"); } writer.println("</td>"); writer.println("</tr>"); writer.println("</table>"); writer.println("<div style='border:1px solid #ddd;" + "margin-top:40px;font-size:90%'>"); writer.println("Debug Info<br/>"); Enumeration<String> parameterNames = request .getParameterNames(); while (parameterNames.hasMoreElements()) { String paramName = parameterNames.nextElement(); writer.println(paramName + ": "); String[] paramValues = request .getParameterValues(paramName); for (String paramValue : paramValues) { writer.println(paramValue + "<br/>"); } } writer.println("</div>"); writer.println("</body>"); writer.println("</html>"); } }
利用下面的URL,可以调用FormServlet:
http://localhost:8080/app01b/form
被调用的doGet方法会被这个HTML表单发送给浏览器:
<form method='post'> <input name='name'/> <textarea name='address' cols='40' rows='5'></textarea> <select name='country'>"); <option>United States</option> <option>Canada</option> </select> <input type='radio' name='deliveryMethod' value='First Class'/> <input type='radio' name='deliveryMethod' value='Second Class'/ > <textarea name='instruction' cols='40' rows='5'></textarea> <textarea name='instruction' cols='40' rows='5'></textarea> <input type='checkbox' name='catalogRequest'/> <input type='reset'/> <input type='submit'/> </form>
4,使用部署描述符(即xml配置)
利用部署描述符是配置Servlet应用程序的另一种方法,部署描述符总是命名为web.xml,并且放在WEB-INF目录下。
清单1.5和清单1.6分别展示了SimpleServlet和WelcomeServlet。
清单1.5 未标注的SimpleServlet类 package app01c; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class SimpleServlet extends HttpServlet { private static final long serialVersionUID = 8946L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.print("<html><head></head>" + "<body>Simple Servlet</body></html"); } } 清单1.6 未标注的WelcomeServlet类 package app01c; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class WelcomeServlet extends HttpServlet { private static final long serialVersionUID = 27126L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.print("<html><head></head>" + "<body>Welcome</body></html>"); } } 清单1.7 部署描述符 <?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ➥ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class>app01c.SimpleServlet</servlet-class> <load-on-startup>10</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SimpleServlet</servlet-name> <url-pattern>/simple</url-pattern> </servlet-mapping> <servlet> <servlet-name>WelcomeServlet</servlet-name> <servlet-class>app01c.WelcomeServlet</servlet-class> <load-on-startup>20</load-on-startup> </servlet> <servlet-mapping> <servlet-name>WelcomeServlet</servlet-name> <url-pattern>/welcome</url-pattern> </servlet-mapping> </web-app>
好处:
使用部署描述符有诸多好处。 1,拥有注解没有的功能
可以将在@WebServlet中没有对等元素的元素,如load-on-startup元素。 这个元素使得Servlet在应用程序启动时加载,而不是在第一次调用时加载。 如果Servlet的init方法需要花一些时间才能完成的话,
使用load-on-startup意味着第一次调用Servlet所花的时间并不比后续的调用长,这项功能就特别有用。
不需要重新编译servlet: 2,如果需要修改配置值,如Servlet路径,则不需要重新编译Servlet类。 3,可以将初始参数传给一个Servlet,并且不需要重新编译Servlet类,就可以对它们进行编辑。
部署描述符还允许覆盖在Servlet标注中定义的值。
Servlet上的WebServlet标注如果同时也在部署描述符中进行声明,那么它将不起作用。
然而,在有部署描述符的应用程序中,却不在部署描述符中标注Servlet时,则仍然有效。
这意味着,可以标注Servlet,并在同一个应用程序的部署描述符中声明这些Servlet。
ps:xml配置优先于注解

浙公网安备 33010602011771号