Servlet架构初解析

    技术2022-07-20  69

    通过源码解析Servlet架构

    1.Servlet和ServletConfig

    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中,可以使用一个或多个<init-param>标签为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);

    2.GenericServlet和HttpServlet

    在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); } }

    下图是Servlet架构于SpringMVC的关系。

    Processed: 0.009, SQL: 9