Servlet是Server + Applet的缩写,表示一个服务器应用.Servlet其实就是一套规范,我们按照这套规范写的代码就可以直接在Java的服务器上面运行.如下图所示的是Servlet3.1中Servlet的结构图
一,Servlet接口
public interface Servlet {
/*
init方法在容器启动时被容器调用(注:load-on-startup可以指定Servlet被创建的时机,若为负数或者不设置,
则在第一次请求时[即第一次调用该Servlet才被创建],若为0或者正数,则在当前WEB应用被Servlet容器加载时创建实例,
且数越小越早被创建),只会被调用一次;
*/
public void init(ServletConfig config) throws ServletException;
/*
用于获取ServletConfig,ServletConfig顾名思义指的是Servlet的配置,比如我们在web.xml中定义Servlet时通过init-param
标签配置的参数就是通过ServletConfig来保存的.
*/
public ServletConfig getServletConfig();
/*
service方法用于具体处理一个请求
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/*
getServletInfo方法可以获取一些Servlet相关的信息,若作者,版权等,这个方法需要自己实现,默认返回空字符串;
*/
public String getServletInfo();
/*
主要用于在Servlet销毁(一般指关闭服务器)时释放一些资源,也只会调用一次
*/
public void destroy(){}
二,ServletConfig
package javax.servlet;
import java.util.Enumeration;
public interface ServletConfig {
/*
用于获取Servlet的名字,也就是我们在web.xml中定义的servlet-name
*/
public String getServletName();
/*
返回值ServletContext代表的是我们这个应用的本身,那么ServletContext里边设置的参数就可以被当前应用的所有Servlet共享了[注:做项目时,参数可以保存在Session中,也可以在Application中,而后者很多时候就是保存在ServletContext中];
我们可以这么理解,ServletConfig是Servlet级的,而ServletContext是Context(也就是Application)级的,另外我们可以获取更高一层的站点级也就是Tomcat中的Host级的相应操作:在Servlet的标准中有这么一个方法:public ServletContext getContext(String uripath),它可以根据路径获取到同一站点下的别的应用的ServletContext!由于安全原因,一般返回null,如果想使用需要进行一些设置.
*/
public ServletContext getServletContext();
//用于获取init-param配置的参数
public String getInitParameter(String name);
//用于获取配置的所有init-param的名字的集合
public Enumeration getInitParameterNames();
}
三 GenericServlet
GenericServlet是Servlet的默认实现,主要做了三件事:
①实现了ServletConfig接口,可以直接调用ServletConfig中的方法;
②提供了无参的init方法;
③提供了log方法;
/*
GenericServlet实现了ServletConfig接口,当需要调用ServletConfig中方法的时候,可以直接调用.比如获取ServletContext的时候可以直接调用getServletContext,而无须调用getServletConfig().getServletContext(),不过底层实现其实是内部调用,源码如下:
*/
public ServletContext getServletContext() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletContext();
}
/*
GenericServlet实现了Servlet的init(ServletConfig config)方法,在里面将参数config设置给了内部变量config,然后调用了无参的init()方法;
这种做法有三个作用:
①将参数config设置给了内部属性config,这样就可以在ServletConfig的接口方法中直接调用config相应的方法来执行;
②当我们在写Servlet的时候就可以只处理自己的初始化逻辑,而不需要关心config了;
③在重写init()方法时不需要在调用super.init(config)了,如果在自己的Servlet中重写了带参的init方法,一定记着调用super.init(config),否则会报空指针异常
*/
//GenericServlet提供了两个log方法,一个记录日志,一个记录异常
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);
}
四 HttpServlet
HttpServlet是用HTTP协议实现的Servlet的基类,SpringMVC中的DispatchServlet就是继承该类,HttpServlet主要重写了service方法.在service方法中首先将ServletRequest和ServletResponse转换为了HttpServletRequest和HttpServletResponse ,然后根据HTTP请求的类型不同将请求路由到了不同的处理方法
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
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) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
doGet()
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);
}
}
/*
具体处理方法时doXXX的结构,.doGet,doPost,doPut和doDelete方法都是模板方法,而且子类如果没有实现将抛出异常,在调用doGet方法之前还对是否过期做了检查,如果没有过期则直接返回304状态码使用缓存;doHead调用了doGet的请求,然后返回空body的Response;doOption和doTrace主要用来做一些调试工作,doOtion返回所有支持的处理类型的集合,正常情况下可以禁用,doTrace是用来远程诊断服务器的,它会将接收到的header原封不动地返回
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}