Servlet架构初解析
通過源碼解析Servlet架構
1.Servlet和ServletConfig
public interface Servlet {//生命周期方法//init 方法被設計成只調用一次。它在第一次創建 Servlet 時被調用,在后續每次用戶請求時不再調用void init(ServletConfig var1) throws ServletException;//getServletConfig() 方法返回一個 ServletConfig 對象,該對象用來返回初始化參數和 ServletContextServletConfig 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的關系。
總結
以上是生活随笔為你收集整理的Servlet架构初解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Winpcap进行抓包,分析数据包结构并
- 下一篇: Spring中Controller层、F