Springmvc ViewResolver設(shè)計實現(xiàn)過程解析
總結(jié):
ViewResolver 如果要改需要自己注入到容器中并進行修改, springmvc使用的是InterResourceViewResover
view不需要自己改,是springmvc根據(jù)return返回值選的
既然看到有ModelAndView直接跳轉(zhuǎn)jsp的, 有請求轉(zhuǎn)發(fā)的,有重定向的,這里整體是怎么設(shè)計的: (@ResponseBody的在此不作展開)
HiController:
@Controller
public class HiController {
@RequestMapping("/hi")
public ModelAndView getHi() {
ModelAndView mav = new ModelAndView("me");
return mav;
}
@RequestMapping("/yes")
public String forwardYes() {
return "forward:patch";
}
@RequestMapping("/no")
public String RedirectNo() {
return "redirect:patch";
}
@ResponseBody
@RequestMapping("/patch")
public String redirectNo() {
return "from forward or redirect request"; // 這種情況沒有view,在這里不討論
}
}
主要代碼:
DispatcherServlet.doDispatch()里的:

DispatcherServlet.render方法:
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); // 1
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); // 2
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
1. view = resolveViewName()會根據(jù)不同的路徑生成不同的view, return mav 會返回JstlView, return "forward:/patch" 會返回InternalResourceView, return "direct:/patch" 會返回IndirectView
2. 不同的view去走不同的view.render(), 根據(jù)不同的view重寫abstract void renderMergedOutputModel方法

再來看是如何生成不同的view:[/code][code]view = resolveViewName() 進去,走到
DiapatcherServlet先有ViewResolver這個,用來生成不同的view
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;
}
ViewResolver 接口只有一個方法
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
要配置具體的視圖解析器,springMVC中使用的是InterResourceViewResover,InterResourceViewResover 和他的父類UrlBasedViewResolver中都沒有重寫resolveViewName方法,再上一層的父類AbstractCahingViewResolver實現(xiàn)了resolveViewName方法

AbstractCahingViewResolver:
@Override
@Nullable
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.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);
}
}
InterResourceViewResover中沒有createView方法,所以是調(diào)用它父類UrlBasedViewResolver的createView方法:
@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;
}
// Check for special "redirect:" prefix.
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); // return "direct:/patch"在這里構(gòu)造view
}
// Check for special "forward:" prefix.
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); // return "forward:/patch" 在這里構(gòu)造view
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale); // return mav 在這里構(gòu)造view
}
關(guān)于ViewResolver的代碼執(zhí)行順序, 前面分析那么多,這里再打斷點快速驗證一下:
進DispatcherServlet的doDispatch看到就是這個解析器:

斷點放在這里,

然后下一步:

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫分表
本文主要介紹了SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫分表,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
Java中使用正則表達式獲取網(wǎng)頁中所有圖片的路徑
這篇文章主要介紹了Java中使用正則表達式獲取網(wǎng)頁中所有圖片的路徑,本文直接給出實例代碼,需要的朋友可以參考下2015-06-06

