t;
<property name="order" value="99"/>
</bean>
<!--4.加入两个常规的配置-->
<!--支持SpringMVC的高级功能,比如:JSR303校验,映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将SpringMVC不能处理的请求,交给tomcat处理,比如css,js-->
<mvc:default-servlet-handler/>
测试,访问view.jsp,点击超链接
成功跳转到 my_view.jsp
后台输出如下:说明整个执行流程如图所示。
2.3创建自定义视图的步骤
- 自定义一个视图:创建一个 View 的 bean,该 bean 需要继承自 AbstractView,并实现renderMergedOutputModel方法
- 并把自定义 View 加入到 IOC 容器中
- 自定义视图的视图解析器,使用 BeanNameViewResolver,这个视图解析器也需要配置到 ioc 容器文件中
- BeanNameViewResolver 的调用优先级需要设置一下,设置 order 比 Integer.MAX_VALUE 小的值,以确保其在默认的视图解析器之前被调用
2.4Debug源码-自定义视图解析器执行流程
自定义视图-工作流程:
- SpringMVC 调用目标方法,返回自定义 View 在 IOC 容器中的 id
- SpringMVC 调用 BeanNameViewResolver 视图解析器:从 IOC 容器中获取返回 id 值对应的 bean,即自定义的 View 的对象
- SpringMVC 调用自定义视图的 renderMergedOutputModel 方法,渲染视图
- 说明:如果 SpringMVC 调用 Handler 的目标方法时,返回的自定义 View ,在 IOC 容器中的 id 不存在,则仍然按照默认的视图解析器机制处理。
Debug-01
(1)在GoodsHandler的目标方法中打上断点:
(2)点击debug,访问view.jsp,点击超链接,可以看到后台光标跳转到断点处:
(3)在源码 BeanNameViewResolver 的 resolveViewName 方法处打上断点:
(4)点击Resume,光标跳转到了这个断点处,viewName 的值就是自定义视图对象的 id:这里完成视图解析
resolveViewName 方法如下:
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws BeansException {
//获取ioc容器对象
ApplicationContext context = obtainApplicationContext();
//如果容器对象中不存在 目标方法返回的自定义视图对象id
if (!context.containsBean(viewName)) {
// Allow for ViewResolver chaining...
//就返回null,让默认的视图解析器处理该视图
return null;
}
//判断自定义的视图是不是 org.springframework.web.servlet.View 类型
if (!context.isTypeMatch(viewName, View.class)) {
//如果不是
if (logger.isDebugEnabled()) {
logger.debug("Found bean named '" + viewName + "' but it does not implement View");
}
// Since we're looking into the general ApplicationContext here,
// let's accept this as a non-match and allow for chaining as well...
return null;
}
//如果是,就返回这个自定义视图对象
return context.getBean(viewName, View.class);
}
(5)在自定义视图对象里打上断点:
(6)点击 resume,光标跳转到该断点:在这里完成视图渲染,并转发到结果页面
(7)最后由 tomcat 将数据返回给客户端:
2.5Debug源码-默认视图解析器执行流程
将默认视图解析器的优先级调高:
debug-02
(1)仍然在GoodsHandler中添加断点:
(2)浏览器访问 view.jsp,可以看到后台光标跳转到了断点处:
(3)分别在默认视图解析器(InternalResourceViewResolver)和自定义视图解析器(BeanNameViewResolver) 中的方法中打上断点:
(4)点击resume,可以看到光标先跳到了默认视图解析器的 buildView 方法中:因为默认解析器的优先级在之前设置为最高。
buildView 方法:
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//根据目标方法返回的viewName创建一个View对象
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
if (this.alwaysInclude != null) {
view.setAlwaysInclude(this.alwaysInclude);
}
view.setPreventDispatchLoop(true);
return view;
}
这个 View 对象的 url 是按照你配置的前缀和后缀,拼接完成的 url
(5)之后就会到该View对象进行视图渲染,然后由Tomcat将数据返回给客户端。
但是如果该url下没有/WEB-INF/pages/liView.jsp文件,就会报错:
2.6Debug源码-自定义View不存在,会走默认视图解析机制
视图解析器可以配置多个,按照指定的顺序来对视图进行解析。如果上一个视图解析器不匹配,下一个视图解析器就会去解析视图,以此类推:
-
在容器文件中,将默认的视图解析器调用优先级降低,提高自定义视图解析器的调用优先级。见2.2的容器文件配置
-
删除2.2中的自定义视图MyView.java。也就是说,自定义视图解析器解析目标方法返回的视图对象时,将会无法解析该视图,因为它不存在。
-
这时就会去调用下一个优先级的视图解析器,即默认视图解析器。
debug-03
(1)仍然在GoodsHandler中添加断点:
(2)浏览器访问 view.jsp,可以看到后台光标跳转到了断点处:
(3)在自定义的视图解析器 BeanNameViewResolver 中打上断点:
(4)点击resume,可以看到光标跳转到该断点