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方法
详见:https://blog.csdn.net/shuixiou1/article/details/113006428
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(
Map<String, Object> model, 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
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<ViewResolver>(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);
}
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> 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";
}
过程省略......
完毕!!!