前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring MVC的模板方法模式 顶

Spring MVC的模板方法模式 顶

作者头像
算法之名
发布2019-08-20 10:20:18
1.5K0
发布2019-08-20 10:20:18
举报
文章被收录于专栏:算法之名算法之名

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

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

代码语言:javascript
复制
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的抽象类,我们来看一下这三个方法的实现

代码语言:javascript
复制
public void init(ServletConfig config) throws ServletException {
    //用于保存Servlet配置,Servlet在初始化时需要初始化配置信息,如名称,参数等,在处理HTTP请求时会经常用到这些配置信息
    this.config = config;
    //让子类实现的方法
    this.init();
}
代码语言:javascript
复制
public void init() throws ServletException {
}
代码语言:javascript
复制
//抽象方法,子类必须实现。不同的Servlet实现会依赖不同的协议,所以实现各不相同
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
代码语言:javascript
复制
//让子类实现的方法
public void destroy() {
}

下面是对ServletConfig的定义和获取,在HttpServletBean中将会使用到。

代码语言:javascript
复制
private transient ServletConfig config;
代码语言:javascript
复制
public ServletConfig getServletConfig() {
    return this.config;
}
代码语言:javascript
复制
//从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

代码语言:javascript
复制
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");

通用协议的service实现

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //构造一个特殊的响应类,这个类在内部忽略了所有响应体的输出
    NoBodyResponse response = new NoBodyResponse(resp);
    //调用doGet处理
    this.doGet(req, response);
    //设置响应体的字节大小
    response.setContentLength();
}
代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
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);
    }

}
代码语言:javascript
复制
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());
}
代码语言:javascript
复制
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];
}
代码语言:javascript
复制
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实现的。

代码语言:javascript
复制
//这里使用的是commons的日志框架,并不像mybatis一样进行了所有日志框架的适配
protected final Log logger = LogFactory.getLog(this.getClass());
代码语言:javascript
复制
//必须的配置属性
private final Set<String> requiredProperties = new HashSet(4);
代码语言:javascript
复制
//添加必须的配置属性
protected final void addRequiredProperty(String property) {
    this.requiredProperties.add(property);
}
代码语言:javascript
复制
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");
    }

}
代码语言:javascript
复制
@Nullable
public String getServletName() {
    //这里调用的是GenericServlet中的方法
    return this.getServletConfig() != null?this.getServletConfig().getServletName():null;
}
代码语言:javascript
复制
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, ", "));
        }
    }
}
代码语言:javascript
复制
//模板方法,给子类使用的
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
}
代码语言:javascript
复制
//模板方法,给子类使用的
protected void initServletBean() throws ServletException {
}

因为HttpServletBean也为一个抽象类,它里面设置了一些模板方法给子类去完成,这也是模板方法模式的特点。

继承于HttpServletBean的是一个抽象类FrameworkServlet,它的最主要作用就是加载一个Web应用程序环境,这是通过实现父类的模板方法initServletBean()来完成的。并且重写HttpServlet中的模板方法,派遣HTTP请求到统一的Spring Web MVC的控制器方法。

代码语言:javascript
复制
@Nullable
private WebApplicationContext webApplicationContext; //专用的Web环境,可以在构造器中赋入

我们先说一下WebApplicationContext是什么,它是一个专门为Web程序准备的上下文共享环境,它是继承于ApplicationContext,ApplicationContext是Spring的一个核心上下文,通过ApplicationContext可以获取在Spring中加载的Bean.比如在Springboot项目中

代码语言:javascript
复制
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属性列表中。

代码语言:javascript
复制
@Nullable
private String contextId; //上下文Id
代码语言:javascript
复制
private boolean refreshEventReceived = false; //用于检测是否已调用OnRefresh的标志
代码语言:javascript
复制
private boolean publishContext = true; //是否应该将上下文作为servletContext属性发布
代码语言:javascript
复制
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");
    }

}
代码语言:javascript
复制
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中的定义

代码语言:javascript
复制
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; //WebApplicationContext.ROOT

这两段代码是在WebApplicationContextUtils中的

代码语言:javascript
复制
@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
    return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
代码语言:javascript
复制
@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中

