纸上得来终觉浅
1 什么是Servlet和Servlet容器?
Servlet就是运行服务端的java小程序,侠义上它是一个接口Servlet,广义上指所有实现了该接口的类;
Servlet容器就是能够运行Servlet、JSP、Filter等的软件环境,可以创建Servlet并且调用其相关生命周期方法,如Tomcat
2 Servlet接口
2.1 当我们继承该接口时,发现它包含了五个方法,如下:
public class HelloServlet implements Servlet{
/*Servlet关闭时要执行的方法,整个Servlet生命周期只执行一次*/
@Override
public void destroy() {
}
/*获得Servlet的配置信息
* 一般在init方法之后就可以获得
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/*获得Servlet的作者信息*/
@Override
public String getServletInfo() {
return null;
}
/*Servlet第一次运行时要执行的方法,整个生命周期只执行一次
*ServletConfig是Servlet的一些配置信息
*/
@Override
public void init(ServletConfig arg0) throws ServletException {
}
/*每次调用Servlet时执行的方法*/
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
}
}
2.2 有一个已经存在的类,GenericServlet,它继承了Servlet,ServletConfig,Serializable接口;
上面已经说了Servlet接口,下面说一下ServletConfig接口,它的方法如下:
<pre name="code" class="java">public interface ServletConfig {
/*获取Servlet的名字*/
public String getServletName();
/*获取Servlet的上下文信息*/
public ServletContext getServletContext();
/*获得Servlet的初始化参数对应的值*/
public String getInitParameter(String name);
/*获得Servlet所有的初始化参数*/
public Enumeration<String> getInitParameterNames();
}
还有一个接口是序列化接口,前面已经说了,它是用来实现序列化的。
GenericServlet类的实现如下:
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
}
@Override
public void destroy() {
}
@Override
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String msg) {
getServletContext().log(getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
@Override
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
@Override
public String getServletName() {
return config.getServletName();
}
}
已经实现了获得ServletConfig,获得ServletContext,获得初始化参数等,所以使用时,直接继承这个类,只需要重写service方法即可,用起来很方便。
2.3 既然有了GenericServlet这个不错的方法,就可以继承然后重写service即可,但是如果我们要获得Http的相关信息,就要把servletrequest转化为httpServletRequest,而且如果要根据http的请求不同,做不同的处理,还要根据请求的方法进行判断; 我们每次写Servlet都会遇到这些问题;
现在有一个类HttpServlet,继承了GenericServlet,并且做了更多的事情,解决了刚刚提到的那些问题:
它在进行service处理的时候,先进行请求和回应的类型转换:
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);
}
然后根据请求方法的类型,调用不同的处理方法:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
所以只需要重写doGet,doPost,doHead方法即可;
3.所以在写Servlet的时候,只需要继承httpServlet就可以了,如果想在初始化或者关闭Servlet等方法中进行一些操作,直接重写就可以了;3.1下面是一个例子:
Web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>roadArchitectWeb</display-name>
<!-- 配置和映射 Servlet -->
<servlet>
<!-- Servlet 注册的名字 -->
<servlet-name>helloServlet</servlet-name>
<!-- Servlet 的全类名 -->
<servlet-class>roadArchitectWeb.Test.HelloServlet</servlet-class>
<!-- 配置 Serlvet 的初始化参数。 且节点必须在 load-on-startup 节点的前面 -->
<init-param>
<!-- 参数名 -->
<param-name>user</param-name>
<!-- 参数值 -->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
<!-- 可以指定 Serlvet 被创建的时机. 若为负数, 则在第一次请求时被创建.若为 0 或正数, 则在当前 WEB 应用被 -->
<!-- Serlvet 容器加载时创建实例, 且数组越小越早被创建. -->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 需要和某一个 servlet 节点的 serlvet-name 子节点的文本节点一致 -->
<servlet-name>helloServlet</servlet-name>
<!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录. -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
HelloServlet:
package roadArchitectWeb.Test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
public void init() throws ServletException {
System.out.println("HelloServlet.init()");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet.doGet() req.getMethod():"+req.getMethod());
System.out.println("HelloServlet.doGet() getInitParameter():"+getInitParameter("user"));
System.out.println("HelloServlet.doGet() getInitParameterNames():"+getInitParameterNames());
System.out.println("HelloServlet.doGet() getServletName():"+getServletName());
/*getServletConfig返回ServletConfig对象,这个对象还有很多其他方法*/
System.out.println("HelloServlet.doGet() getServletConfig().toString():"+getServletConfig().toString());
/*getServletContext返回ServletContext对象,这个对象也有很多其他方法*/
System.out.println("HelloServlet.doGet() getServletContext().getContextPath():"+getServletContext().getContextPath());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HelloServlet.doPost():"+req.getMethod());
}
@Override
public void destroy() {
System.out.println("HelloServlet.destroy()");
}
}
3.2 下面梳理下一些规则(Servlet还有Filter等其实没什么东西,都是些规则而已,实践下就可以了)
A:上面的load-on-startup值为0 ,所以在tomcat启动的时候已经开始加载了,如果把它设为-1,则在第一次请求的时候加载,如:
B:同一个Servlet可以被映射到多个URL上,即多个 <servlet-mapping> 元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
<servlet-mapping>
<!-- 需要和某一个 servlet 节点的 serlvet-name 子节点的文本节点一致 -->
<servlet-name>helloServlet</servlet-name>
<!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录. -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<!-- 需要和某一个 servlet 节点的 serlvet-name 子节点的文本节点一致 -->
<servlet-name>helloServlet</servlet-name>
<!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录. -->
<url-pattern>/heihei</url-pattern>
</servlet-mapping>
那么在请求hello和heihei的时候,执行同样的servlet,结果当然也是一样的;
C:在Servlet映射到的URL中也可以使用 * 通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。
Servlet的基础部分大概就是这些