Spring MVC的所有組件繼承圖以下所示java
模板方法模式是由抽象類或接口定義好執行順序,由子類去實現,但不管子類如何實現,他都得按照抽象類或者接口定義好的順序去執行。實例代碼請參考 設計模式整理 ,Servlet的起點從Servlet接口開始。ios
package javax.servlet; import java.io.IOException; public interface Servlet { //用於初始化Servlet組件 void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); //用於處理每一個Web容器傳遞過來的請求與響應 void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; String getServletInfo(); //讓Servlet組件釋放已使用的資源。 void destroy(); }
實現Servlet接口的是一個GenericServlet的抽象類,咱們來看一下這三個方法的實現git
public void init(ServletConfig config) throws ServletException { //用於保存Servlet配置,Servlet在初始化時須要初始化配置信息,如名稱,參數等,在處理HTTP請求時會常常用到這些配置信息 this.config = config; //讓子類實現的方法 this.init(); }
public void init() throws ServletException { }
//抽象方法,子類必須實現。不一樣的Servlet實現會依賴不一樣的協議,因此實現各不相同 public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
//讓子類實現的方法
public void destroy() {
}
下面是對ServletConfig的定義和獲取,在HttpServletBean中將會使用到。web
private transient ServletConfig config;
public ServletConfig getServletConfig() { return this.config; }
//從Servlet配置中獲取Servlet上下文,Servlet上下文全局惟一,爲全部Servlet共享,因此叫全局應用程序共享對象,它能夠讀取全局配置參數,能夠獲取當前工程下的資源文件 public ServletContext getServletContext() { ServletConfig sc = this.getServletConfig(); if(sc == null) { throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized")); } else { return sc.getServletContext(); } }
繼承於GenericServlet是一個HttpServlet的抽象類,該類沒有實現init和destroy方法,由於它們不是抽象方法,可是它必需要實現抽象方法servicespring
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
通用協議的service實現設計模式
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { if(req instanceof HttpServletRequest && res instanceof HttpServletResponse) { //強制轉換成http的請求和響應 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; this.service(request, response); } else { throw new ServletException("non-HTTP request or response"); } }
http的service數組
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取http請求中的方法 String method = req.getMethod(); long lastModified; //若是是GET方法 if(method.equals("GET")) { //獲取此次請求的最後修改時間 lastModified = this.getLastModified(req); //-1表示這個Servlet不支持最後的修改操做,直接用doGet處理GET請求 if(lastModified == -1L) { this.doGet(req, resp); } else { //若是這個Servlet支持最後的修改操做,則獲取請求頭中包含的請求的最後修改時間 long ifModifiedSince = req.getDateHeader("If-Modified-Since"); //若是該修改時間早於這個Servlet的最後修改時間,說明這個Servlet在用戶進行上一次HTTP請求時已被修改 if(ifModifiedSince < lastModified) { //設置最新修改時間到響應頭中 this.maybeSetLastModified(resp, lastModified); //調用doGet處理HTTP GET請求 this.doGet(req, resp); } else { //若是該修改時間晚於這個Servlet的最後修改時間,說明這個Servlet從請求的最後修改時間開始就沒被修改,返回304狀態 resp.setStatus(304); } } //若是此次請求使用的是HEAD方法 } else if(method.equals("HEAD")) { //若是這個Servlet支持最後的修改操做,則設置這個Servlet的最後修改時間到響應頭中 lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); //與處理HTTP GET方法不一樣,不管請求頭中的修改時間是否是早於這個Servlet的最後修改時間,都會發送HEAD響應給用戶,由於HTTP HEAD是專門用於查詢Servlet頭信息的操 //做 this.doHead(req, resp); //若是此次請求使用的是POST方法 } else if(method.equals("POST")) { //調用doPost處理 this.doPost(req, resp); //若是此次請求使用的是PUT方法 } else if(method.equals("PUT")) { //調用doPut處理 this.doPut(req, resp); //若是此次請求使用的是DELETE方法 } else if(method.equals("DELETE")) { //調用doDelete處理 this.doDelete(req, resp); //若是此次請求使用的是OPTIONS方法 } else if(method.equals("OPTIONS")) { //調用doOptions處理 this.doOptions(req, resp); //若是此次請求使用的是TRACE方法 } else if(method.equals("TRACE")) { //調用doTrace處理 this.doTrace(req, resp); } else { //若是此次請求使用的是其餘未知方法,則返回501錯誤消息 String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取請求頭包含的協議(HTTP版本) String protocol = req.getProtocol(); //獲得javax.servlet.http.LocalStrings資源包中的http.method_get_not_supported的值 String msg = lStrings.getString("http.method_get_not_supported"); //若是是HTTP1.1,發送錯誤405 if(protocol.endsWith("1.1")) { resp.sendError(405, msg); } else { //若是是更早版本發送錯誤400 resp.sendError(400, msg); } }
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //構造一個特殊的響應類,這個類在內部忽略了全部響應體的輸出 NoBodyResponse response = new NoBodyResponse(resp); //調用doGet處理 this.doGet(req, response); //設置響應體的字節大小 response.setContentLength(); }
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取請求頭包含的協議(HTTP版本) String protocol = req.getProtocol(); //獲得javax.servlet.http.LocalStrings資源包中的http.method_post_not_supported的值 String msg = lStrings.getString("http.method_post_not_supported"); //若是是HTTP1.1,發送錯誤405 if(protocol.endsWith("1.1")) { resp.sendError(405, msg); } else { //若是是更早版本發送錯誤400 resp.sendError(400, msg); } }
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); } }
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); } }
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲得HttpServlet父類的全部方法 Method[] methods = this.getAllDeclaredMethods(this.getClass()); //在初始化時假設它不支持任何HTTP方法 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; //遍歷該HTTPServlet全部父類的方法 for(int i = 0; i < methods.length; ++i) { //提取方法名 String methodName = methods[i].getName(); //若是名稱是doGet,doPost,doPut,doDelete,則支持它們相應的方法 if(methodName.equals("doGet")) { ALLOW_GET = true; ALLOW_HEAD = true; } else if(methodName.equals("doPost")) { ALLOW_POST = true; } else if(methodName.equals("doPut")) { ALLOW_PUT = true; } else if(methodName.equals("doDelete")) { ALLOW_DELETE = true; } } //將全部的被支持的方法添加到一個統一的StringBuilder中 StringBuilder allow = new StringBuilder(); if(ALLOW_GET) { allow.append("GET"); } if(ALLOW_HEAD) { if(allow.length() > 0) { allow.append(", "); } allow.append("HEAD"); } if(ALLOW_POST) { if(allow.length() > 0) { allow.append(", "); } allow.append("POST"); } if(ALLOW_PUT) { if(allow.length() > 0) { allow.append(", "); } allow.append("PUT"); } if(ALLOW_DELETE) { if(allow.length() > 0) { allow.append(", "); } allow.append("DELETE"); } if(ALLOW_TRACE) { if(allow.length() > 0) { allow.append(", "); } allow.append("TRACE"); } if(ALLOW_OPTIONS) { if(allow.length() > 0) { allow.append(", "); } allow.append("OPTIONS"); } //把支持的方法拼接成字符串設置到HTTP的響應頭中,這個值的key是"Allow" resp.setHeader("Allow", allow.toString()); }
private Method[] getAllDeclaredMethods(Class<? extends HttpServlet> c) { //HTTPServlet的類實例 Class<?> clazz = c; Method[] allMethods; //遍歷該類的全部父類 for(allMethods = null; !clazz.equals(HttpServlet.class); clazz = clazz.getSuperclass()) { //獲得父類的全部方法 Method[] thisMethods = clazz.getDeclaredMethods(); //獲取第一個父類以後的全部父類的全部方法 if(allMethods != null && allMethods.length > 0) { //將allMethods提取到臨時數組 Method[] subClassMethods = allMethods; //對AllMethods擴容 allMethods = new Method[thisMethods.length + allMethods.length]; //將thisMethods複製到allMethods,佔用allMethods從0到thisMethods.length的位置 System.arraycopy(thisMethods, 0, allMethods, 0, thisMethods.length); //將subClassMethods複製到allMethods,佔用allMethods從thisMethods.length到thisMethods.length加subClassMethods.length的位置 System.arraycopy(subClassMethods, 0, allMethods, thisMethods.length, subClassMethods.length); } else { //獲取第一個父類的全部方法 allMethods = thisMethods; } } return allMethods != null?allMethods:new Method[0]; }
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //鏈接URI字符串和協議版本信息字符串 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,以:分隔名值對,在每對請求頭信息之間使用回車換行進行分隔 buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName)); } //拼接回車換行 buffer.append(CRLF); //獲取buffer長度 int responseLength = buffer.length(); //設置響應類型爲message/http resp.setContentType("message/http"); //設置響應體的長度 resp.setContentLength(responseLength); //輸出字符串消息到響應中 ServletOutputStream out = resp.getOutputStream(); out.print(buffer.toString()); }
咱們能夠看到這上面全部的doGet,doPost,doPut,doDelete都只是一個預處理,都沒有真正實現響應的邏輯,也就是說它們都須要子類去真正實現它們,它們只是一個模板方法。而HttpServlet只是一個抽象類,它不可能去真正實例化的。安全
繼承於HttpServlet是一個HttpServletBean的抽象類,該類只對init()實現了重寫。這裏已經進入了Spring MVC的範疇了,以前都不是Spring MVC實現的。springboot
//這裏使用的是commons的日誌框架,並不像mybatis同樣進行了全部日誌框架的適配 protected final Log logger = LogFactory.getLog(this.getClass());
//必須的配置屬性 private final Set<String> requiredProperties = new HashSet(4);
//添加必須的配置屬性 protected final void addRequiredProperty(String property) { this.requiredProperties.add(property); }
public final void init() throws ServletException { if(this.logger.isDebugEnabled()) { //這個getServletName是從Servlet配置中獲取的,而這個配置是在GenericServlet的初始化階段保存的,具體能夠看後面的源碼說明以及以前GenericServlet的說明 this.logger.debug("Initializing servlet '" + this.getServletName() + "'"); } //使用Serlvet配置的初始化參數建立一個PropertyValues,這是一個名值對的集合,子類也能夠指定哪些屬性是必須的 PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); //若是該名值對不爲空 if(!pvs.isEmpty()) { try { //該代碼等同於BeanWrapper bw = new BeanWrapperImpl(this);BeanWrapper是bean的一個代理包裝器,能夠經過setPropertyValue,getPropertyValue來給實體 //類中具備getter,setter屬性設值和獲取值,這裏是把當前Servlet做爲bean,把bean的屬性存取方法信息放入bw中 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); //getServletContext()是GenericServlet中的方法,上面已有說明,這裏是經過Servlet上下文獲取資源導入器,由此能夠獲得Web應用的內部資源,如文件,圖片等 ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext()); //registerCustomEditor是BeanWrapper的上層接口PropertyEditorRegistry中的方法,這裏是註冊一個經過資源導入器轉化的用戶化編輯器 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment())); //可讓子類增長更多的用戶花編輯器,或者對BeanWrapper進行更多的初始化 this.initBeanWrapper(bw); //注意這裏是羣設屬性,而不是單個設置某一個屬性,把pvs中的全部名值對賦到當前Servlet中,true表示忽略位置屬性 bw.setPropertyValues(pvs, true); } catch (BeansException var4) { if(this.logger.isErrorEnabled()) { this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4); } throw var4; } } //給子類一個機會去初始化其須要的資源,這是一個模板方法 this.initServletBean(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully"); } }
@Nullable public String getServletName() { //這裏調用的是GenericServlet中的方法 return this.getServletConfig() != null?this.getServletConfig().getServletName():null; }
private static class ServletConfigPropertyValues extends MutablePropertyValues { public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) throws ServletException { //獲取必須的配置信息 Set<String> missingProps = !CollectionUtils.isEmpty(requiredProperties)?new HashSet(requiredProperties):null; //獲取配置的初始化參數 Enumeration paramNames = config.getInitParameterNames(); //遍歷全部的配置初始化參數 while(paramNames.hasMoreElements()) { //取出鍵值對 String property = (String)paramNames.nextElement(); Object value = config.getInitParameter(property); //將該鍵值對添加進屬性值 this.addPropertyValue(new PropertyValue(property, value)); //從必須的配置信息中移除該項,進行逐項檢查,直到全部的必須信息都添加完畢 if(missingProps != null) { missingProps.remove(property); } } //若是必須的配置信息沒有添加完,則拋出異常 if(!CollectionUtils.isEmpty(missingProps)) { throw new ServletException("Initialization from ServletConfig for servlet '" + config.getServletName() + "' failed; the following required properties were missing: " + StringUtils.collectionToDelimitedString(missingProps, ", ")); } } }
//模板方法,給子類使用的 protected void initBeanWrapper(BeanWrapper bw) throws BeansException { }
//模板方法,給子類使用的 protected void initServletBean() throws ServletException { }
由於HttpServletBean也爲一個抽象類,它裏面設置了一些模板方法給子類去完成,這也是模板方法模式的特色。session
繼承於HttpServletBean的是一個抽象類FrameworkServlet,它的最主要做用就是加載一個Web應用程序環境,這是經過實現父類的模板方法initServletBean()來完成的。而且重寫HttpServlet中的模板方法,派遣HTTP請求到統一的Spring Web MVC的控制器方法。
@Nullable private WebApplicationContext webApplicationContext; //專用的Web環境,能夠在構造器中賦入
咱們先說一下WebApplicationContext是什麼,它是一個專門爲Web程序準備的上下文共享環境,它是繼承於ApplicationContext,ApplicationContext是Spring的一個核心上下文,經過ApplicationContext能夠獲取在Spring中加載的Bean.好比在Springboot項目中
ApplicationContext context = SpringApplication.run(UserCenterApplication.class, args); XXX userDao = (XXX)context.getBean(XXX.class);
ApplicationContext能夠做用於各類應用程序,並不只侷限於Web應用程序。 WebApplicationContext是專門爲web應用準備的,他容許從相對於web根目錄的路勁中裝載配置文件完成初始化工做,從WebApplicationContext中能夠得到ServletContext的引用,整個Web應用上下文對象將做爲屬性放置在ServletContext中,以便web應用能夠訪問spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法來得到WebApplicationContext對象。 WebApplicationContext擴展了ApplicationContext.在 WebApplicationContext中定義了一個常量 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啓動時, WebApplicationContext以此爲鍵放置在ServletContext屬性列表中。
@Nullable private String contextId; //上下文Id
private boolean refreshEventReceived = false; //用於檢測是否已調用OnRefresh的標誌
private boolean publishContext = true; //是否應該將上下文做爲servletContext屬性發布
protected final void initServletBean() throws ServletException { //打印初始化信息到Servlet容器的日誌中 this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'"); if(this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started"); } //初始化環境的開始時間 long startTime = System.currentTimeMillis(); try { //初始化Servlet的環境,具體見後續源碼 this.webApplicationContext = this.initWebApplicationContext(); this.initFrameworkServlet(); } catch (RuntimeException | ServletException var5) { this.logger.error("Context initialization failed", var5); throw var5; } if(this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
protected WebApplicationContext initWebApplicationContext() { //從Servlet的上下文中取出共享的Web根環境 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; //若是有專用的Web環境 if(this.webApplicationContext != null) { //取得該Web環境 wac = this.webApplicationContext; //若是該Web環境是一個可配置的Web環境實例, ConfigurableWebApplicationContext擴展了WebApplicationContext,它容許經過配置的方式實例化,同時設置兩個重要方 //法, setServletContext(ServletContext context) 爲spring設置web應用上下文,以便二者整合 。 setConfigLocations(String[]locations) 設置Spring配置 //的文件地址。配置有兩種方式,一種是xml來配置的環境,另外一種是如今springboot都使用@Configuration來進行配置類的配置 if(wac instanceof ConfigurableWebApplicationContext) { //獲取該配置Web環境實例 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; //若是該實例未處於活動狀態 if(!cwac.isActive()) { //若是該實例的父環境爲空 if(cwac.getParent() == null) { //將根環境設爲其父環境 cwac.setParent(rootContext); } //刷新更改後的該環境實例,具體源碼見後面 this.configureAndRefreshWebApplicationContext(cwac); } } } if(wac == null) { //若是沒有專用的Web環境,根據contextAttribute屬性在Servlet上下文中查找Web環境 wac = this.findWebApplicationContext(); } if(wac == null) { //若是沒有專用的Web環境,也沒有在Servlet上下文中找到contextAttribute屬性的Web環境,則建立一個以共享的Web根環境爲父容器的XML配置環境 wac = this.createWebApplicationContext(rootContext); } //若是沒有調用過onRefresh()方法 if(!this.refreshEventReceived) { //調用onRefresh方法,這是一個模板方法,供子類實現 this.onRefresh(wac); } //若是應該將該Web環境上下文做爲ServletContext屬性發布 if(this.publishContext) { //獲取屬性名稱,以FrameworkServlet.CONTEXT開頭+在Servlet配置中獲取的名稱 String attrName = this.getServletContextAttributeName(); //將該Web環境發佈到Servlet上下文的屬性當中 this.getServletContext().setAttribute(attrName, wac); if(this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
在WebApplicationContext中的定義
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; //WebApplicationContext.ROOT
這兩段代碼是在WebApplicationContextUtils中的
@Nullable public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); }
@Nullable public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { //Servlet上下文不能爲空,不然會拋出異常 Assert.notNull(sc, "ServletContext must not be null"); //獲取Servlet上下文中的名稱爲WebApplicationContext.ROOT的值,這個值就是一個WebApplicationContext的對象 Object attr = sc.getAttribute(attrName); if(attr == null) { return null; } else if(attr instanceof RuntimeException) { throw (RuntimeException)attr; } else if(attr instanceof Error) { throw (Error)attr; } else if(attr instanceof Exception) { throw new IllegalStateException((Exception)attr); } else if(!(attr instanceof WebApplicationContext)) { throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); } else { return (WebApplicationContext)attr; } }
在FrameworkServlet中
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { //若是Web配置環境對象的類名加地址哈希與其自己的Id相等 if(ObjectUtils.identityToString(wac).equals(wac.getId())) { //若是Servlet的contextId不爲空 if(this.contextId != null) { //將Servlet的contextId設爲該Web配置環境對象的Id wac.setId(this.contextId); } else { //若是Servlet的contextId爲空,將該Web配置環境對象的Id設爲WebApplicationContext:環境裝載地址/Servlet名稱 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName()); } } //設置該Web配置環境對象的各類屬性以及監聽器 wac.setServletContext(this.getServletContext()); wac.setServletConfig(this.getServletConfig()); wac.setNamespace(this.getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null))); //獲取該Web配置環境對象的全部配置項,不管是XML仍是配置類 ConfigurableEnvironment env = wac.getEnvironment(); if(env instanceof ConfigurableWebEnvironment) { //初始化屬性資源 ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig()); } //給子類加載更多的配置項,模板方法 this.postProcessWebApplicationContext(wac); //實施該Web配置環境對象的初始化,將全部的資源對象保存進該Web配置環境對象中,具體見後面的源碼 this.applyInitializers(wac); //刷新Web配置環境對象 wac.refresh(); }
public static String identityToString(@Nullable Object obj) { //若是obj非空,返回對象的類名稱@對象的十六進制內存地址Hash值 return obj == null?"":obj.getClass().getName() + "@" + getIdentityHexString(obj); }
public static String getIdentityHexString(Object obj) { //以十六進制形式返回對象的內存地址Hash值 return Integer.toHexString(System.identityHashCode(obj)); }
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":"; //前綴名WebApplicationContext:
//獲取對象的字符串轉換 public static String getDisplayString(@Nullable Object obj) { return obj == null?"":nullSafeToString(obj); }
//由不一樣的對象類型,轉換成不一樣的字符串 public static String nullSafeToString(@Nullable Object obj) { if(obj == null) { return "null"; } else if(obj instanceof String) { return (String)obj; } else if(obj instanceof Object[]) { return nullSafeToString((Object[])((Object[])obj)); } else if(obj instanceof boolean[]) { return nullSafeToString((boolean[])((boolean[])obj)); } else if(obj instanceof byte[]) { return nullSafeToString((byte[])((byte[])obj)); } else if(obj instanceof char[]) { return nullSafeToString((char[])((char[])obj)); } else if(obj instanceof double[]) { return nullSafeToString((double[])((double[])obj)); } else if(obj instanceof float[]) { return nullSafeToString((float[])((float[])obj)); } else if(obj instanceof int[]) { return nullSafeToString((int[])((int[])obj)); } else if(obj instanceof long[]) { return nullSafeToString((long[])((long[])obj)); } else if(obj instanceof short[]) { return nullSafeToString((short[])((short[])obj)); } else { String str = obj.toString(); return str != null?str:""; } }
//模板方法,給子類使用 protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) { }
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
//分隔標記 private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
//資源配置列表,好比說在springboot中的property或者yml文件中配置的 private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers = new ArrayList<>();
@Nullable private String contextInitializerClasses;
protected void applyInitializers(ConfigurableApplicationContext wac) { //根據Servlet上下文獲取全局初始化類參數 String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM); if (globalClassNames != null) { for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { //遍歷全部的資源類,將每個資源類自己的實例添加到列表 this.contextInitializers.add(loadInitializer(className, wac)); } } //在XML文件中配置的資源 if (this.contextInitializerClasses != null) { for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) { this.contextInitializers.add(loadInitializer(className, wac)); } } //對該資源列表進行排序 AnnotationAwareOrderComparator.sort(this.contextInitializers); for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { //遍歷全部的資源對象,在配置環境上下文中保存 initializer.initialize(wac); } }
private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer( String className, ConfigurableApplicationContext wac) { try { //獲取屬性配置的類實例 Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader()); //獲取在ApplicationContextInitializer接口中的泛型在initializerClass中的具體類實例 Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); //若是該泛型類不爲null,且wac對象不能被轉化爲該泛型類,拋出異常 if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { throw new ApplicationContextException(String.format( "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } //返回initializerClass類自己的實例 return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); } catch (ClassNotFoundException ex) { throw new ApplicationContextException(String.format("Could not load class [%s] specified " + "via 'contextInitializerClasses' init-param", className), ex); } }
@Nullable protected WebApplicationContext findWebApplicationContext() { //獲取contextAttribute屬性 String attrName = getContextAttribute(); if (attrName == null) { return null; } //在Servlet上下文中查找contextAttribute的Web環境 WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }
@Nullable private String contextAttribute;
@Nullable public String getContextAttribute() { return this.contextAttribute; }
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); }
@Nullable private String contextConfigLocation; //顯示上下文配置位置
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //獲取XML配置的Web環境類實例 Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } //若是該contextClass不是可配置的Web環境接口的實現類,則拋出異常 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } //獲取XML配置的Web環境自己的實例 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //設置該XML配置的Web環境實例的環境 wac.setEnvironment(getEnvironment()); //設置該XML配置的Web環境實例的父容器 wac.setParent(parent); //獲取顯示上下文配置位置 String configLocation = getContextConfigLocation(); if (configLocation != null) { //設置該XML配置的Web環境實例的配置位置 wac.setConfigLocation(configLocation); } //刷新該XML配置的Web環境實例 configureAndRefreshWebApplicationContext(wac); return wac; }
//默認XML配置的Web環境 public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
public Class<?> getContextClass() { return this.contextClass; }
//模板方法 protected void onRefresh(ApplicationContext context) { }
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; //FrameworkServlet.CONTEXT
public String getServletContextAttributeName() { //返回FrameworkServlet.CONTEXT+在Servlet配置中獲取的名稱 return SERVLET_CONTEXT_PREFIX + getServletName(); }
咱們再來看一下它的分發
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
@Override protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
@Override protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
@Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
這幾種HTTP方法都調用了同一個方法processRequest
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //保存當前線程局部存儲的地域信息,以備在處理完這個請求後恢復 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //獲取請求的地域信息 LocaleContext localeContext = buildLocaleContext(request); //保存當前線程局部存儲的請求屬性,以備在處理完這個請求後恢復 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //獲取請求屬性 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //獲取Web異步管理器 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); //將FrameworkServlet,請求綁定攔截器做爲鍵值對放入LinkedHashMap中 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //將請求屬性,地域信息添加爲當前線程的引用,且移除子線程的引用 initContextHolders(request, localeContext, requestAttributes); try { //Spring MVC真正的派遣工做流,模板方法,子類必須實現的抽象方法 doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { //恢復以前的保存信息 resetContextHolders(request, previousLocaleContext, previousAttributes); //若是請求屬性不爲null if (requestAttributes != null) { //完成全部的請求步驟 requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } //經過事件傳遞,讓註冊了事件監聽器的bean能夠接收事件 publishRequestHandledEvent(request, response, startTime, failureCause); } }
在LocaleContextHolder抽象類中
//定義一個名爲LocaleContext的區域信息當前線程安全引用 private static final ThreadLocal<LocaleContext> localeContextHolder = new NamedThreadLocal("LocaleContext");
//定義一個可被子線程使用的名爲LocaleContext的區域信息當前線程安全引用 private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder = new NamedInheritableThreadLocal("LocaleContext");
@Nullable public static LocaleContext getLocaleContext() { //獲取這個區域信息 LocaleContext localeContext = (LocaleContext)localeContextHolder.get(); //若是該區域信息爲null if(localeContext == null) { //從子線程的容器獲取 localeContext = (LocaleContext)inheritableLocaleContextHolder.get(); } return localeContext; }
@Nullable protected LocaleContext buildLocaleContext(HttpServletRequest request) { //以請求的Locale構建一個地域信息對象 return new SimpleLocaleContext(request.getLocale()); }
在RequestContextHolder中
//定義一個名字爲Request attributes的請求屬性當前線程安全引用 private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes"); //定義一個名字爲Request attributes的可被子線程使用的請求屬性當前線程安全引用 private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
@Nullable public static RequestAttributes getRequestAttributes() { //獲取請求屬性 RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get(); //若是該請求屬性爲null if(attributes == null) { //從子線程的容器獲取 attributes = (RequestAttributes)inheritableRequestAttributesHolder.get(); } return attributes; }
@Nullable protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) { //若是當前線程的局部存儲的請求屬性爲null或者該請求屬性爲一個Servlet請求屬性實例 if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) { //用request,response構建一個新的Servlet請求屬性實例 return new ServletRequestAttributes(request, response); } else { return null; } }
在WebAsyncUtils中
public static final String WEB_ASYNC_MANAGER_ATTRIBUTE = WebAsyncManager.class.getName() + ".WEB_ASYNC_MANAGER"; //WebAsyncManager.WEB_ASYNC_MANAGER
public static WebAsyncManager getAsyncManager(ServletRequest servletRequest) { WebAsyncManager asyncManager = null; //從Servlet請求中獲取WebAsyncManager.WEB_ASYNC_MANAGER屬性對象 Object asyncManagerAttr = servletRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE); //若是該對象爲Web異步管理器實例 if(asyncManagerAttr instanceof WebAsyncManager) { //獲取該對象 asyncManager = (WebAsyncManager)asyncManagerAttr; } //若是獲取的對象爲null if(asyncManager == null) { //生成一個新的Web異步管理器實例 asyncManager = new WebAsyncManager(); //將該新的Web異步管理器實例放入Servlet請求屬性中 servletRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager); } return asyncManager; }
在WebAsyncManager中
private final Map<Object, CallableProcessingInterceptor> callableInterceptors; //可回調的攔截器Map,被初始化爲一個LinkedHashMap,主要是爲了保證它的有序性
public void registerCallableInterceptor(Object key, CallableProcessingInterceptor interceptor) { Assert.notNull(key, "Key is required"); Assert.notNull(interceptor, "CallableProcessingInterceptor is required"); //將key和攔截器添加到map中 this.callableInterceptors.put(key, interceptor); }
private boolean threadContextInheritable = false; //是否暴露地域信息和請求屬性給子線程
private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) { //若是地域信息不爲null if (localeContext != null) { //將地域信息添加爲當前線程的引用,且清空子線程的引用,由於threadContextInheritable默認爲false LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); } //若是請求屬性不爲null if (requestAttributes != null) { //將請求屬性添加爲當前線程的引用,且清空子線程的引用 RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } }
public static void setLocaleContext(@Nullable LocaleContext localeContext, boolean inheritable) { if(localeContext == null) { //若是地域信息爲null,清空當前線程的地域信息 resetLocaleContext(); } else if(inheritable) { //若是inheritable爲true,將地域信息設置爲可被子線程使用的線程引用 inheritableLocaleContextHolder.set(localeContext); //移除當前線程的地域信息 localeContextHolder.remove(); } else { //若是inheritable爲false,將地域信息設置爲當前線程的引用 localeContextHolder.set(localeContext); //移除子線程可用的地域信息 inheritableLocaleContextHolder.remove(); } }
public static void resetLocaleContext() { //移除當前線程的地域信息 localeContextHolder.remove(); //移除當前線程的可被子線程繼承使用的地域信息 inheritableLocaleContextHolder.remove(); }
//子類必須實現的抽象方法 protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;
private void resetContextHolders(HttpServletRequest request, @Nullable LocaleContext prevLocaleContext, @Nullable RequestAttributes previousAttributes) { LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable); RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable); if (logger.isTraceEnabled()) { logger.trace("Cleared thread-bound request context: " + request); } }
在AbstractRequestAttributes中
protected final Map<String, Runnable> requestDestructionCallbacks = new LinkedHashMap(8); //請求銷燬的回調任務映射
public void requestCompleted() { //執行全部的線程任務,執行完清空任務 this.executeRequestDestructionCallbacks(); //更新全部的會話屬性到實際會話中 this.updateAccessedSessionAttributes(); //將請求的活動狀態設置爲false this.requestActive = false; }
private void executeRequestDestructionCallbacks() { //獲取請求的銷燬回調映射 Map var1 = this.requestDestructionCallbacks; synchronized(this.requestDestructionCallbacks) { //鎖同步該映射 //獲取其線程value的迭代器 Iterator var2 = this.requestDestructionCallbacks.values().iterator(); //執行全部線程 while(var2.hasNext()) { Runnable runnable = (Runnable)var2.next(); runnable.run(); } //清空該請求的銷燬回調映射 this.requestDestructionCallbacks.clear(); } }
在AbstractRequestAttributes的子類ServletRequestAttributes中
private final Map<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<>(1); //須要更新的會話屬性映射
@Override protected void updateAccessedSessionAttributes() { //若是會話屬性映射不爲空 if (!this.sessionAttributesToUpdate.isEmpty()) { //從請求中獲得會話 HttpSession session = getSession(false); //若是該會話不爲null if (session != null) { try { //遍歷全部的會話屬性映射 for (Map.Entry<String, Object> entry : this.sessionAttributesToUpdate.entrySet()) { //獲取名稱 String name = entry.getKey(); //獲取值 Object newValue = entry.getValue(); //獲取該名稱在會話中的值 Object oldValue = session.getAttribute(name); //若是獲取到的值跟會話中的值相同,且該值不是經常使用類型類(Integer,Double等等) if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) { //將該值放入會話的name屬性中 session.setAttribute(name, newValue); } } } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } //清空須要更新的會話屬性映射 this.sessionAttributesToUpdate.clear(); } }
@Nullable protected final HttpSession getSession(boolean allowCreate) { //若是原始請求還處於活動狀態 if (isRequestActive()) { //從請求中獲取會話 HttpSession session = this.request.getSession(allowCreate); //將該會話設置給會話屬性 this.session = session; return session; } else { // 若是原始請求未處於活動狀態,獲取會話屬性 HttpSession session = this.session; //若是該會話爲null if (session == null) { if (allowCreate) { throw new IllegalStateException( "No session found and request already completed - cannot create new session!"); } else { session = this.request.getSession(false); this.session = session; } } return session; } }
private boolean publishEvents = true; //是否在每一個請求結束時,發佈一個ServletRequest事件
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) { //若是能夠發佈一個ServletRequest事件且Web配置環境對象不爲null if (this.publishEvents && this.webApplicationContext != null) { //計算這個請求的總處理時間. long processingTime = System.currentTimeMillis() - startTime; //將該時間傳遞給應用程序環境,註冊事件監聽器的Bean就會接收到這個事件,能夠用於統計分析 this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, response.getStatus())); } }
如今咱們來看一下doOptions,doTrace的請求處理
private boolean dispatchOptionsRequest = false; //是否將請求發送到doService
@Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //是否將請求發送到Spring MVC處理,若是是原始方法則放行 if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { processRequest(request, response); if (response.containsHeader("Allow")) { // Proper OPTIONS response coming from a handler - we're done. return; } } // 調用HTTPServlet對OPTIONS方法的默認實現 super.doOptions(request, new HttpServletResponseWrapper(response) { @Override public void setHeader(String name, String value) { if ("Allow".equals(name)) { value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name(); } super.setHeader(name, value); } }); }
@Override protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //是否將請求發送到Spring MVC處理 if (this.dispatchTraceRequest) { processRequest(request, response); if ("message/http".equals(response.getContentType())) { // Proper TRACE response coming from a handler - we're done. return; } } // 調用HTTPServlet對TRACE方法的默認實現 super.doTrace(request, response); }
以上這兩個方法能夠被子類改寫,改寫後不會對Spring WEB MVC流程有任何影響
繼承於FrameworkServlet是Spring MVC的最終實現類DispatcherServlet,DispatcherServlet在經過監聽事件得知Servlet的Web應用程序環境初始化或者刷新後,首先在加載的Web應用程序環境(包括主環境和子環境)中查找是否是已經註冊了相應的組件,若是查找到了註冊的組件,就會使用這些組件;若是沒有找到就會加載默認的配置策略。這些默認的配置策略被保存在一個屬性文件裏,這個屬性文件和DispatcherServlet在同一個目錄裏,文件名爲DispatcherServlet.properties。DispatcherServlet經過讀取不一樣組件配置的實現類名,實例化而且初始化這些組件的實現。
DispatcherServlet.properties的內容以下
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
從DispatcherServlet的靜態代碼塊就能夠知道這一點了
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage()); } }
Spring Web MVC的組件按照數量來劃分,可分爲可選組件,單值組件和多值組件
initStrategies()方法是在Web應用程序環境初始化或者刷新時被調用的,加載了Srping Web MVC所需的全部組件
protected void initStrategies(ApplicationContext context) { //初始化多部(multipart)請求解析器,沒有默認的實現 initMultipartResolver(context); //初始化地域解析器,默認的實現是AcceptHeaderLocaleResolver initLocaleResolver(context); //初始化主題解析器,默認的實現是FixedThemeResolver initThemeResolver(context); //初始化處理器映射,這是個集合,默認的實現是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping initHandlerMappings(context); //初始化處理器適配器,這是個集合,默認的實現是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter initHandlerAdapters(context); //初始化處理器異常解析器,這是個集合,默認的實現是AnnotationMethodhandlerExceptionResolver,ResponseStatusExceptionResolver和 //DefaultHandlerExceptionResolver initHandlerExceptionResolvers(context); //初始化請求到視圖名解析器,默認的實現是DefaultRequestToViewNameTranslator initRequestToViewNameTranslator(context); //初始化視圖解析器,這是個集合,默認的實現是InternalResourceViewResolver initViewResolvers(context); //初始化重定向數據保存器,默認的實現是SessionFlashMapManager initFlashMapManager(context); }
對可選組件的代碼以註釋以下
@Nullable private MultipartResolver multipartResolver; //此servlet使用的多部分解析程序
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; //此命名空間的bean工廠中multipartresolver對象的已知名稱
private void initMultipartResolver(ApplicationContext context) { try { //從配置的Web應用程序環境中查找多部請求解析器 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isDebugEnabled()) { //若是沒有多部請求解析器在Web應用程序環境中被註冊,則忽略這種狀況,畢竟不是全部的應用程序都須要使用它,多部請求一般會被應用到文件上傳的狀況中 logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + "': no multipart request handling provided"); } } }
對單值組件的代碼及註釋以下
@Nullable private LocaleResolver localeResolver; //此servlet使用的地域請求解析器
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; //此命名空間的bean工廠中localeresolver對象的已知名稱。
private static final Properties defaultStrategies; //從DispatcherServlet.properties中引入的資源,從靜態代碼塊中引入
private void initLocaleResolver(ApplicationContext context) { try { //從配置的Web應用程序環境中查找地域請求解析器 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using LocaleResolver [" + this.localeResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { //若是在Web應用程序中沒有地域請求解析器,則查找默認的配置策略,而且根據配置初始化默認的地域請求解析器 this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); if (logger.isDebugEnabled()) { logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME + "': using default [" + this.localeResolver + "]"); } } }
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) { //獲取在DispatcherServlet.properties解析出的策略對象實例的列表 List<T> strategies = getDefaultStrategies(context, strategyInterface); //若是該策略列表長度不等於1,拋出異常 if (strategies.size() != 1) { throw new BeanInitializationException( "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]"); } //返回策略對象實例 return strategies.get(0); }
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { //獲取該策略接口類的類名稱 String key = strategyInterface.getName(); //從默認的配置策略中獲取該接口名對應的值 String value = defaultStrategies.getProperty(key); //若是該值不爲null if (value != null) { //以,分隔成一個字符串數組classNames String[] classNames = StringUtils.commaDelimitedListToStringArray(value); //定義一個新的策略列表,長度爲classNames的長度 List<T> strategies = new ArrayList<>(classNames.length); //遍歷classNames數組 for (String className : classNames) { try { //反射獲取類實例 Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); //根據配置環境建立獲取的類自己的實例 Object strategy = createDefaultStrategy(context, clazz); //將建立的類自己的實例添加到策略列表中 strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException( "Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", err); } } //返回該策略列表 return strategies; } else { //若是該值爲null,返回一個新的鏈表列表 return new LinkedList<>(); } }
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { //返回clazz類自己的bean實例 return context.getAutowireCapableBeanFactory().createBean(clazz); }
initThemeResolver和initRequestToViewNameTranslator一樣初始化單值組件,與initLocaleResolver具備相同的實現。
初始化多值組件的代碼及註釋以下
@Nullable private List<HandlerMapping> handlerMappings; //此servlet使用的處理器映射列表
private boolean detectAllHandlerMappings = true; //自動檢測處理器映射列表
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //若是配置爲自動檢測全部的處理器映射 if (this.detectAllHandlerMappings) { //在加載的Web應用程序中查找全部實現處理器映射接口的Bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); //若是該映射不爲空 if (!matchingBeans.isEmpty()) { //將該Servlet的處理器映射列表實例化爲該映射的值集合(Bean集合)的數組列表 this.handlerMappings = new ArrayList<>(matchingBeans.values()); //根據這些Bean所實現的標籤接口進行排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { //若是沒有配置爲自動檢測全部的處理器映射,則在Web應用程序環境中查找名稱爲handlerMapping的Bean做爲處理器映射 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); //構造單個Bean的集合 this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { //若是仍然沒有查找到註冊的處理器映射的實現,則使用默認的配置策略加載處理器映射 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
在BeanFactoryUtils中
public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException { Assert.notNull(lbf, "ListableBeanFactory must not be null"); //初始化一個初始容量爲4的鏈表型HashMap Map<String, T> result = new LinkedHashMap(4); //根據type類實例,獲得全部的Bean,並將名稱與bean的映射所有添加到result中 result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit)); //若是lbf是一個分層BeanFactory實例 if(lbf instanceof HierarchicalBeanFactory) { //獲取分層BeanFactory HierarchicalBeanFactory hbf = (HierarchicalBeanFactory)lbf; //若是該分層BeanFactory的父工廠是一個列表型BeanFactory if(hbf.getParentBeanFactory() instanceof ListableBeanFactory) { //再根據type取出其父工廠全部的Bean,並將名稱與bean放入父工廠結果映射中 Map<String, T> parentResult = beansOfTypeIncludingAncestors((ListableBeanFactory)hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit); //遍歷父工廠的映射 parentResult.forEach((beanName, beanType) -> { //若是result以及hbf自己都不包含beanName if(!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { //將beanName,beanType做爲key,value添加到result中 result.put(beanName, beanType); } }); } } return result; }
initHandlerAdapters、initHandlerExceptionResolvers、 initViewResolvers一樣是初始化多值組件,與initHandlerMappings具備相同的實現。
以前在FrameworkServlet中說了HTTP請求的派遣,有一個抽象方法必需要實現的,就是doService,請注意FrameworkServlet在派遣以前保存了請求的屬性信息,在完成服務後恢復了這些信息。
private boolean cleanupAfterInclude = true; //在包含請求以後執行請求屬性的清除?
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet"; //DispatcherServlet默認策略屬性的通用前綴。
//請求屬性以保存當前Web應用程序上下文。 //不然,只能經過標籤等獲取全局Web應用程序上下文。 public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; //DispatcherServlet.CONTEXT
//請求屬性以保存當前的地域解析器,可由視圖檢索。 public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; //DispatcherServlet.LOCALE_RESOLVER
//請求屬性以保存當前的主題解析器,可由視圖檢索。 public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; //DispatcherServlet.THEME_RESOLVER
//請求屬性以保存當前主題源,可由視圖檢索。 public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; //DispatcherServlet.THEME_SOURCE
//保存只讀的請求屬性的名稱。 //若是有重定向屬性,則由之前的請求保存。 public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; //DispatcherServlet.INPUT_FLASH_MAP
//保存「output"flashmap的請求屬性的名稱 //爲後續請求保存的屬性。 public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; //DispatcherServlet.OUTPUT_FLASH_MAP
//保存flashmapmanager的請求屬性的名稱。 public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; //DispatcherServlet.FLASH_MAP_MANAGER
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } //對於一個include請求,除了須要保存和恢復請求環境信息,還須要保存請求屬性,在請求處理完畢後,若是其中的某個屬性發生改變,則須要恢復該屬性 Map<String, Object> attributesSnapshot = null; //若是請求的屬性中包含javax.servlet.include.request_uri if (WebUtils.isIncludeRequest(request)) { //將屬性快照實例化 attributesSnapshot = new HashMap<>(); //遍歷全部的請求屬性 Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); //若是請求清除屬性開關打開(默認true)或者請求屬性名以org.springframework.web.servlet前綴開頭 if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { //保存全部請求屬性 attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } //在request屬性裏存儲Web應用程序環境 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //在request屬性裏存儲地域解析器 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); //在request屬性裏存儲主題解析器 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); //在request屬性裏存儲主題源 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); //若是該Servlet的重定向管理器不爲null if (this.flashMapManager != null) { //檢索更新重定向信息,獲取第一個排序後的重定向實例 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { //若是該重定向實例不爲null,則將請求的DispatcherServlet.INPUT_FLASH_MAP(只讀的重定向)屬性設爲該實例 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } //爲後續請求重定向在請求屬性DispatcherServlet.OUTPUT_FLASH_MAP中設定一個新的重定向實例 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); //在請求屬性DispatcherServlet.FLASH_MAP_MANAGER中設定爲該Servlet的重定向管理器 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { //開始Spring Web MVC的核心工做流 doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { //若是是HTTP多部請求,則將其轉換而且封裝成一個簡單的HTTP請求 processedRequest = checkMultipart(request); //該請求是不是多部請求 multipartRequestParsed = (processedRequest != request); //根據處理器的映射,獲取處理器執行鏈 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { //若是沒有發現任何處理器,則發送錯誤信息 noHandlerFound(processedRequest, response); return; } //查找支持的處理器適配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //獲取請求方法 String method = request.getMethod(); //判斷是不是GET方法 boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //若是未調用前置攔截器,直接返回 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //經過獲取的處理器適配器代理調用處理器 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //應用默認視圖名 applyDefaultViewName(processedRequest, mv); //應用後置攔截器 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { //若是該Servlet的多部請求解析器不爲null且該請求爲多部請求 if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { //若是內部請求也爲多部請求,打印日誌 if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) { logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " + "this typically results from an additional MultipartFilter in web.xml"); } else if (hasMultipartException(request) ) { logger.debug("Multipart resolution failed for current request before - " + "skipping re-resolution for undisturbed error rendering"); } else { try { return this.multipartResolver.resolveMultipart(request); } catch (MultipartException ex) { if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) { logger.debug("Multipart resolution failed for error dispatch", ex); // Keep processing error dispatch with regular request handle below } else { throw ex; } } } } // If not returned before: return original request. return request; }
在WebUtils中
public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri";
public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";
public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
public static boolean isIncludeRequest(ServletRequest request) { return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null); }
public static void setSessionAttribute(HttpServletRequest request, String name, @Nullable Object value) { Assert.notNull(request, "Request must not be null"); if (value != null) { request.getSession().setAttribute(name, value); } else { HttpSession session = request.getSession(false); if (session != null) { session.removeAttribute(name); } } }
@SuppressWarnings("unchecked") @Nullable public static <T> T getNativeRequest(ServletRequest request, @Nullable Class<T> requiredType) { if (requiredType != null) { //若是必須的類型類實例爲請求對象實例,返回該請求 if (requiredType.isInstance(request)) { return (T) request; } //若是該請求爲一個Servlet請求裝飾器實例 else if (request instanceof ServletRequestWrapper) { return getNativeRequest(((ServletRequestWrapper) request).getRequest(), requiredType); } } return null; }
在AbstractFlashMapManager中
private static final Object DEFAULT_FLASH_MAPS_MUTEX = new Object();
@Override @Nullable public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) { //從會話中獲取的SessionFlashMapManager.FLASH_MAPS屬性的值 List<FlashMap> allFlashMaps = retrieveFlashMaps(request); if (CollectionUtils.isEmpty(allFlashMaps)) { return null; } if (logger.isDebugEnabled()) { logger.debug("Retrieved FlashMap(s): " + allFlashMaps); } //獲得全部的到期會話重定向 List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps); //獲取請求排序後的第一個重定向實例 FlashMap match = getMatchingFlashMap(allFlashMaps, request); if (match != null) { //將該實例添加到到期會話重定向列表中 mapsToRemove.add(match); } //若是到期會話重定向列表不爲空 if (!mapsToRemove.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Removing FlashMap(s): " + mapsToRemove); } //獲得在請求中與重定向互斥的對象 Object mutex = getFlashMapsMutex(request); //若是該互斥對象不爲null if (mutex != null) { synchronized (mutex) { //鎖同步該互斥對象 //從會話中獲取的SessionFlashMapManager.FLASH_MAPS屬性的值 allFlashMaps = retrieveFlashMaps(request); if (allFlashMaps != null) { //若是該屬性值(List)不爲null,移除全部的過時重定向 allFlashMaps.removeAll(mapsToRemove); //更新會話對象的重定向屬性信息 updateFlashMaps(allFlashMaps, request, response); } } } else { //移除全部的過時重定向 allFlashMaps.removeAll(mapsToRemove); //更新會話對象的重定向屬性信息 updateFlashMaps(allFlashMaps, request, response); } } //返回排序後的第一個重定向實例 return match; }
private List<FlashMap> getExpiredFlashMaps(List<FlashMap> allMaps) { //實例化一個鏈表List的重定向存儲實例 List<FlashMap> result = new LinkedList<>(); //遍歷全部的會話重定向存儲 for (FlashMap map : allMaps) { //若是會話重定向到期 if (map.isExpired()) { //result添加該會話重定向對象 result.add(map); } } return result; }
@Nullable private FlashMap getMatchingFlashMap(List<FlashMap> allMaps, HttpServletRequest request) { //實例化一個重定向鏈表列表 List<FlashMap> result = new LinkedList<>(); //遍歷全部的會話重定向存儲 for (FlashMap flashMap : allMaps) { //若是該重定向實例是request請求的重定向 if (isFlashMapForRequest(flashMap, request)) { //將該重定向實例添加到列表中 result.add(flashMap); } } //若是該列表不爲空 if (!result.isEmpty()) { //對該列表排序 Collections.sort(result); if (logger.isDebugEnabled()) { logger.debug("Found matching FlashMap(s): " + result); } //返回排序後的第一個重定向對象實例 return result.get(0); } return null; }
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) { //獲取重定向的目標地址 String expectedPath = flashMap.getTargetRequestPath(); //若是該目標地址不爲null if (expectedPath != null) { //獲取處理後的請求uri字符串 String requestUri = getUrlPathHelper().getOriginatingRequestUri(request); if (!requestUri.equals(expectedPath) && !requestUri.equals(expectedPath + "/")) { //若是該字符串不等於重定向的目標地址,返回false return false; } } //獲取實際請求的查詢字符串,以鍵值(值爲List)對形式存儲 MultiValueMap<String, String> actualParams = getOriginatingRequestParams(request); //獲取重定向目標請求參數的map(值爲List) MultiValueMap<String, String> expectedParams = flashMap.getTargetRequestParams(); //遍歷重定向目標請求參數 for (String expectedName : expectedParams.keySet()) { //在實際請求的查詢字符串映射中獲取重定向鍵的值(列表) List<String> actualValues = actualParams.get(expectedName); if (actualValues == null) { return false; } for (String expectedValue : expectedParams.get(expectedName)) { if (!actualValues.contains(expectedValue)) { return false; } } } return true; }
private MultiValueMap<String, String> getOriginatingRequestParams(HttpServletRequest request) { //獲取請求的查詢字符串 String query = getUrlPathHelper().getOriginatingQueryString(request); //將請求參數以鍵值(值爲List)對形式存儲 return ServletUriComponentsBuilder.fromPath("/").query(query).build().getQueryParams(); }
@Nullable protected Object getFlashMapsMutex(HttpServletRequest request) { return DEFAULT_FLASH_MAPS_MUTEX; }
在SessionFlashMapManager中
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS"; //SessionFlashMapManager.FLASH_MAPS
@Override @SuppressWarnings("unchecked") @Nullable protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) { //獲取request的會話 HttpSession session = request.getSession(false); //若是該會話不爲null,返回從會話中獲取的SessionFlashMapManager.FLASH_MAPS屬性的值,該值爲一個列表,不然返回null return (session != null ? (List<FlashMap>) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null); }
@Override protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) { //若是重定向列表不爲空,將請求會話的SessionFlashMapManager.FLASH_MAPS屬性設置爲重定向列表,不然從會話中移除該屬性 WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, (!flashMaps.isEmpty() ? flashMaps : null)); }
在FlashMap中
private long expirationTime = -1; //到期時間
@Nullable private String targetRequestPath; //目標請求地址
private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<>(4); //目標請求參數map
public boolean isExpired() { return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime); }
@Nullable public String getTargetRequestPath() { return this.targetRequestPath; }
public MultiValueMap<String, String> getTargetRequestParams() { return this.targetRequestParams; }
在UrlPathHelper中
private static final String WEBSPHERE_URI_ATTRIBUTE = "com.ibm.websphere.servlet.uri_non_decoded"; //特殊URI屬性
private boolean removeSemicolonContent = true; //移除分號標誌
private boolean urlDecode = true; //url譯碼
private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
public String getOriginatingRequestUri(HttpServletRequest request) { //獲取請求的特殊URI屬性 String uri = (String) request.getAttribute(WEBSPHERE_URI_ATTRIBUTE); if (uri == null) { //若是該uri爲null,獲取請求屬性javax.servlet.forward.request_uri的值 uri = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE); if (uri == null) { //若是uri依然爲null,獲取請求的URI uri = request.getRequestURI(); } } //返回清除了分號且轉碼後又清除了雙斜槓的字符串 return decodeAndCleanUriString(request, uri); }
private String decodeAndCleanUriString(HttpServletRequest request, String uri) { //從uri中移除全部的分號 uri = removeSemicolonContent(uri); //對uri以請求的字符集編碼進行轉碼 uri = decodeRequestString(request, uri); //清除雙斜槓 uri = getSanitizedPath(uri); return uri; }
public String removeSemicolonContent(String requestUri) { return (this.removeSemicolonContent ? removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri)); }
private String removeSemicolonContentInternal(String requestUri) { //獲取分號的位置 int semicolonIndex = requestUri.indexOf(';'); while (semicolonIndex != -1) { //從分號的位置開始獲取斜線的位置 int slashIndex = requestUri.indexOf('/', semicolonIndex); String start = requestUri.substring(0, semicolonIndex); requestUri = (slashIndex != -1) ? start + requestUri.substring(slashIndex) : start; semicolonIndex = requestUri.indexOf(';', semicolonIndex); } return requestUri; }
public String decodeRequestString(HttpServletRequest request, String source) { if (this.urlDecode) { return decodeInternal(request, source); } return source; }
private String decodeInternal(HttpServletRequest request, String source) { //獲取請求的字符集編碼 String enc = determineEncoding(request); try { //對uri以請求的字符集編碼進行轉碼 return UriUtils.decode(source, enc); } catch (UnsupportedCharsetException ex) { if (logger.isWarnEnabled()) { logger.warn("Could not decode request string [" + source + "] with encoding '" + enc + "': falling back to platform default encoding; exception message: " + ex.getMessage()); } return URLDecoder.decode(source); } }
protected String determineEncoding(HttpServletRequest request) { //獲得請求的字符集編碼 String enc = request.getCharacterEncoding(); if (enc == null) { //若是拿不到請求的字符集編碼,則默認8859-1編碼 enc = getDefaultEncoding(); } return enc; }
protected String getDefaultEncoding() { return this.defaultEncoding; }
private String getSanitizedPath(final String path) { //獲取uri String sanitized = path; while (true) { int index = sanitized.indexOf("//"); if (index < 0) { break; } else { sanitized = sanitized.substring(0, index) + sanitized.substring(index + 1); } } return sanitized; }
public String getOriginatingQueryString(HttpServletRequest request) { //若是請求的屬性中拿的到javax.servlet.forward.request_uri(重定向uri屬性)的值或者javax.servlet.error.request_uri(錯誤請求uri屬性)的值 if ((request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE) != null) || (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null)) { //返回請求屬性javax.servlet.forward.query_string(查詢字符串)中的值 return (String) request.getAttribute(WebUtils.FORWARD_QUERY_STRING_ATTRIBUTE); } else { //若是都拿不到,返回請求的查詢字符串 return request.getQueryString(); } }
在UriUtils中
public static String decode(String source, String encoding) { return StringUtils.uriDecode(source, Charset.forName(encoding)); }
在StringUtils中
public static String uriDecode(String source, Charset charset) { //得到uri的長度 int length = source.length(); if (length == 0) { return source; } Assert.notNull(charset, "Charset must not be null"); //創建一個uri長度的字節數組輸出流 ByteArrayOutputStream bos = new ByteArrayOutputStream(length); boolean changed = false; //從0到uri長度的循環 for (int i = 0; i < length; i++) { //遍歷uri的每個字符 int ch = source.charAt(i); //若是該字符爲% if (ch == '%') { //將%後2位轉化成字節數組 if (i + 2 < length) { char hex1 = source.charAt(i + 1); char hex2 = source.charAt(i + 2); int u = Character.digit(hex1, 16); int l = Character.digit(hex2, 16); if (u == -1 || l == -1) { throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); } bos.write((char) ((u << 4) + l)); i += 2; changed = true; } else { throw new IllegalArgumentException("Invalid encoded sequence \"" + source.substring(i) + "\""); } } else { bos.write(ch); } } return (changed ? new String(bos.toByteArray(), charset) : source); }
在UriComponentsBuilder中
private CompositePathComponentBuilder pathBuilder;
private final LinkedList<PathComponentBuilder> builders = new LinkedList<>();
@Nullable private String ssp;
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");
public static UriComponentsBuilder fromPath(String path) { UriComponentsBuilder builder = new UriComponentsBuilder(); builder.path(path); return builder; }
@Override public UriComponentsBuilder path(String path) { //將path添加到全路徑後,以/分隔 this.pathBuilder.addPath(path); resetSchemeSpecificPart(); return this; }
public void addPath(String path) { //若是path有字符(不包含空格,回車,tab) if (StringUtils.hasText(path)) { //獲取鏈表列表builders中的最後一個PathSegmentComponentBuilder(分段路徑)對象實例 PathSegmentComponentBuilder psBuilder = getLastBuilder(PathSegmentComponentBuilder.class); //獲取鏈表列表builders中最後一個FullPathComponentBuilder(全路徑)對象實例 FullPathComponentBuilder fpBuilder = getLastBuilder(FullPathComponentBuilder.class); //若是該分段路徑實例不爲null if (psBuilder != null) { 在path前添加/(若是沒有) path = (path.startsWith("/") ? path : "/" + path); } //若是全路徑實例爲null if (fpBuilder == null) { //生成一個新的全路徑實例對象 fpBuilder = new FullPathComponentBuilder(); //並把該對象添加到鏈表列表builders中 this.builders.add(fpBuilder); } //將path添加到全路徑實例中,實際是一個StringBuilder對象 fpBuilder.append(path); } }
@SuppressWarnings("unchecked") @Nullable private <T> T getLastBuilder(Class<T> builderClass) { if (!this.builders.isEmpty()) { //若是該鏈表列表不爲空,獲取最後一個節點 PathComponentBuilder last = this.builders.getLast(); //若是last是一個builderCalss的對象實例 if (builderClass.isInstance(last)) { return (T) last; } } return null; }
private void resetSchemeSpecificPart() { this.ssp = null; }
@Override public UriComponentsBuilder query(@Nullable String query) { if (query != null) { //對查詢語句進行正則匹配 Matcher matcher = QUERY_PARAM_PATTERN.matcher(query); //當部分匹配時 while (matcher.find()) { String name = matcher.group(1); String eq = matcher.group(2); String value = matcher.group(3); queryParam(name, (value != null ? value : (StringUtils.hasLength(eq) ? "" : null))); } } else { this.queryParams.clear(); } resetSchemeSpecificPart(); return this; }