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>");
}
}
View Code

通过扩展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)
给这个响应对象添加一个cookievoid 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>&nbsp;</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>&nbsp;</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>");
}
}
View Code

利用下面的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>
View Code

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>
View Code

好处:

使用部署描述符有诸多好处。
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配置优先于注解

 

posted @ 2018-10-31 18:47  假程序猿  阅读(263)  评论(0)    收藏  举报