Servlet 谷歌翻译:小服务程序,servlet是一个让程序跟web服务器打交道的途径,相当于一个管道。
sun公司已经写好了 Servlet接口规范,我们只需要实现它就行了,到时候内部会自动进行向上强制转型。tomcat服务器里面的 jar 包有资源绑定器,绑定配置文件(这个配置文件需要我们自己来写,一般是xml文件,放在WEB-INF目录下),里面注册了我们实现的servlet类。servlet对象在xml文件中注册后,由web服务器去创建、调用、销毁的,tomcat服务器中成web容器(web container)web容器创建的servlet对象会被放到一个HashMap集合中。(如下图所示)
在默认情况下,服务器在启动的时候,servlet对象并不会被实例化。当用户使用某个功能就相当于调用了我们写的实现接口的类,服务器才会创建出需要的Servlet对象,避免浪费内存。
- Serlvet接口 及 子类之间的关系
GenericServlet抽象类实现了 Servlet接口(规范子类中的方法) 和 ServletConfig接口(获取Servlet对象的配置信息),而 HttpServlet抽象类又继承了GenericServlet抽象类,也就是说使用HttpServlet类更加方便,并且采用了HTTP通信协议,所以我们只需要继承它就可以。
public interface Servlet {...}
public interface ServletConfig {...}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {...}
public abstract class HttpServlet extends GenericServlet{...}
- Servlet接口中的方法
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
首先 getServletConfig()方法是用来获取ServletConfig的,这个在GenericServlet类中直接设置为成员变量,而getServletInfo()方法在GenericServlet类中直接返回空字符串,如果想获取Servlet信息可以自己写一个GenericServlet类重写getServletInfo()方法。但是没必要,ServletConfig中就包含了Servlet的信息(这些信息就是xml文件中注册的Servlet信息),直接拿就好了。
- 生命周期
用户在发送第一次请求的时候,Servlet对象被实例化,之后tomcat服务器立马调用init(初始化)方法、无参构造方法 和service方法。用户后面发送第二次、第三次请求的时候,只执行service方法。在服务器要关闭的时候,会调用destroy方法,将资源关闭或者保存,然后就销毁Servlet对象。
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
public Enumeration getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String message) {
this.getServletContext().log(this.getServletName() + ": " + message);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
}
- HttpServlet类
前面也说了HttpServlet 采用了HTTP协议,所以它有get、post、put、delete、options、trace等方法,最常用的就是get、post两个方法。get()方法是发起请求,post()方法是提交表单;get支持缓存,请求发送后,浏览器先在本地缓存中找,找不到才去服务器上找目的是为了提高用户体验。
这些方法都有两个参数 HttpServletRequest、HttpServletResponse,这两个参数其实是两个接口,分别继承了 ServletRequest 和 ServletResponse。HttpServletRequest 可以获取url、head、method、cookie、session、pathInfo、user信息等参数信息,而 HttpServletResponse可以设置cookie、encoding、status等信息。
用户在使用某些功能的时候,由于业务需求,会携带参数,比如查询某个字段,就会将这个字段添加url进行访问,有时是多个字段打包成一个对象,或者多个对象,我们可以将其放进一个Map集合中,用HttpServletRequest 中getParameter()、getParameterMap()方法取出。
使用HttpServlet 类中的service()方法,一般会先调用public修饰的service()方法,然后它再去调用重载的protected修饰的service()方法。该方法会拿 HttpServletRequest 中的Method,然后进行匹对,如果是get就调用get,如果是post就调用post。
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);
}
}
继承HttpServlet之后要重写doGet()或者doPost()方法,不然只要发送请求就一定会执行父类(HttpServlet)中的doGet和doPost方法,而一执行父类的中这两个方法就一定会报405错误。



