Spring MVC的模板方法模式

原创
2019/03/24 12:37
阅读数 493

Spring MVC的全部组件继承图如下所示

模板方法模式是由抽象类或接口定义好执行顺序,由子类去实现,但无论子类如何实现,他都得按照抽象类或者接口定义好的顺序去执行。实例代码请参考 设计模式整理 Servlet的起点从Servlet接口开始。

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的抽象类,我们来看一下这三个方法的实现

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中将会使用到。

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方法,因为它们不是抽象方法,但是它必须要实现抽象方法service

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实现的。

//这里使用的是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也为一个抽象类,它里面设置了一些模板方法给子类去完成,这也是模板方法模式的特点。

继承于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;
}
展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部