代码语言:javascript
复制
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();
}
代码语言:javascript
复制
public static String identityToString(@Nullable Object obj) {
    //如果obj非空,返回对象的类名称@对象的十六进制内存地址Hash值
    return obj == null?"":obj.getClass().getName() + "@" + getIdentityHexString(obj);
}
代码语言:javascript
复制
public static String getIdentityHexString(Object obj) {
    //以十六进制形式返回对象的内存地址Hash值
    return Integer.toHexString(System.identityHashCode(obj));
}
代码语言:javascript
复制
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":"; //前缀名WebApplicationContext:
代码语言:javascript
复制
//获取对象的字符串转换
public static String getDisplayString(@Nullable Object obj) {
    return obj == null?"":nullSafeToString(obj);
}
代码语言:javascript
复制
//由不同的对象类型,转换成不同的字符串
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:"";
    }
}
代码语言:javascript
复制
//模板方法,给子类使用
protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
}
代码语言:javascript
复制
public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
代码语言:javascript
复制
//分隔标记
private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
代码语言:javascript
复制
//资源配置列表,比如说在springboot中的property或者yml文件中配置的
private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
      new ArrayList<>();
代码语言:javascript
复制
@Nullable
private String contextInitializerClasses;
代码语言:javascript
复制
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);
   }
}
代码语言:javascript
复制
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);
   }
}
代码语言:javascript
复制
@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;
}
代码语言:javascript
复制
@Nullable
private String contextAttribute;
代码语言:javascript
复制
@Nullable
public String getContextAttribute() {
   return this.contextAttribute;
}
代码语言:javascript
复制
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
   return createWebApplicationContext((ApplicationContext) parent);
}
代码语言:javascript
复制
@Nullable
private String contextConfigLocation; //显示上下文配置位置
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
//默认XML配置的Web环境
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
代码语言:javascript
复制
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
代码语言:javascript
复制
public Class<?> getContextClass() {
   return this.contextClass;
}
代码语言:javascript
复制
//模板方法
protected void onRefresh(ApplicationContext context) {
}
代码语言:javascript
复制
public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; //FrameworkServlet.CONTEXT
代码语言:javascript
复制
public String getServletContextAttributeName() {
   //返回FrameworkServlet.CONTEXT+在Servlet配置中获取的名称
   return SERVLET_CONTEXT_PREFIX + getServletName();
}

我们再来看一下它的分发

代码语言:javascript
复制
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}
代码语言:javascript
复制
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}
代码语言:javascript
复制
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}
代码语言:javascript
复制
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}

这几种HTTP方法都调用了同一个方法processRequest

代码语言:javascript
复制
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抽象类中

代码语言:javascript
复制
//定义一个名为LocaleContext的区域信息当前线程安全引用
private static final ThreadLocal<LocaleContext> localeContextHolder = new NamedThreadLocal("LocaleContext");
代码语言:javascript
复制
//定义一个可被子线程使用的名为LocaleContext的区域信息当前线程安全引用
private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder = new NamedInheritableThreadLocal("LocaleContext");
代码语言:javascript
复制
@Nullable
public static LocaleContext getLocaleContext() {
    //获取这个区域信息
    LocaleContext localeContext = (LocaleContext)localeContextHolder.get();
    //如果该区域信息为null
    if(localeContext == null) {
        //从子线程的容器获取
        localeContext = (LocaleContext)inheritableLocaleContextHolder.get();
    }

    return localeContext;
}
代码语言:javascript
复制
@Nullable
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
   //以请求的Locale构建一个地域信息对象
   return new SimpleLocaleContext(request.getLocale());
}

在RequestContextHolder中

代码语言:javascript
复制
//定义一个名字为Request attributes的请求属性当前线程安全引用
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
//定义一个名字为Request attributes的可被子线程使用的请求属性当前线程安全引用
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
代码语言:javascript
复制
@Nullable
public static RequestAttributes getRequestAttributes() {
    //获取请求属性
    RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
    //如果该请求属性为null
    if(attributes == null) {
        //从子线程的容器获取
        attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();
    }

    return attributes;
}
代码语言:javascript
复制
@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中

代码语言:javascript
复制
public static final String WEB_ASYNC_MANAGER_ATTRIBUTE = WebAsyncManager.class.getName() + ".WEB_ASYNC_MANAGER"; //WebAsyncManager.WEB_ASYNC_MANAGER
代码语言:javascript
复制
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中

代码语言:javascript
复制
private final Map<Object, CallableProcessingInterceptor> callableInterceptors; //可回调的拦截器Map,被初始化为一个LinkedHashMap,主要是为了保证它的有序性
代码语言:javascript
复制
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);
}
代码语言:javascript
复制
private boolean threadContextInheritable = false; //是否暴露地域信息和请求属性给子线程
代码语言:javascript
复制
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);
   }
}
代码语言:javascript
复制
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();
    }

}
代码语言:javascript
复制
public static void resetLocaleContext() {
    //移除当前线程的地域信息
    localeContextHolder.remove();
    //移除当前线程的可被子线程继承使用的地域信息
    inheritableLocaleContextHolder.remove();
}
代码语言:javascript
复制
//子类必须实现的抽象方法
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
      throws Exception;
