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的组件按照数量来划分,可分为可选组件,单值组件和多值组件
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;
}