文章目录
源码解析
DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = null;
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
AbstractHandlerMethodAdapter#handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
InvocableHandlerMethod#invokeForRequest
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);
}
HandlerMethodReturnValueHandlerComposite#handleReturnValue
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);
}
//ViewNameMethodReturnValueHandler#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
}
//ViewNameMethodReturnValueHandler#isRedirectViewName
protected boolean isRedirectViewName(String viewName) {
return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
}
RequestMappingHandlerAdapter#getModelAndView
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
return mav;
}
DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
view.render(mv.getModelInternal(), request, response);
}
DispatcherServlet#resolveViewName
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;
}
有如下5种视图解析器:
ContentNegotiatingViewResolver
BeanNameViewResolver
ThymeleafViewResolver
ViewResolverComposite
InternalResourceViewResolver。
且ContentNegotiatingViewResolver包含了其他4种视图解析器。
视图解析器解析得到视图对象。
ContentNegotiatingViewResolver#resolveViewName
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
}
- ContentNegotiatingViewResolver#getCandidateViews
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
throws Exception {
List<View> candidateViews = new ArrayList<>();
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
}
}
return candidateViews;
}
ContentNegotiatingViewResolver有如下4种视图解析器:
BeanNameViewResolver
ThymeleafViewResolver
ViewResolverComposite
InternalResourceViewResolver。
对于视图名"redirect:/main.html"
,
视图名经ThymeleafViewResolver解析,得到RedirectView类的视图对象:RedirectView@7647;
视图名经InternalResourceViewResolver解析,得到RedirectView类的视图对象:RedirectView@7888。
即,得到以上两个候选的视图对象。
对于视图名"table"
,
视图名经ThymeleafViewResolver解析,得到Thymeleaf类的视图对象:ThymeleafView@8487。
视图名经InternalResourceViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@8501。
即,得到以上两个候选的视图对象。
对于视图名"forward:/table"
,
视图名经ThymeleafViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@7111。
视图名经InternalResourceViewResolver解析,得到InternalResourceView类的视图对象:InternalResourceView@7132。
即,得到以上两个候选的视图对象。
- ContentNegotiatingViewResolver#getBestView
private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) {
for (View candidateView : candidateViews) {
if (candidateView instanceof SmartView) {
SmartView smartView = (SmartView) candidateView;
if (smartView.isRedirectView()) {
return candidateView;
}
}
}
}
getBestView,得到最佳的视图对象。
对"redirect:/main.html"
,得到的最佳视图对象是:RedirectView@7647。
对于"table"
,得到的最佳视图对象是:ThymeleafView@8487。
对于"forward:/table"
,得到的最佳视图对象是:InternalResourceView@7111。
AbstractView#render
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
RedirectView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String targetUrl = createTargetUrl(model, request);
targetUrl = updateTargetUrl(targetUrl, model, request, response);
// Save flash attributes
RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);
// Redirect
sendRedirect(request, response, targetUrl, this.http10Compatible);
}
//RedirectView#sendRedirect
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
String targetUrl, boolean http10Compatible) throws IOException {
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
if (http10Compatible) {
response.sendRedirect(encodedURL);
}
}
小结
控制器方法返回的视图名称,
- 如果是普通字符串,没有前缀,如
"table"
,经视图解析器解析,将返回一个Thymeleaf类型的视图对象,即Thymeleaf视图。 - 如果字符串前缀是
"forward:/xxx"
,如"forward:/table"
,经视图解析器解析,将返回一个InternalResourceView类型的视图对象,即InternalResourceView视图(也称 转发视图)。 - 如果字符串前缀是
"redirect:/xxx"
,如"redirect:/main.html"
,将返回一个RedirectView类型的视图对象,即RedirectView视图(也称 重定向视图)。
上述所涉内容,文章SpringMVC视图也有过介绍。
本篇源码调试内容基于文章Thymeleaf应用实例。