代码语言:javascript
复制
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中

代码语言:javascript
复制
protected final Map<String, Runnable> requestDestructionCallbacks = new LinkedHashMap(8); //请求销毁的回调任务映射
代码语言:javascript
复制
public void requestCompleted() {
    //执行所有的线程任务,执行完清空任务
    this.executeRequestDestructionCallbacks();
    //更新所有的会话属性到实际会话中
    this.updateAccessedSessionAttributes();
    //将请求的活动状态设置为false
    this.requestActive = false;
}
代码语言:javascript
复制
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中

代码语言:javascript
复制
private final Map<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<>(1); //需要更新的会话属性映射
代码语言:javascript
复制
@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();
   }
}
代码语言:javascript
复制
@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;
   }
}
代码语言:javascript
复制
private boolean publishEvents = true;  //是否在每个请求结束时,发布一个ServletRequest事件
代码语言:javascript
复制
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的请求处理

代码语言:javascript
复制
private boolean dispatchOptionsRequest = false; //是否将请求发送到doService
代码语言:javascript
复制
@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);
      }
   });
}
代码语言:javascript
复制
@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的内容如下

代码语言:javascript
复制
# 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的静态代码块就可以知道这一点了

代码语言:javascript
复制
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
代码语言:javascript
复制
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所需的所有组件

代码语言:javascript
复制
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);
}

对可选组件的代码以注释如下

代码语言:javascript
复制
@Nullable
private MultipartResolver multipartResolver;  //此servlet使用的多部分解析程序
代码语言:javascript
复制
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; //此命名空间的bean工厂中multipartresolver对象的已知名称
代码语言:javascript
复制
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");
      }
   }
}

对单值组件的代码及注释如下

代码语言:javascript
复制
@Nullable
private LocaleResolver localeResolver; //此servlet使用的地域请求解析器
代码语言:javascript
复制
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; //此命名空间的bean工厂中localeresolver对象的已知名称。
代码语言:javascript
复制
private static final Properties defaultStrategies; //从DispatcherServlet.properties中引入的资源,从静态代码块中引入
代码语言:javascript
复制
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 + "]");
      }
   }
}
代码语言:javascript
复制
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);
}
代码语言:javascript
复制
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<>();
   }
}
代码语言:javascript
复制
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
   //返回clazz类本身的bean实例
   return context.getAutowireCapableBeanFactory().createBean(clazz);
}

initThemeResolver和initRequestToViewNameTranslator同样初始化单值组件,与initLocaleResolver具有相同的实现。

初始化多值组件的代码及注释如下

代码语言:javascript
复制
@Nullable
private List<HandlerMapping> handlerMappings; //此servlet使用的处理器映射列表
代码语言:javascript
复制
private boolean detectAllHandlerMappings = true; //自动检测处理器映射列表
代码语言:javascript
复制
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
代码语言:javascript
复制
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中

代码语言:javascript
复制
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在派遣之前保存了请求的属性信息,在完成服务后恢复了这些信息。

代码语言:javascript
复制
private boolean cleanupAfterInclude = true; //在包含请求之后执行请求属性的清除?
代码语言:javascript
复制
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet"; //DispatcherServlet默认策略属性的通用前缀。
代码语言:javascript
复制
//请求属性以保存当前Web应用程序上下文。
//否则,只能通过标签等获取全局Web应用程序上下文。
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; //DispatcherServlet.CONTEXT
代码语言:javascript
复制
//请求属性以保存当前的地域解析器,可由视图检索。
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; //DispatcherServlet.LOCALE_RESOLVER
代码语言:javascript
复制
//请求属性以保存当前的主题解析器,可由视图检索。
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; //DispatcherServlet.THEME_RESOLVER
代码语言:javascript
复制
//请求属性以保存当前主题源,可由视图检索。
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; //DispatcherServlet.THEME_SOURCE
代码语言:javascript
复制
//保存只读的请求属性的名称。
//如果有重定向属性,则由以前的请求保存。
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; //DispatcherServlet.INPUT_FLASH_MAP
代码语言:javascript
复制
//保存“output"flashmap的请求属性的名称
//为后续请求保存的属性。
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; //DispatcherServlet.OUTPUT_FLASH_MAP
代码语言:javascript
复制
//保存flashmapmanager的请求属性的名称。
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; //DispatcherServlet.FLASH_MAP_MANAGER
代码语言:javascript
复制
@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);
         }
      }
   }
}
代码语言:javascript
复制
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);
         }
      }
   }
}
代码语言:javascript
复制
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中

