SpringMVC——视图解析
一、SpringMVC解析视图概述
- 不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,由视图解析器解析视图,然后,进行页面的跳转。
视图解析源码分析:重要的两个接口View和ViewResolver
- View视图:
- ViewResolver视图解析器:
二、视图和视图解析器
- 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图
- Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart等各种表现形式的视图
- 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦
2.1 视图
-
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。
-
为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口:
-
视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题
常用的视图实现类:
2.2 视图解析器
- SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中 配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
- 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
- 所有的视图解析器都必须实现 ViewResolver 接口:
常用的视图解析器实现类:
- 程序员可以选择一种视图解析器或混用多种视图解析器
- 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高 。
- SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
- InternalResourceViewResolver
- JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器:
- JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器:
三、JstlView(国际化)
-
若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView (断点调试,将JSTL的jar包增加到项目中,视图解析器会自动修改为JstlView)
-
若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件
3.1 JstlView使用流程
-
配置两个语言的配置文件:(命名规范:基础名_语言代码_国家代码.properties)
-
增加jstl标签 jar包(断点调试,这时的View对象就是JstlView)
-
设置国际化资源文件
<!--让SpringMVC管理国际化资源文件;配置一个资源文件管理器;id是必须叫messageSource --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <!-- basename指定基础名--> <property name="basename" value="i18n"></property> </bean>
-
控制器代码
@Controller public class helloController { @RequestMapping("toLogin") public String login(){ System.out.println("login..."); return "login"; } }
-
登录页面(/WEB-INF/pages/login.jsp)使用fmt标签库
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="toLogin"> <fmt:message key="username"/><input /><br/> <fmt:message key="password" /><input /><br/> <input type="submit" value="<fmt:message key='loginBtn' />" /> </form> </body> </html>
注意点:
-
一定要过SpringMVC的视图解析流程,人家会创建一个jstlView帮你快速国际化;不能直接访问登录页面,也不能通过forward访问。
分析源码:
3.2 view-controller将请求映射一个页面
-
若希望直接响应通过 SpringMVC 渲染的页面,可以使用 mvc:view-controller 标签实现
<!-- 发送一个请求("toLoginPage");直接来到web-inf下的login页面;mvc名称空间下有一个请求映射标签 --> <!-- path="":指定哪个请求 view-name:指定映射给哪个视图 走了SpringMVC的整个流程,视图解析....不需要再写控制器方法 --> <mvc:view-controller path="/toLogin" view-name="login"/>
-
配置<mvc:view-controller>会导致其他请求路径失效
解决办法:配置mvc:annotation-driven标签<!-- 开启mvc注解驱动模式;开启了mvc的开挂模式 --> <mvc:annotation-driven></mvc:annotation-driven>
三、forward和redirect
- 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
- 如果返回的字符串中带
forward:
或redirect:
前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和 redirect: 当成指示符,其后的字符串作为 URL 来处理 - redirect:success.jsp:会完成一个到 success.jsp 的重定向的操作
- forward:success.jsp:会完成一个到 success.jsp 的转发操作
- forward前缀指定一个转发操作
/** * forward:/hello.jsp * forward:转发到一个页面 * /hello.jsp:转发到当前项目下的hello * * 注意:一定加/,如果不加/就是相对路径,容易出问题 */ @RequestMapping("/handle01") public String handle01(){ System.out.println("handle01"); return "forward:/hello.jsp"; }
- redirect前缀指定重定向到页面
/** * 重定向到hello.jsp页面 * 有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串; * * 转发 forward:转发的路径 * 重定向 redirect:重定向的路径 * /hello.jsp:代表就是从当前项目下开始;SpringMVC会为路径自动的拼接上项目名 * * 原生的Servlet重定向/路径需要加上项目名才能成功 * response.sendRedirect("/hello.jsp") * @return */ @RequestMapping("handle03") public String handle03(){ System.out.println("handle03..."); return "redirect:/hello.jsp";//请求转发到hello.jsp页面 }
四、自定义视图
-
编写自定义的视图解析器,和视图实现类
控制器方法:
/** * 自定义视图解析器和视图对象 */ @Controller public class MyViewResolverController { @RequestMapping("/handleplus") public String handleplus(Model model){ List<String> vname = new ArrayList<String>(); List<String> imgname = new ArrayList<String>(); vname.add("佟老师"); vname.add("康师傅"); imgname.add("萌萌"); model.addAttribute("video", vname); model.addAttribute("imgs", imgname); return "meinv:/login"; } }
自定义视图:
/** * 自定义视图 * @author ZB * */ public class MyView implements View { @Override public String getContentType() { // TODO Auto-generated method stub return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub System.out.println("进入到自定义视图中:"+model.get("viedo")); response.setContentType("text/html");//设置相应类型 response.getWriter().write("<h1>哈哈</h1>"); } }
自定义视图解析器:
/** * 自定义视图解析器 * @author ZB * */ public class MyMeiNvViewResolver implements ViewResolver { @Override public View resolveViewName(String viewName, Locale locale) throws Exception { /** * 根据视图名返回视图对象 */ //处理以meinv为前缀的 if(viewName.startsWith("meinv:")){ return new MyView(); }else{ //如果不能处理返回null即可 return null; } } }
-
视图解析器必须放在ioc容器中,让其工作,能创建出我们的自定义视图对象;
<bean class="com.zb.view.MyMeiNvViewResolver"> </bean>
输入网址/handleplus,会报404: /05_SpringMVC_viewResolver/WEB-INF/pages/meinv:/login.jsp
debug分析原因:
InternalResourceViewResolver视图解析器位于自定义视图解析器之前解析:导致meinv:/login被InternalResourceViewResolver进行拼串将请求路径拼成了WEB-INF/pages/meinv:/login.jsp,从而出现404
解决办法:指定视图解析器的优先级
自定义视图解析器实现Ordered的接口:
/**
* 自定义视图解析器
* @author ZB
*
*/
public class MyMeiNvViewResolver implements ViewResolver,Ordered {
private Integer order;
@Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
/**
* 根据视图名返回视图对象
*/
//处理以meinv为前缀的
if(viewName.startsWith("meinv:")){
return new MyView();
}else{
//如果不能处理返回null即可
return null;
}
}
@Override
public int getOrder() {
return order;
}
public void setOrder(Integer order){
this.order = order;
}
}
在spring配置中指定优先级,数字越小优先级越高
<bean class="com.zb.view.MyMeiNvViewResolver">
<!-- 自定义的视图解析器,value="1"数字越小优先级越高 -->
<property name="order" value="1"></property>
</bean>
此时:自定义视图解析器优先解析:(得到响应页面)