Spring MVC的模板方法模式

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的組件按照數量來劃分,可分爲可選組件,單值組件和多值組件

  • 可選組件指在整個流程中可能須要也可能不須要的組件,例如MultipartResolver。
  • 單值組件指在整個流程中只須要一個這樣的組件,例如ThemeResolver、LocaleResolver和RequestToViewNameTranslator。
  • 多值組件指在整個流程中能夠配置多個實現的組件,在運行時輪詢查找哪一個組件支持當前的HTTP請求,若存在這樣的組件,則使用其進行處理。

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;
}
相關文章
相關標籤/搜索