代码语言:javascript
复制
public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
代码语言:javascript
复制
public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
代码语言:javascript
复制
public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri";
代码语言:javascript
复制
public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";
代码语言:javascript
复制
public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
代码语言:javascript
复制
public static boolean isIncludeRequest(ServletRequest request) {
   return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null);
}
代码语言:javascript
复制
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);
      }
   }
}
代码语言:javascript
复制
@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中

代码语言:javascript
复制
private static final Object DEFAULT_FLASH_MAPS_MUTEX = new Object();
代码语言:javascript
复制
@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;
}
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
@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;
}
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
private MultiValueMap<String, String> getOriginatingRequestParams(HttpServletRequest request) {
   //获取请求的查询字符串
   String query = getUrlPathHelper().getOriginatingQueryString(request);
   //将请求参数以键值(值为List)对形式存储
   return ServletUriComponentsBuilder.fromPath("/").query(query).build().getQueryParams();
}
代码语言:javascript
复制
@Nullable
protected Object getFlashMapsMutex(HttpServletRequest request) {
   return DEFAULT_FLASH_MAPS_MUTEX;
}

在SessionFlashMapManager中

代码语言:javascript
复制
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS"; //SessionFlashMapManager.FLASH_MAPS
代码语言:javascript
复制
@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);
}
代码语言:javascript
复制
@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中

代码语言:javascript
复制
private long expirationTime = -1; //到期时间
代码语言:javascript
复制
@Nullable
private String targetRequestPath; //目标请求地址
代码语言:javascript
复制
private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<>(4); //目标请求参数map
代码语言:javascript
复制
public boolean isExpired() {
   return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime);
}
代码语言:javascript
复制
@Nullable
public String getTargetRequestPath() {
   return this.targetRequestPath;
}
代码语言:javascript
复制
public MultiValueMap<String, String> getTargetRequestParams() {
   return this.targetRequestParams;
}

在UrlPathHelper中

代码语言:javascript
复制
private static final String WEBSPHERE_URI_ATTRIBUTE = "com.ibm.websphere.servlet.uri_non_decoded"; //特殊URI属性
代码语言:javascript
复制
private boolean removeSemicolonContent = true; //移除分号标志
代码语言:javascript
复制
private boolean urlDecode = true; //url译码
代码语言:javascript
复制
private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
代码语言:javascript
复制
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);
}
代码语言:javascript
复制
private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
   //从uri中移除所有的分号
   uri = removeSemicolonContent(uri);
   //对uri以请求的字符集编码进行转码
   uri = decodeRequestString(request, uri);
   //清除双斜杠
   uri = getSanitizedPath(uri);
   return uri;
}
代码语言:javascript
复制
public String removeSemicolonContent(String requestUri) {
   return (this.removeSemicolonContent ?
         removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri));
}
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
public String decodeRequestString(HttpServletRequest request, String source) {
   if (this.urlDecode) {
      return decodeInternal(request, source);
   }
   return source;
}
代码语言:javascript
复制
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);
   }
}
代码语言:javascript
复制
protected String determineEncoding(HttpServletRequest request) {
   //得到请求的字符集编码
   String enc = request.getCharacterEncoding();
   if (enc == null) {
      //如果拿不到请求的字符集编码,则默认8859-1编码
      enc = getDefaultEncoding();
   }
   return enc;
}
代码语言:javascript
复制
protected String getDefaultEncoding() {
   return this.defaultEncoding;
}
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
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中

代码语言:javascript
复制
public static String decode(String source, String encoding) {
   return StringUtils.uriDecode(source, Charset.forName(encoding));
}

在StringUtils中

代码语言:javascript
复制
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中

代码语言:javascript
复制
private CompositePathComponentBuilder pathBuilder;
代码语言:javascript
复制
private final LinkedList<PathComponentBuilder> builders = new LinkedList<>();
代码语言:javascript
复制
@Nullable
private String ssp;
代码语言:javascript
复制
private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");
代码语言:javascript
复制
public static UriComponentsBuilder fromPath(String path) {
   UriComponentsBuilder builder = new UriComponentsBuilder();
   builder.path(path);
   return builder;
}
代码语言:javascript
复制
@Override
public UriComponentsBuilder path(String path) {
   //将path添加到全路径后,以/分隔
   this.pathBuilder.addPath(path);
   resetSchemeSpecificPart();
   return this;
}
代码语言:javascript
复制
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);
   }
}
代码语言:javascript
复制
@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;
}
代码语言:javascript
复制
private void resetSchemeSpecificPart() {
   this.ssp = null;
}
代码语言:javascript
复制
@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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档