本文共 6751 字,大约阅读时间需要 22 分钟。
springmvc的视图解析
概述:springmvc的视图解析主要是通过ViewResolver构建View,然后组装model数据,最后渲染视图,ViewResolver是由我们配置的,默认提供的ViewResolver是InternalResourceViewResolver即Jsp的视图,下面我们在源码里找到视图解析的原理和实现。
一、ViewResolver分析
springmvc视图解析是通过ViewResolver根据view的名称,来查询指定的view,而View复制渲染,Resolver复制查找指定的view,默认情况下配置的ViewResolver是InternalResourceViewResolver,对应InternalResourceViewResolver查询出的view是InternalResourceView的子类JstlView.class。
1、ViewResolver 的继承结构
ViewResolver 顶级接口
+ View resolveViewName(String viewName, Locale locale) AbstractCachingViewResolver 缓存抽象基类---主要功能是View对象加缓存访问的处理 + View resolveViewName(String viewName, Locale locale) // 缓存处理,内部调用createView()创建view View createView(String viewName) // 创建视图方法,直接调用loadView创建view abstract View loadView(String viewNmae) // 抽象方法-子类实现 UrlBasedViewResolver Url解析的基础实现类---主要功能就是处理redirect和forword,以及根据class创建View设置url View createView(String viewName) // 复写父类增加解析redirect和forword处理后,再次调用父类的createView方法。 View loadView(String viewName) // 主要调用buildView实现 View buildView(String viewName) // 主要根据getViewClass创建view实例,并且必须是AbstractUrlBasedView的子类 InternalResourceViewResolver 继承UrlBasedViewResolver---处理jsp的视图解析, 功能就是提供getViewClass() AbstractTemplateViewResolver 模板视图解析的基类 --- 主要就是覆写buildView并限制getViewClass类型和公共设置 View buildView(String viewName) // 覆写buildView,并调用父类的buildView后,限制AbstractTemplateView的子类 FreeMarkerViewResolver 继承AbstractTemplateViewResolver---功能就是提供FreeMarker的ViewClass()2、类图和介绍
3、ViewResolver主要的代码片段
1) AbstractCachingViewResolver实现的resolveViewName方法
详见:
2) UrlBasedViewResolver的createView方法解析redirect和forward。
protected View createView(String viewName, Locale locale) throws Exception { if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); return applyLifecycleMethods(viewName, view); } if (viewName.startsWith(FORWARD_URL_PREFIX)) { String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); return new InternalResourceView(forwardUrl); } // 调用父类createView,实际调用模板方法loadView由本类是实现,再调用buildView return super.createView(viewName, locale); }
3) UrlBasedViewResolver的buildView方法
protected AbstractUrlBasedView buildView(String viewName) throws Exception { AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass()); // 根据viewClass创建View view.setUrl(getPrefix() + viewName + getSuffix()); // 设置url String contentType = getContentType(); if (contentType != null) { view.setContentType(contentType); } // .....略 return view; }
4) InternalResourceViewResolver和FreeMarkerViewResolver的设置viewClass
public InternalResourceViewResolver() { // 构造函数设置ViewClass Class viewClass = requiredViewClass(); if (viewClass.equals(InternalResourceView.class)) { viewClass = JstlView.class; } setViewClass(viewClass); } protected Class requiredViewClass() { // 提供InternalResourceView视图 return InternalResourceView.class; } public FreeMarkerViewResolver() { // 构造函数设置ViewClass setViewClass(requiredViewClass()); } protected Class requiredViewClass() { // 提供FreeMarkerView的视图 return FreeMarkerView.class; }
二、View类分析
1、概述:从上面ViewResolver的分析中我们可以知道,ViewResolver获取view时,已经创建了view的实例,并且处理了redirect和forword等请求,设置了url,现在如果要渲染页面,只需要把model的数据和视图进行组合,然后渲染页面即可。
View的类型和ViewResolver成对应出现的,最终的ViewResolver都指定了具体的View类型,我们先看View的继承结构。2、View的类结构
View 视图接口
+ void render(model, request, response)AbstractView 抽象基类
+ void render(model, request, response) // 实现render主要是一些model数据的合并,调用模板方法renderMergedOutputModel abstract renderMergedOutputModel() // model数据合并后,执行渲染的模板方法,子类实现。 AbstractUrlBasedView 抽象基类 --- 提供Url内置属性 InternalResourceView JSP的视图实现 --- 转发请求到jsp + renderMergedOutputModel() // Model数据添加到requestAttr中,RequestDispatcher转发请求到jsp页面RedirectView 重定向操作用视图的包装 ---- 主要是重定向及参数处理以及处理FlashMap
+ renderMergedOutputModel() // 重定向参数处理以及FlashMap的处理。3、源码片段
InternalResourceView的renderMergedOutputModel()方法protected void renderMergedOutputModel( Mapmodel, HttpServletRequest request, HttpServletResponse response) throws Exception { exposeModelAsRequestAttributes(model, request); // model参数到requestAttr String dispatcherPath = prepareForRendering(request, response); // 路径处理 RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); // request.getRequestDispatcher(path); rd.forward(request, response); // 转发 }
三、流程分析
对ViewResolver和View的基本功能和类结构了解后,基本就知道,spring-mvc视图解析,就是一个对应的视图解析器,根据视图名称创建对应类型视图的过程,这个过程处理了视图的缓存,redirect和forword,并且最终会组装model参数,或者处理重定向FlashMap的参数到session等等一系列操作。
下面我们对照执行过程中的源码了解一下springmvc的视图解析。
3.1 首先,我们先看下视图解析器ViewResolver如何初始的,即springmvc根据什么来确定视图解析器的类型,直接切入主题,我们到spring-mvc核心servlet-DispatcherServlet中初始化ViewResolver中查看。
private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) { // 从spring容器查询所有的ViewResolvers MapmatchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList (matchingBeans.values()); OrderComparator.sort(this.viewResolvers); } } else { // 查询一个ViewResolvers ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList(vr); } // 如果没有,就加载默认,默认实际上是DispatcherServlet.properties中配置了 if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); } } }
protectedList getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); // 从配置文件得到指定的key if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); for (String className : classNames) { Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); // 获取class,并后续加载到spring容器创建bean // ......... } }
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
上面是DispatcherServlet.properties指定的默认视图解析器
3.2 实际追踪请求过程
先创建一个Controller
/** * 基本请求 */ @RequestMapping("/hello") public String baseHello(Model model) { model.addAttribute("param1", "param1"); model.addAttribute("param2", "param2"); return "/hello"; } /** * 重定向请求 */ @RequestMapping("/redirect") public String redireHello(Model model) { return "redirect:/hello"; }
过程省略......
完毕!!!
转载地址:http://zruni.baihongyu.com/