public interface Servlet {
//生命周期方法
//init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用
void init(ServletConfig var1) throws ServletException;
//getServletConfig() 方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext
ServletConfig getServletConfig();
//生命周期方法
//service() 方法是执行实际任务的主要方法
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
//getServletInfo() 方法是一个可选的方法,它提供有关 servlet 的信息,如作者、版本、版权
String getServletInfo();
//生命周期方法
//当Servlet被销毁时执行该方法
void destroy();
}
在Servlet的配置文件web.xml
中,可以使用一个或多个
标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
这样做的好处是:如果将数据库信息、编码方式等配置信息放在web.xml中,如果以后数据库的用户名、密码改变了,则直接很方便地修改web.xml就行了,避免了直接修改源代码的麻烦。
public interface ServletConfig {
//获取当前Servlet在web.xml中配置的名字
String getServletName();
//获取代表当前web应用的ServletContext对象
ServletContext getServletContext();
//获取当前Servlet指定名称的初始化参数的值
String getInitParameter(String var1);
//获取当前Servlet所有初始化参数的名字组成的枚举
Enumeration<String> getInitParameterNames();
}
WEB容器在启动时,它会为**每个程序都创建一个对应的ServletContext对象,**它代表当前web应用。
ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext()
方法获得ServletContext对象。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
在一个HttpServlet实例中使用
ServletContext servletcontext = this.getServletContext();
servletcontext.setAttribute("name", "springmvc");
在另一个HttpServlet实例中可以用
ServletContext servletContext = this.getServletContext();
String name = (String)servletContext.getAttribute("name");
ServletContext接口里的方法很多,常用的有如下:
void setAttribute(String, Object);
Object getAttribute(String);
void removeAttribute(String);
在javax.servlet
包中提供了一个抽象类GenericServet,他是一个通用的,不依赖于具体协议的Servlet。
注意这里抽象类GenericServlet是实现了Servlet和ServletConfig接口,但是其内部又组合了一个ServletConfig属性,既是依赖关系,又是继承关系,我个人觉得这种实现了方法应该是为了使用的方便性,GenericServlet的实现类通过代理实现对ServletConfig方法的直接访问。
在GenericServlet中只有service方法是抽象方法,所以实现类只需要实现service方法就好了,非常简介明了。
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<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
//代理,获取ServletConfig对象
public ServletConfig getServletConfig() {
return this.config;
}
//代理,获取SerlvetContext对象
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继承于GenericServlet抽象类,基于HTTP协议实现了service(ServletRequest var1, SerletResponse var2)
方法
它本身根据请求的类型实现了多种对应的服务,包括DELETE
,HEAD
,GET
,POST
,PUT
等,在service()方法中根据请求类型的不同分配不同的处理方法,一般我们使用servlet编程时基本上都继承自HttpServlet,根据需要实现不同的方法。
public abstract class HttpServlet extends GenericServlet {
private static final long serialVersionUID = 1L;
private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";
private static final String HEADER_IFMODSINCE = "If-Modified-Since";
private static final String HEADER_LASTMOD = "Last-Modified";
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
private static final ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
public HttpServlet() {
}
//处理Get请求,如果不重写就会默认返回400、405错误,剩下的某些方法也都是如此
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(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected long getLastModified(HttpServletRequest req) {
return -1L;
}
//HEAD方法与GET方法一样,都是向服务器发出指定资源的请求。但是,服务器在响应HEAD请求时不会回传资源的内容部分,即:响应主体。这样,我们可以不传输全部内容的情况下,就可以获取服务器的响应头信息。HEAD方法常被用于客户端查看服务器的性能。
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
this.doGet(req, resp);
} else {
NoBodyResponse response = new NoBodyResponse(resp);
this.doGet(req, response);
response.setContentLength();
}
}
//处理post请求
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
//处理Put请求
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_put_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
//处理delete请求
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_delete_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
//通过反射获取类的所有接口,不用重写
private static Method[] getAllDeclaredMethods(Class<?> c) {
if (c.equals(HttpServlet.class)) {
return null;
} else {
Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
Method[] thisMethods = c.getDeclaredMethods();
if (parentMethods != null && parentMethods.length > 0) {
Method[] allMethods = new Method[parentMethods.length + thisMethods.length];
System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length);
System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length);
thisMethods = allMethods;
}
return thisMethods;
}
}
//通过调用getAllDeclaredMethods判断服务器支持哪些方法,不用重写
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Method[] methods = getAllDeclaredMethods(this.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false;
boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
Class clazz = null;
try {
clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
Method getAllowTrace = clazz.getMethod("getAllowTrace", (Class[])null);
ALLOW_TRACE = (Boolean)getAllowTrace.invoke(req, (Object[])null);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException var14) {
}
for(int i = 0; i < methods.length; ++i) {
Method m = methods[i];
if (m.getName().equals("doGet")) {
ALLOW_GET = true;
ALLOW_HEAD = true;
}
if (m.getName().equals("doPost")) {
ALLOW_POST = true;
}
if (m.getName().equals("doPut")) {
ALLOW_PUT = true;
}
if (m.getName().equals("doDelete")) {
ALLOW_DELETE = true;
}
}
String allow = null;
if (ALLOW_GET) {
allow = "GET";
}
if (ALLOW_HEAD) {
if (allow == null) {
allow = "HEAD";
} else {
allow = allow + ", HEAD";
}
}
if (ALLOW_POST) {
if (allow == null) {
allow = "POST";
} else {
allow = allow + ", POST";
}
}
if (ALLOW_PUT) {
if (allow == null) {
allow = "PUT";
} else {
allow = allow + ", PUT";
}
}
if (ALLOW_DELETE) {
if (allow == null) {
allow = "DELETE";
} else {
allow = allow + ", DELETE";
}
}
if (ALLOW_TRACE) {
if (allow == null) {
allow = "TRACE";
} else {
allow = allow + ", TRACE";
}
}
if (ALLOW_OPTIONS) {
if (allow == null) {
allow = "OPTIONS";
} else {
allow = allow + ", OPTIONS";
}
}
resp.setHeader("Allow", allow);
}
//处理trace请求,TRACE请求服务器回显其收到的请求信息,该方法主要用于HTTP请求的测试或诊断。
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String CRLF = "\r\n";
StringBuilder buffer = (new StringBuilder("TRACE ")).append(req.getRequestURI()).append(" ").append(req.getProtocol());
Enumeration reqHeaderEnum = req.getHeaderNames();
while(reqHeaderEnum.hasMoreElements()) {
String headerName = (String)reqHeaderEnum.nextElement();
buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName));
}
buffer.append(CRLF);
int responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
}
//根据请求类型分配处理方法,注意此service不是GenericServlet.service()方法的实现,参数类型不同,这是HttpServlet类自己定义的service方法
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);
}
}
//设置响应header中的Last-Modified域
private void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
if (!resp.containsHeader("Last-Modified")) {
if (lastModified >= 0L) {
resp.setDateHeader("Last-Modified", lastModified);
}
}
}
//这次是实现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 var6) {
throw new ServletException(lStrings.getString("http.non_http"));
}
this.service(request, response);
}
}