Spring mvc源码解析
页面请求路径分析:
SpringMVC处理请求:
DispatchServlet ----> 接受请求 ---> path,request parameters ---> path --> @RequestMapping method --->解析参数,参数绑定 --->执行方法--->返回值解析
bean对象(实现Controller接口的对象)
请求步骤:
- 启动Tomcat
创建Tomcat容器,占用8080端口启动Tomcat中spring_mvc_helloworld应用。
- 解析web.xml文件
解析web.xml文件中内容,创建相应对象实例和其他配置功能
核心就是创建DispatchServlet
- 创建DispatchServlet对象
DispatchServlet处理请求核心代码:
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 {
//检查是否是上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取HandlerMapping
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据HandlerMapping获取HandlerAdapter(适配器模式)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获得方法类型
String method = request.getMethod();
// 判断是否是get请求
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//判断是否执行Handler前置处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 执行方法,并返回视图和数据 ---核心处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//完成默认视图名
applyDefaultViewName(processedRequest, mv);
//完成Handler的后置处理
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);
}
}
}
}
3.1、初始化DispatchServlet
调用DispatchServlet.init()初始化方法,如果没有定义init方法,会自动调用父类HttpServletBean中的init()方法初始化
DispatchServlet初始化方法在父类中已经重写
DispatchServlet.init() —> 创建Spring容器
—> initStrategies() —>initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);例:
initHandlerMappings(context);
—> 创建RequestMapingHadlerMapping —> afterPropertiesSet()
—> 创建RequestMapingHadlerAdapter ---->afterPropertiesSet()
—> 创建BeanNameUrlHandlerMapping —> detectHandler()
—> 创建RouterFunctionMapping —> afterPropertiesSet()
3.2、init()初始化方法
DispatchServlet的父类FarmeworkServlet中有属性WebApplicationContext,就是DispatchServlet内部的IOC容器,
在FarmeworkServlet进行IOC容器创建。
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
//创建哪类IOC容器,默认是XmlWebApplicationContext类型
Class<?> contextClass = this.getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//创建XmlWebApplicationContext对象实例
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent); //父容器
// this.getContextConfigLocation();获取到web.xml文件中配置的init-param的contextConfigLocation属性
String configLocation = this.getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//创建和刷新IOC容器,就会根据配置文件扫描组件,将组件注入IOC
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
3.3、Hadler:处理器 -->定义处理此url请求的处理器
SpringMVC中一共提供了三种Hadler:
1.@RequestMapping注解:标注了此注解,请求的路径映射到标注的方法或类上就是一个Handler,可以进行方法调用。(常用)
2.实现Controller接口的bean对象,就成为一个Hadler,实现接口中handleRequest方法,进行逻辑处理。此时这个类需要放到IOC容器中,并标明请求路径.例:@Component(“/hello”).
3.实现HttpRequestHandler接口的bean对象,就成为一个Hadler,原理与2类似。
4.RouterFunction对象
3.4 HandlerMapping:处理器映射器–>将url与方法进行映射
HandlerMapping初始化:将标注有@Controller和@RequestMapping注解的方法或类,还有实现Controller接口,实现HttpRequestHandler接口,RouterFunctiond对象的Handler进行请求与方法映射
在FrameworkServlet中有SpringIOC容器监听器ContextRefreshListener,监听SpringIOC容器创建,创建完毕后,DispatchServlet会调用onRefresh方法,此时就会再进行一些初始化。
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
SpringMVC根据Hadler类型提供了三种HandlerMapping,分别用于不同Hadler的映射关系:
1.RequestMappingHandlerMapping
—>最终将RequestMapping注解与方法映射关系存入Map—>Map<RequestInfo,HandleMethod>
2.BeanNameUrlHandlerMapping -->因为实现Controller和HttpRequestHandler两个HadlerMapping都是创建了类似的bean(样
式:@Component(“/hello”)),所以两种使用一个BeanNameUrlHandlerMapping 进行bean的查找。原理:寻找哪个bean的名称是以/开头,找到作为Handler,存入Map—》Map<path,bean对象>
3.RouterFunctionMapping
以RequestMappingHandlerMapping 源码为例,DispachServlet的initStrategies方法的initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//根据类型判断是否有自定义的HadlerMapping
if (this.detectAllHandlerMappings) {
//创建一个存放HadlerMapping的Map,此时Spring提供的HadlerMapping并没有放入Map中
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
//根据名字判断是否有自定义的HadlerMapping
} else {
try {
//将自定义的HadlerMapping放入其中
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var4) {
}
}
//如果没有自定义的HadlerMapping,就使用Spring默认提供的三种HadlerMapping
if (this.handlerMappings == null) {
//取默认的handlerMappings,在DispatchServlet.properties配置文件中有默认配置,进行读取.
//将读取的默认类创建为Bean对象,放入IOC容器中
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
}
}
Iterator var6 = this.handlerMappings.iterator();
while(var6.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var6.next();
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
RequestMappingHandlerMapping–>AbstractHandlerMethodMapping:查找标注RequestMapping或controller注解的Handler
protected void initHandlerMethods() {
//获取Spring容器中所有bean的名字
String[] var1 = this.getCandidateBeanNames();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
if (!beanName.startsWith("scopedTarget.")) {
//判断bean的类型
this.processCandidateBean(beanName);
}
}
this.handlerMethodsInitialized(this.getHandlerMethods());
}
//判断bean类型
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//根据bean的名字获取到类型
beanType = this.obtainApplicationContext().getType(beanName);
} catch (Throwable var4) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
}
}
//this.isHandler(beanType)判断这个类是否标注了Controller注解或RequestMapping注解
if (beanType != null && this.isHandler(beanType)) {
this.detectHandlerMethods(beanName); //根据bean对象名字查找处理方法
}
}
}
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
最终的到所有标有@RequestMapping注解的方法,存入到一个Map中,这个map以path为key,以method为value进行存储
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
//获取bean的类型
Class<?> userType = ClassUtils.getUserClass(handlerType);
//循环所有方法<Method,@RequestMapping注解信息>,并判断是否有@Request注解,将RequestMapping注解上的信息查找后封装为RequestMappingInfo对象
//Map<Method, T>中key存放的是标注了@RequestMapping注解的方法,value存放的是@RequestMapping的信息RequestMappingInfo对象
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
}
//遍历@RequestMapping方法,转换为指定格式的Map注册到mappingRegistry中,mappingRegistry此时还是Map<Path,HandlerMapping>
//经过注册,最后生成指定格式的map,Map<RequestMappingInfo,Method>-->最终存入AbstractHandlerMethodMapping的mappingRegistry中
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
初始化完成后,生成方法或类与路径或@RequestMapping注解映射的Map,根据请求从Map中获取对应HandlerMapping:
DispatchServlet.doDispatch() —>getHandler()
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;
}
mapping.getHandler(request)—>getHandlerInternal(request)—>lookupHandlerMethod(lookupPath, request)—>List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
根据url中的请求路径从Map,this.mappingRegistry中匹配路径key,得到Value就是对应的加了@RequestMapping注解的RequestMappingInfo信息,最终得到Handler
根据url找到具体handle方法
AbstractHandlerMethodMapping类中的lookupHandlerMethod方法
//从已经存有的mappingRegistry的Map对象中匹配路径,查找对应的方法
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
- SpringMVC父子容器(!不常用)
当一个Tomcat程序有多个DispatchServlet时,每个DispatchServlet都有一个Spring容器,每个容器中的bean不相同,如果想要多个DispatchServlet容器共用一个bean对象,就需要创建一个父容器来实现不同DispatchServlet公用一个Bean。
创建父容器在创建DispatchServlet之前!!!!
如何创建父容器?
在web.xml文件中
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-parent.xml</param-value> <!--父容器配置文件-->
</context-param>
在Tomcat初始化时,通过这个监听器,会根据配置先创建父容器,然后再创建其他子容器
创建完父容器会将容器存入ServletContext(应用上下文)中,创建完成各个DispatchServlet容器后,在FrameworkServlet类中initWebApplicationContext()设置父子容器关系
protected WebApplicationContext initWebApplicationContext() {
//获取到父容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (wac == null) {
//创建子容器时,将父容器传入,设置父子容器
wac = this.createWebApplicationContext(rootContext);
}
return wac;
}
-
TomCat启动完成
-
DispatchServlet处理请求
HandlerAdapter:处理器适配器(适配器模式)
SpringMvc根据HandlerMapping创建了多种相对应的HandlerAdapter
1.标注的@RequesMapping -----> RequestMappingHandlerAdapter
2.HttpRequestHandler ----->HttpRequestHandlerAdapter
3.实现Controller接口对象 ----->SimpleControllerHandlerAdapter
4.HandlerFunction对象 ----->HandlerFunctionAdapter
3和4都是实现接口,然后返回对象,放入Spring容器中,处理请求时,使用这两种方法的都会使用SimpleControllerHandlerAdapter适配器。
传入不同的HandlerMapping得到不同HandlerAdapter,执行各自handle方法,来执行HandlerMapping中的Map的对应的方法.
DispatchServlet.getHandlerAdapter()–
这里以有@RequestMapping注解的Handler处理器为例,根据Handler处理器找到相应Adapter适配器(RequestMappingHandlerAdapter)—
DispatchServlet中:
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");
}
6.参数解析流程
请求到达之前的初始化
1、初始化数据/参数绑定器
@InitBinder
@InitBinder从字面意思可以看出这个的作用是给Binder做初始化的,@InitBinder主要用在@Controller中标注于方法上(@RestController也算),表示初始化当前控制器的数据绑定器(或者属性绑定器),只对当前的Controller有效。@InitBinder标注的方法必须有一个参数WebDataBinder。所谓的属性编辑器可以理解就是帮助我们完成参数绑定,然后是在请求到达controller要执行方法前执行
用法:
@InitBinder
private void initBinder(WebDataBinder binder) {
// 可用于自定义参数校验,然后通过addValidators来进行绑定controller
binder.addValidators(userValidator);
// 可用于注册 属性编译器
binder.registerCustomEditor(String.class,new StringTrimmerEditor(true));
}
WebDataBinder
WebDataBinder的作用是从Web 请求中,把请求里的参数都绑定到对应的JavaBean上!在Controller方法中的参数类型可以是基本类型,也可以是封装后的普通Java类型。若这个普通的Java类型没有声明任何注解,则意味着它的每一个属性都需要到Request中去查找对应的请求参数,而WebDataBinder则可以帮助我们实现从Request中取出请求参数并绑定到JavaBean中。
我们知道通过@InitBinder修饰的可以拿到WebDataBinder,WebDataBinder 其实已经帮我们完成了基本的参数映射,日期类型就是个特殊的。
使用get请求params传date类型,SpringMVC在默认时,是不支持这种类型转换的。此时我们就需要自定义编译器,然后通过binder.registerCustomEditor注册进去。post请求json传参默认是支持yyyy-MM-dd其他格式也会报错的!
当然除此外在日期类型字段上添加@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”) 也是可以的。
2、数据绑定器
关于Date属性绑定器有两种方案:使用spring提供的CustomDateEditor
,另外一种就是自定义PropertyEditorSuppotr
@RequestMapping("body")
@RestController
public class RequestBodyController {
@GetMapping("/test")
public Params request(Params params) {
System.out.println(params);
return params;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
// 格式化date方式一:get请求params传参必须传yyyy-MM-dd HH:mm:ss,否则400错误
// post请求json传参只能传yyyy-MM-dd,如果传其他格式,连这个方法都进不来就400异常了
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
binder.registerCustomEditor(Date.class, dateEditor);
// // 格式化date方式二,自定义PropertyEditorSupport,然后利用hutool的格式化,DateUtil.parse支持的格式有很多种,这里支持很多种是可以传入任何格式,他都会给你格式化成yyyy-MM-dd HH:mm:ss
// 日期没有时分秒的时候格式化出来的是2022-10-11 00:00:00
// 自定义的这种方式对于json传参方式没有效果,压根连方法都不会进入
// binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
// @Override
// public void setAsText(String text) {
// System.out.println("1111");
// // DateUtil.parse是hutool当中的方法,hutool是一个Java工具包
// setValue(DateUtil.parse(text));
// }
// });
// 格式化string:如果是字符串类型,就去除字符串的前后空格
binder.registerCustomEditor(String.class,
new StringTrimmerEditor(true));
}
}
3、全局数据绑定器
3.1. 方式一:@ControllerAdvice
@InitBinder方法只对当前Controller生效,要想全局生效,可以使用@ControllerAdvice。通过@ControllerAdvice可以将对于控制器的全局配置放置在同一个位置,注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler,@InitBinder,@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效
@ControllerAdvice
public class GlobalControllerAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class,
new StringTrimmerEditor(true));
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
// DateUtil.parse是hutool当中的方法
setValue(DateUtil.parse(text));
}
});
}
}
3.2. 方式二:RequestMappingHandlerAdapter
@Configuration
public class Config {
@Bean
public RequestMappingHandlerAdapter webBindingInitializer(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
requestMappingHandlerAdapter.setWebBindingInitializer(new WebBindingInitializer() {
@Override
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
// DateUtil.parse是hutool当中的方法
setValue(DateUtil.parse(text));
}
});
// 如果是字符串类型,就去除字符串的前后空格
binder.registerCustomEditor(String.class,
new StringTrimmerEditor(true));
}
});
return requestMappingHandlerAdapter;
}
}
RequestMappingHandlerAdapter初始化
@Override
public void afterPropertiesSet() { //afterPropertiesSet是InitializingBean接口方法,在Bean对象初始化时调用
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
//配置默认的参数解析器
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//配置默认的InitBinderArgumentResolvers解析器,如@InitBinder(单个handle)、@ControllerAdvice(全局handle)
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
//配置默认的返回值解析器
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
根据参数注解/类型寻找参数解析器
参数解析:以有@RequestMapping注解的HandlerAdapter(RequestMappingHandlerAdapter)为例
DispatchServlet.doDispatch–>ha.handle()–>AbstractHandlerMethodAdapter.handleInternal()–>RequestMappingHandlerAdapter.handleInternal()
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request); //检查请求
// 判断是否是同步session,默认是false
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 真正执行调用的处理请求方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
// 真正执行调用的处理请求方法
mav = invokeHandlerMethod(request, response, handlerMethod);
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//封装Servlet请求
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//根据方法请求获取数据绑定器,用于除了基本数据类型映射以外,处理日期等类型的映射
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//创建Model工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//判断初始化参数解析器是否为空
if (this.argumentResolvers != null) {
//获取参数列表中各个参数的参数解析器(默认配置了27个)
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
//获取返回值处理器(默认配置了14个)
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//设置时间处理工厂
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//创建ModelAndViewContainer,进行Model和View信息的存储
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//执行和处理方法,并将视图和数据封装进mavContainer中
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//获取ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
寻找参数解析器:
//执行上图invokeAndHandle(webRequest, mavContainer)
// -->invokeAndHandle().invokeForRequest(webRequest, mavContainer, providedArgs)
// -->invokeForRequest().getMethodArgumentValues(request, mavContainer, providedArgs)
// -->getMethodArgumentValues()
//执行完getMethodArgumentValues()得到所有参数值
getMethodArgumentValues()获取方法参数值:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取方法参数
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//创建一个参数长度的数组来存放参数解析器
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//判断是否有参数解析器可以解析这个参数 -->supports:支持
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//将参数解析器存入数组中
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
通过参数类型,注解、名字来获取参数解析器
this.resolvers.supportsParameter(parameter)方法
//将参数进行与参数解析器的适配:this.argumentResolvers默认的参数解析器类型,默认27个
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
最终找到方法中每个参数的参数解析器进行解析。
执行方法
执行完getMethodArgumentValues()得到所有参数值args后。doInvoke(args)方法执行。
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
方法执行完成:返回值处理器
- 在RequestMappingHandlerAdapter中设置了默认的返回器处理器(15个)
- 执行RequestMappingHandlerAdapter类中invokeHandlerMethod方法,会将默认参数解析器和返回值处理器添加。
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
- 然后会继续执行invokeHandlerMethod方法中的invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//处理请求,并返回返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
//判断返回值是否为空
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//寻找返回值处理器(适配)
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
- this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//将方法返回值和类型与处理器进行匹配,找到返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
- //将方法返回值和类型与处理器进行匹配,找到返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
同样使用适配模式找到返回值处理器
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
**常用的返回值处理器:**RequestResponseBodyMethodProcessor,ViewNameMethodReturnValueHandler,ModelAndViewMethodReturnValueHandler
返回值转换器
- 在RequestMappingHandlerAdapter中设置了四个默认返回值转换器 —>将返回值转为方法返回值类型
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
//添加负责读取二进制格式的数据和写出二进制格式的数据;
this.messageConverters.add(new ByteArrayHttpMessageConverter());
//添加负责读取字符串格式的数据和写出二进制格式的数据(当返回值是或者接受值是String类型时,是由这个处理)
this.messageConverters.add(new StringHttpMessageConverter());
if (!shouldIgnoreXml) {
try {
//添加负责读取和写入xml中javax.xml.transform.Source定义的数据
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
}
//添加可以读取x-www-form-urlencoded参数的实现类(表单)
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
Json格式转换器(MappingJackson2XmlHttpMessageConverter):如果使用@ResposeBody注解,将返回值转换为Json格式数据。需要加入Json的依赖包jackson-core。SpringMVC会自动将返回值转换为Json格式数据。
找到返回值处理器后,处理返回值
handleReturnValue()
--->handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); //处理返回值
handler == RequestResponseBodyMethodProcessor
这里以处理标注@ResponseBody注解的RequestResponseBodyMethodProcessor类举例。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//设置请求已经处理完成。因为标注@ResponseBody不需要视图转发,不需要其他操作,所以直接设置为True。
mavContainer.setRequestHandled(true);
//将NativeWebRequest转为容易操作的ServletServerHttpRequest
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
//写入数据处理器
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
将数据写入页面
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
//判断方法返回值是否属于String。value是方法返回值
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
//不是String返回值类型
else {
body = value;
//返回值类型
valueType = getReturnValueType(body, returnType);
//目标类型(方法的返回类型)
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
//判断是否是资源类型
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
//添加前端浏览器媒体类型
MediaType selectedMediaType = null;
//添加后端服务器媒体类型
MediaType contentType = outputMessage.getHeaders().getContentType();
//是否是内容预设
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
//得到Request对象
HttpServletRequest request = inputMessage.getServletRequest();
//创建前端浏览器可接受的媒体类型对象
List<MediaType> acceptableTypes;
try {
//获取前端浏览器传递的可接受的媒体类型
acceptableTypes = getAcceptableMediaTypes(request);
}
catch (HttpMediaTypeNotAcceptableException ex) {
int series = outputMessage.getServletResponse().getStatus() / 100;
if (body == null || series == 4 || series == 5) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring error response content (if any). " + ex);
}
return;
}
throw ex;
}
//获得后端服务器可生产的媒体类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
//添加可以使用的媒体类型
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
//根据特征排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
//找到可以使用的媒体类型后
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
//循环遍历查找哪个返回值处理器可以处理这种媒体类型
for (HttpMessageConverter<?> converter : this.messageConverters) {
//获得继承GenericHttpMessageConverter类的处理器
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
//找到返回值处理器后
if (genericConverter != null ?
//将方法返回值转换为方法返回类型,这里使用的函数式接口调用生成body
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
//判断转换后的数据是否为空
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
//将数据写回页面
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
//将数据写回页面后,返回
return;
}
}
}
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));
}
}
需要视图解析器处理请求(没有标注@ResponseBody注解的):
DispatchServlet通过适配到的HandlerAdapter执行完handle方法,返回封装好的视图和数据ModelAndView,通过后续processDispatchResult方法,处理视图,进行页面跳转和渲染.
DispatchServlet-->mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-->RequestMappingHandlerAdapter类
执行完invokeHandlerMethod().invokeAndHandle()后
返回getModelAndView(mavContainer, modelFactory, webRequest);
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//如果请求类中有@SessionAttributes注解,会将Session中数据放入mavContainer
modelFactory.updateModel(webRequest, mavContainer);
//判断请求是否处理完成了,RequestResponseBodyMethodProcessor处理时会在写页面数据之前设置为true,用来此处判断,没有视图
if (mavContainer.isRequestHandled()) {
return null;
}
//获取Model数据
ModelMap model = mavContainer.getModel();
//将视图名,Model数据封装为一个ModelAndView
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
//判断是否是视图转发
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
//判断是否是重定向
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
//将封装好的视图进行返回到DispatchServlet进行处理
return mav;
}
寻找视图解析器:DispatchServlet.resolveViewName()
找到视图解析器,SpringMVC默认设置了一个视图解析器InternalResourceViewResolver,处理JSP页面
@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) {
//找到适配的视图解析器,解析视图名找到视图,默认只有一个处理JSP页面的InternalResourceViewResolver
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
视图解析器解析视图:InternalResourceViewResolver调用父类中AbstractCachingViewResolver.resolveViewName()
public View resolveViewName(String viewName, Locale locale) throws Exception {
//查看是否有缓存
if (!isCache()) {
return createView(viewName, locale);
}
else {
//获取缓存中的View
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
//如果缓存中没有view,创建View
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
//根据视图名创建视图
view = createView(viewName, locale);
//创建失败,设置一个未解决的视图
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
//将创建的视图放入缓存中
if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
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);
}
}
创建视图:UrlBasedViewResolver
@Override
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;
}
//判断是否是重定向,如果是重定向创建RedirectView视图
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);
}
// 判断是否是转发,如果是转发创建InternalResourceView视图
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);
}
// 如果视图名前没标注,调用父类AbstractCachingViewResolver中创建视图方法
return super.createView(viewName, locale);
}
父类AbstractCachingViewResolver中创建视图方法
protected View createView(String viewName, Locale locale) throws Exception {
//加载视图,loadView是View接口中的方法
return loadView(viewName, locale);
}
子类(InternalResourceViewResolver)实现loadView接口方法:UrlBasedViewResolver
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
//构建View,处理jsp页面跳转到InternalResourceViewResolver中的buildView方法,创建
AbstractUrlBasedView view = buildView(viewName);
View result = applyLifecycleMethods(viewName, view);
//返回View
return (view.checkResource(locale) ? result : null);
}
创建处理JSP的View:InternalResourceViewResolver.buildView —>InternalResourceView
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//创建处理JSP的InternalResourceView
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
if (this.alwaysInclude != null) {
view.setAlwaysInclude(this.alwaysInclude);
}
view.setPreventDispatchLoop(true);
//返回视图
return view;
}
将视图返回到DispatchServlet后进行结果处理
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);
}
}
//判断视图是否为空
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) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
视图渲染
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) {
// 解析视图名
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) {
request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
response.setStatus(mv.getStatus().value());
}
//渲染当前View页面
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
渲染视图:DispatchServlet.render() —>view.render() —>Abstract.renderMergedOutputModel()—>InternalResourceView.renderMergedOutputModel()
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//将模型对象公开为请求属性
exposeModelAsRequestAttributes(model, request);
// 如果有的话,将帮助程序公开为请求属性。
exposeHelpers(request);
// 确定请求调度程序的路径。
String dispatcherPath = prepareForRendering(request, response);
// 获取目标资源(通常是JSP)的RequestDispatcher
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!");
}
//得到路径和RequestDispatcher请求后,就不是SpringMvc中代码,使用的Tomcat中Servlet中代码进行跳转
// 如果已经包含或响应已经提交,则执行include,否则执行forward。
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// 注意:被转发的资源应该决定内容类型本身
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
rd.forward(request, response);
}
}
处理请求完成