Springmvc ViewResolver设计实现过程解析
总结:
viewresolver 如果要改需要自己注入到容器中并进行修改, springmvc使用的是interresourceviewresover
view不需要自己改,是springmvc根据return返回值选的
既然看到有modelandview直接跳转jsp的, 有请求转发的,有重定向的,这里整体是怎么设计的: (@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()会根据不同的路径生成不同的view, return mav 会返回jstlview, return "forward:/patch" 会返回internalresourceview, return "direct:/patch" 会返回indirectview
2. 不同的view去走不同的view.render(), 根据不同的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实现了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方法,所以是调用它父类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"在这里构造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" 在这里构造view } // else fall back to superclass implementation: calling loadview. return super.createview(viewname, locale); // return mav 在这里构造view }
关于viewresolver的代码执行顺序, 前面分析那么多,这里再打断点快速验证一下:
进dispatcherservlet的dodispatch看到就是这个解析器:
断点放在这里,
然后下一步:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Android 9 解决系统显示设置最大导航栏按键显示错位问题
下一篇: 嵌入式软件工程师常用的软件