<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>springmvc</display-name>
<!-- ContextLoaderListener读取
声明应用范围(整个WEB项目)内的上下文初始化参数。-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<!-- 注册spring-mvc核心控制器
默认/WEB-INF/DispatcherServlet-servlet(控制器名字-servlet).xml下寻找配置的spring文件
contextConfigLocation:通知DispatcherServlet去指定的目录加载spring-mvc.xml -->
<servlet>
<servlet-name>web-servlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:web-servlet.xml</param-value>
</init-param>
<!-- 4.服务器启动后立即加载SpringMVC配置文件 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
*.html:过滤所有html
/:所有访问的地址都由dispatchServlet进行解析,对于静态配置文件(js,txt)需要配置不让dispatchServlet进行解析,
使用此种方式可以实现RESTful风格的url
/*:最终要转发到一个jsp页面时,任然会有dispatchServlet解析jsp地址,不能根据jsp找到handler,报错
-->
web-servlet
*.html
org.springframework.web.context.ContextLoaderListener
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/userlist.html">userController</prop>
</props>
</property>
</bean>
<bean id="userController" name="/userlist.html" class="org.example.mvc.UserController"></bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd ">
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
package org.example.mvc;
import org.example.jdbc.User;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.LastModified;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class UserController extends AbstractController implements LastModified {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
String test = (String) getServletContext().getAttribute("test");
System.out.println(test);
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "name1", "1"));
userList.add(new User(2L, "name2", "2"));
return new ModelAndView("userlist", "users", userList);
}
private long lastModified;
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//点击后再次请求页面
/*response.getWriter().write("");
return null;*/
String test = (String) getServletContext().getAttribute("test");
System.out.println(test);
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "name1", "1"));
userList.add(new User(2L, "name2", "2"));
return new ModelAndView("userlist", "users", userList);
}
@Override
public long getLastModified(HttpServletRequest httpServletRequest) {
if (lastModified == 0L) {
//第一次或处理逻辑更新时,重新返回上次修改值
lastModified = System.currentTimeMillis();
}
//return lastModified;
return -1;
}
}
package org.example.jdbc;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@AllArgsConstructor
public class User {
private Long id;
private String name;
private String passwd;
}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>Hello World!</h2>
<c:forEach items="${users}" var="user">
<c:out value="${user.name}"/>
<c:out value="${user.passwd}"/>
</c:forEach>
</body>
</html>
监听器加载application.xml配置文件,生成bean
接着注入拦截器,加载web-servlet.xml,这个配置文件主要配置一些跟web相关的对象,当然也可以放在application.xml里面加载
spring这里用的是servlet拦截,拦截的我这里设置的是*.html结尾的地址
拦截了之后,会查找访问的地址,主要在这儿配置
访问这个Controller后,返回页面,视图是userList
接着交给配置文件用来做一些收尾的工作,比如添加前后缀
接着就访问userList.jsp了,这里主要做的是伪静态处理.因为后缀是html,所以用户访问的时候就不知道是具体用那种语言解析的了,其实在访问的时候还有2种技术能够起到混淆的左右,第一种是urlwrite,第二种是短网址.有空我写出来.
首先查看监听器ContextLoaderListener,监听器继承了ServletContextListener,可以在这个监听器中存放一些tomcat启动时就要完成的代码,主要提供2个方法,分别开启和关闭的时候执行.
@Override
// 生之后调用
public void contextInitialized(ServletContextEvent event) {
//初始化WebAPPlicationContext,也就是spring容器,对web的支持
initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
@Override
// 死之前调用
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
/*
重复创建上下文检测
创建上下文
设置父上下文,如果非活动且没刷新下
配置刷新上下文
存储域值对象中
设置2个副本
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//重复创建上下文,异常
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
//在ServletContext关闭时依旧可用,因为本类存了副本
if (this.context == null) {
//初始化WebApplicationContext
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//是否活动状态
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
//不是活动状态,也没有刷新,设置父上下文
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//配置刷新上下文
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//存储域值对象中,代表整个web应用对象,贯穿整个tomcat生命周期
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
//确定同一类加载器,则赋值
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
//存储类加载器=>上下文
currentContextPerThread.put(ccl, this.context);
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
/*
域值对象获取application上下文类名
如果没有的话,从配置文件获取
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//配置文件获取类全限命名
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
//加载ContextLoader.properties获取spring容器的全限命名
//因为没有设置路径,所以就在当前路径
//可看源码下没有,jar包当前类目录下有,原因是因为打jar的时候,默认把resources目录下的文件和main目录合并了
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
//加载
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
/*
上下文构造id
设置上下文属性,如阈值对象,加载文件路径
替换占位符
对上下文进行一些扩展操作
刷新上下文
*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
//构造默认id
//value=org.springframework.web.context.WebApplicationContext:/tomcat设置的项目名
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//域值对象设置spring容器中
wac.setServletContext(sc);
//获取xml配置的路径,classpath:springmvc.xml
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
//替换占位符
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
//对上下文进行一些扩展操作,在刷新之前
customizeContext(sc, wac);
//刷新上下文
wac.refresh();
}
servlet生命周期由容器控制
初始化
servlet容器加载servlet.class,读到内存中
容器创建servletConfig,里面包含servlet的初始化配置信息
容器创建servlet对象,并调用init()初始化
运行
容器接收请求时,分别创建request和response对象,并传递给service()
service()通过servletRequest获得请求,处理,并通过servletResponse对象生成响应结果
最终销毁servletRequest和servletResponse,不管请求是get/post,最终都由service()处理
销毁
web应用终止,首先调用servlet销毁方法,销毁servletConfig
/*
解析配置文件init-param
将servlet封装BeanWrapper,从而能spring管理
设置自定义属性编辑器
子类覆盖
属性注入
子类覆盖
*/
public final void init() throws ServletException {
// Set bean properties from init parameters.
//解析init-param并封装,PropertyValues支持的所有属性都可以web.xml配置
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//将servlet封装BeanWrapper,从而能spring管理
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
//注册自定义属性编辑器,遇到Resource使用ResourceEditor解析
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//留给子类覆盖
initBeanWrapper(bw);
//属性注入
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
//子类覆盖
initServletBean();
}
/*
对init-param封装以及验证
*/
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
//验证那些必须属性
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<>(requiredProperties) : null);
//获取init-param
Enumeration<String> paramNames = config.getInitParameterNames();
while (paramNames.hasMoreElements()) {
String property = paramNames.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
// Fail if we are still missing properties.
//missingProps依旧还有必须存在的值,异常
if (!CollectionUtils.isEmpty(missingProps)) {
throw new ServletException(
"Initialization from ServletConfig for servlet '" + config.getServletName() +
"' failed; the following required properties were missing: " +
StringUtils.collectionToDelimitedString(missingProps, ", "));
}
}
/*
初始化容器
留给子类扩展
日志打印加载时间
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化web容器
this.webApplicationContext = initWebApplicationContext();
//子类覆盖
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
/* 对webApplicationContext进行设置和刷新,如果构造时注入的话
从域值对象获取spring容器,如果没有的话
创建新容器,如果没有
刷新
添加容器到域值对象*/
protected WebApplicationContext initWebApplicationContext() {
//spring非常喜欢封装一个工具类,来用来对某个对象的获取
//在个方法里面看到了主要对异常的处理.get到了
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//此类继承了ApplicationContextAware,所以会构造注入webApplicationContext
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
//非活动且无父,则加载父
cwac.setParent(rootContext);
}
//配置和刷新
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
//根据contextAttribute获取spring容器,web.xml配置
//域值对象获取spring容器
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
//创建新的容器,并以rootContext当parent
//在这里执行了onRefresh(wac),主要是通过监听器执行的
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
//刷新,主要注册各种解析器,比如视图,url映射子类的
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
//添加容器到域值对象
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
/*
获取contextClass,以此为创建spring容器依据
配置环境变量,parent,location(xml配置)
配置和刷新
*/
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
//获取servlet初始化参数contextClass,默认XmlWebApplicationContext
Class<?> contextClass = getContextClass();
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");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//设置环境变量
wac.setEnvironment(getEnvironment());
//设置listen初始化的parent
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
//配置location,xml配置
wac.setConfigLocation(configLocation);
}
//配置和刷新
configureAndRefreshWebApplicationContext(wac);
return wac;
}
/*
spring容器设置属性
扩展
刷新
*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//设置容器全局唯一标识
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
//设置域值对象
wac.setServletContext(getServletContext());
//设置ServletConfig
wac.setServletConfig(getServletConfig());
//设置命名空间
wac.setNamespace(getNamespace());
//设置监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
//环境变量替换
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//子类覆盖
postProcessWebApplicationContext(wac);
//对上下文进行一些扩展操作,在刷新之前
applyInitializers(wac);
//刷新
wac.refresh();
}
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//处理文件上传,如果想使用multipart,则需要添加对应解析器
initMultipartResolver(context);
//国际化,分别支持url,session,cookie三种配置
initLocaleResolver(context);
//theme主题控制页面风格,
initThemeResolver(context);
//处理映射器:Controller映射处理,可设置多个,分别根据优先级处理
initHandlerMappings(context);
//适配器,通过处理映射器得到处理,轮询适配器,查找能够处理当前HTTP请求的,从而适配当前HTTP请求
initHandlerAdapters(context);
//异常处理器,返回一个ModelAndView
initHandlerExceptionResolvers(context);
//当Controller没有返回视图,response没有流,则通过实现了此接口的类来确定此种情况下返回viewName的约定
//支持前后缀等
initRequestToViewNameTranslator(context);
//选择view渲染
initViewResolvers(context);
//请求存储
//场景,重定向的时候,定向后依旧能使用定向前数据
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//可在web.xml设置detectAllHandlerMappings=false,
//会查找默认名handlerMapping处理
//如果没找到,则加载默认配置文件
//DispatcherService.properties加载(没自定义Strategies的情况下)
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
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.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
get/post以及其他请求都走processRequest方法,统一处理
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
*
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/*
获取当前线程localeContext,requestAttributes
注册回调拦截器
绑定当前线程localeContext,requestAttributes
执行
恢复当前线程localeContext,requestAttributes
发布监听器
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取当前线程localeContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
//获取当前线程requestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
//注册回调拦截器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//绑定当前线程
initContextHolders(request, localeContext, requestAttributes);
try {
//执行
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);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
//发布监听器
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
/*
设置相关属性到request
设置请求缓存
执行
*/
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
//设置相关属性,THEME,CONTEXT
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
//请求缓存
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
//执行
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);
}
}
}
}
/*
Multipart类型处理
获取处理映射器,内部封装Controller
获取适配器
执行last-modified头,如果适配器支持
请求前调用拦截器
适配器调用Controller,返回视图
设置视图默认名称
请求后调用拦截器
视图跳转,以及拦截器请求完执行
清除MultipartHttpServletRequest
*/
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 {
//如果是Multipart类型的request,转成MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//获取handlerMappings
//获取处理映射器,内部封装Controller
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//没找到,response直接返回
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//获取适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
//执行last-modified头,如果适配器支持
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
//增加对lastModified的支持,需要controller继承LastModified
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//请求前调用拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//适配器调用Controller,返回视图
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.
//清除MultipartHttpServletRequest
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
/*
获取Controller
加入拦截器到执行链,以便更好对controller处理
*/
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//获取request对应的Controller,封装成HandlerExecutionChain
Object handler = getHandlerInternal(request);
if (handler == null) {
//使用默认handler
handler = getDefaultHandler();
}
//如果依旧没有,返回null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
//如果是handler名字,则容器获取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//加入拦截器到执行链,以便更好对controller处理
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
/*
handler根据路径处理获取Contorller
*/
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//截取用于匹配的url有效路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根据路径寻找handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
//如果路径是/,使用rootHandler处理
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
//使用默认handler
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
//根据beanName获取对应bean
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
//模板方法
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
/*
通过路径匹配或通配符匹配返回Contorller
*/
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
//直接匹配路径获取Controller
/*
userController
*/
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//子类实现
validateHandler(handler, request);
//构造HandlerExecutionChain,封装controller
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
//通配符匹配handler
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
//将handler封装成HandlerExecutionChain
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
/*
构造HandlerExecutionChain,封装controller
以及2个拦截器
*/
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
//适配器是否支持
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
使用默认SimpleControllerHandlerAdapter
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
获取所有继承了HandlerInterceptor的类,内部分别有三个方法,分别是请求前,请求后,执行完。
在DispatcherServlet#doDispatch分别执行三次
请求前调用
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//调用拦截器
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
@Nullable
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
//对options请求方式处理
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}
// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
//如果需要session内的同步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
//调用用户逻辑
return handleRequestInternal(request, response);
}
/*
异常处理
页面处理
执行拦截器执行完视图方法
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//异常处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//对异常处理返回视图
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
//页面处理
if (mv != null && !mv.wasCleared()) {
//处理页面跳转
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
//请求结束调用拦截器
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
//执行异常解析器并返回视图
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
/*
解析视图
跳转
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
//调用视图解析器,解析名称
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//页面跳转
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
/*
获取视图
*/
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
//缓存没,直接创建视图
return createView(viewName, locale);
}
else {
//缓存获取
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache");
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
//子类实现,主要添加前后缀之内的
return loadView(viewName, locale);
}
/*
对redirect,forward前缀支持
添加url前后缀
生成view,并添加必要属性
*/
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
//如果当前解析器不支持
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
//处理前缀为redirect:情况
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl,
isRedirectContextRelative(), isRedirectHttp10Compatible());
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// Check for special "forward:" prefix.
//处理前缀为forward:
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
InternalResourceView view = new InternalResourceView(forwardUrl);
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
}
// Else fall back to superclass implementation: calling loadView.
//本类实现方法,主要实现了url前后缀添加以及其他属性
return super.createView(viewName, locale);
}
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
//子类实现,主要添加前后缀之内的
return loadView(viewName, locale);
}
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
//设置前后缀
AbstractUrlBasedView view = buildView(viewName);
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
/*
封装数据
子类实现扩展
处理页面跳转
*/
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
//封装页面所需数据
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//子类实现扩展
prepareResponse(request, response);
//处理页面跳转
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
/*
执行转发或包含
*/
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
//model存request
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
//确定路径,避免浏览器路径和返回路径一致
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
//如果已经包含或响应已经提交,则执行include,否则转发。
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
//转发
rd.forward(request, response);
}
}