一.请求数据传入
SpringMVC通过分析处理方法的签名,HTTP请求信息绑定到处理方法的相应的入参中。SpringMVC对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任何方式对方法进行签名。必要是可以对方法及方法入参标注相应的注解(@PathVariable,@RequestParam,@RequestHeader等)。SpringMVC会将HTTP请求绑定到相应的方法入参中,并根据方法的返回值类型做出相应的处理。
1.1 默认方法方法获取参数
直接给方法入参写一个和请求参数名相同的变量,这个变量就来接收请求参数的值。 带,有值,没带值为null。
发出请求:
<a href="handle02?username=tomcat">测试</a>
处理方法:
@RequestMapping(value="/handle02")
public String handle02(String username){
System.out.println("这个变量的值"+username);
return "success";
}
输出tomcat。
1.2 @RequestParam
在处理方法入参处使用@RequestParam可以把请求参数传递给请求方法,有三个属性:
- value:参数名
- required:是否必须。默认为true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常
- defaultValue:默认值,当没有传递参数时使用改值
// @RequestParam("user")String username
// username=request.getParameter("user);
@RequestMapping(value="/handle02")
public String handle02(@RequestParam("user")String username){
System.out.println("这个变量的值"+username);
return "success";
}
要注意@RequestParam和@PathVariable的区别:@PathVariable是获取路径上占位符的参数,而@RequestParam则是获得请求参数
1.3 @RequestHeader
使用@RequestHeader绑定请求头报头的属性值。请求头中包含了若干个属性,服务器可据此获得客户端信息,通过@Request请求头中的属性值绑定处理方法得到入参中。
和上述的@RequestParam一样,有三个属性,含义也一样。
- value:参数名
- required:是否必须。默认为true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常
- defaultValue:默认值,当没有传递参数时使用改值
/*
* @RequestHeader("User-Agent")String userAgent
* userAgent=request.getHeader("User-Agent")
*
*/
@RequestMapping(value="/handle02")
public String handle02(@RequestHeader("User-Agent")String userAgent){
System.out.println("请求头中浏览器的信息"+userAgent);
return "success";
}
1.4 @CookieValue
使用CookieValue绑定请求中的Cookie值。有三个属性:
- value:参数名
- required:是否必须。默认为true,表示请求参数中必须包含对应的参数,若不存在,将抛出异常
- defaultValue:默认值,当没有传递参数时使用改值
/*
* * @Cookievalue:获取某个Cookie
* 以前的操作获取某个cookie:
* Cookie[] cookies= request.getCookies();
* for(Cookie c:cookies)
* {
* if(c.getName().equals("JSESSIONID")){
* String cv=c.getValue();
* }
* }
*/
@RequestMapping(value="/handle02")
public String handle02(@CookieValue("JSESSIONID")String jid){
System.out.println("cookie中id的值"+jid);
return "success";
}
1.5 使用POJO作为参数
使用POJO对象绑定请求参数值。SpringMVC会按请求名和POJO属性,进行自动分配,自动为该对象填充属性值。支持级联属性。
控制器方法:
/*
* 如果请求参数是一个POJO,SpringMVC会自动为这个POJO赋值
* 1)将POJO中的每一个属性从request参数尝试获取出来,并封装即可
* 2)还可以级联封装:属性的属性
*/
@RequestMapping("/book")
public String addBook(Book book){
System.out.println("我要保存的图书:"+book);
return "success";
}
表单页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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="book" method="post">
书名:<input type="text" name="bookName"><br/>
作者:<input type="text" name="author"><br/>
价格:<input type="text" name="price"><br/>
库存:<input type="text" name="stock"><br/>
销量:<input type="text" name="sales"><br/>
<hr/>
省:<input type="text" name="address.provice"/>
市:<input type="text" name="address.city"/>
街道:<input type="text" name="address.street"/><br/>
<input type="submit">
</form>
</body>
</html>
实体类:
package com.test.bean;
public class Book {
private String bookName;
private String author;
private Double price;
private Integer stock;
private Integer sales;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
//一定有无参构造器
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", author=" + author + ", price="
+ price + ", stock=" + stock + ", sales=" + sales
+ ", address=" + address + "]";
}
}
package com.test.bean;
public class Address {
private String provice;
private String city;
private String street;
public String getProvice() {
return provice;
}
public void setProvice(String provice) {
this.provice = provice;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
执行结果:
1.5.1 配置字符编码过滤器
/*
* 提交的数据可能有乱码
* 请求乱码:
* GET请求:改server.xml。在8080端口处添加URIEncoding="UTF-8"
* POST请求:
* 在第一获取请求参数之前设置:
* request.setCharacterEncoding("UTF-8");
* 自己写一个filter,SpringMVC有这个filter:CharacterEncodingFilter
* 响应乱码:
* request.setContentType("text/html;charset=utf-8")
*/
/*
发现执行结果中中文有乱码,就要配置字符编码过滤器,且配置在其他过滤器之前,否则不起作用:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用SpringMVC前端控制器写完就直接写字符编码过滤器。
Tomcat一装上,上手就是server.xml的8080出添加URIEncoding="UTF-8"。
1.6 传入原生API
MVC的Handler方法可以接受哪些ServletAPI类型的参数。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale:国际化有关的区域信息
- InputStream: ServletInputStream inputStream= request.getInputStream();
- OutputStream: ServletOutputStream outputStream=reponse.getOutputStream();
- Reader: BufferReader reader=request.getReader();
- Writer: PrintWriter writer=reponse.getWriter();
@RequestMapping("/handle03")
public String handle03(HttpSession session,HttpServletRequest request,HttpServletResponse reponse){
request.setAttribute("reParam", "我是请求域中的");
session.setAttribute("sessionParam", "我是session域中的");
return "success";
}
二.数据输出
2.1 处理模型数据之Map
SpringMVC在内部使用了一个 org.springframework.ui.Model接口存储模型数据。具体步骤如下:
1)SpringMVC在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
2)如果方法的入参为Map,Model或者ModelMap类型,SpringMVC会将隐含模型的引用传递给这些入参。
3)在方法体内,开发者可以通这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
控制器方法:
package com.test.controller;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
/*
* 除了在方法上传入原生的request和session外,还能怎么样把数据带给页面
*
* 1)可以在方法处传入一个Map,或者Model或者ModelMap。给这些参数里面保存的数据都会放在域中,可以在页面获取。数据存储在请求域中
* 关系:
* Map Model ModelMap:最终都是BindingAwareModelMap在工作;
* 相当于BindingAwareModelMap保存中的东西都会放在请求域中
*
* Map(interface jdk) Model(interface spring)
* || //
* || //
* \/ //
* ModelMap(class) //
* \\ //
* ExtendedModelMap
* ||
* ||
* \/
* BindingAwareModelMap
*
*
*/
@Controller
public class OutputController {
//可以在方法处传入一个Map,或者Model或者ModelMap。
//存储在请求域中:request
@RequestMapping("/handle01")
public String handle01(Map<String,Object> map){
map.put("msg", "你好");
System.out.println("map的类型"+map.getClass());
return "success";
}
/*
* Model:是一个接口
*/
@RequestMapping("/handle02")
public String handle02(Model model){
model.addAttribute("msg", "你好");
System.out.println("model的类型"+model.getClass());
return "success";
}
@RequestMapping("/handle03")
public String handle03(ModelMap map){
map.addAttribute("msg", "你好好");
System.out.println("ModelMap的类型"+map.getClass());
return "success";
}
}
2.2 处理模型数据之ModelAndView
控制器处理方法的返回值如果为ModelAndView,则其包含视图信息,也包含模型数据信息。
添加模型数据方法:
ModelAndView.addObject(String attributeName,Object attributeValue)
ModelAndView.addAllObjcet(Map<String,?) modelMap)
设置视图:
void setView(View view)
void setViewName(String viewName)
实验代码的控制器方法:
/*
* 2)方法的返回值可以变为ModelAndView类型
* 既包含视图信息(页面地址)也包含模型数据(给页面带的数据)
* 而且数据放在请求域中
*/
@RequestMapping("/handle04")
public ModelAndView handle04(){
//之间的返回值就是视图名,视图名视图解析器会帮我们最终拼串得到页面真实地址
ModelAndView view=new ModelAndView();
view.setViewName("success");
view.addObject("msg", "年后");
return view;
}
2.3 处理模型数据之@SessionAttributes
若希望在多个请求之间共用某个模型属性数据,则可以在控制器类(只能放在类上)上标注一个@SessionAttributes,SpringMVC将在模型中对应的属性暂存到HttpSession中。
@SessionAttributes除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型数据将要放到会话之中。
实验代码-控制器方法:
3)SpringMVC提供了一种可以临时给Session域中保存数据的方式
* 使用注解 @SessionAttributes(只能标在类上)
* @SessionAttributes(value="msg"):给BindingAwareModelMap保存中的数据或者ModelAndeView中的数据,同时给session中放一份
* value指定保存数据时要给session中放数据的key
* value="msg":只要保存的是这种key的数据,给Session中放一份
* types={String.class}:只要保存的是这种类型的数据,给Session中也放一份
* 可能会发生异常,给session中放数据用原生API
*/
@SessionAttributes(value={"msg","hah"},types={String.class})
@Controller
public class OutputController {
//可以在方法处传入一个Map,或者Model或者ModelMap。
//存储在请求域中:request
@RequestMapping("/handle01")
public String handle01(Map<String,Object> map){
map.put("msg", "你好");
System.out.println("map的类型"+map.getClass());
return "success";
}
/*
* Model:是一个接口
*/
@RequestMapping("/handle02")
public String handle02(Model model){
model.addAttribute("msg", "你好");
System.out.println("model的类型"+model.getClass());
return "success";
}
@RequestMapping("/handle03")
public String handle03(ModelMap map){
map.addAttribute("msg", "你好好");
System.out.println("ModelMap的类型"+map.getClass());
return "success";
}
/*
* 2)方法的返回值可以变为ModelAndView类型
* 既包含视图信息(页面地址)也包含模型数据(给页面带的数据)
* 而且数据放在请求域中
*/
@RequestMapping("/handle04")
public ModelAndView handle04(){
//之间的返回值就是视图名,视图名视图解析器会帮我们最终拼串得到页面真实地址
ModelAndView view=new ModelAndView();
view.setViewName("success");
view.addObject("msg", "年后");
return view;
}
}
最好不要使用@SessionAttributes注解。如果要使用,为了避免可能引发的异常,要么隐含模型中有@SessionAttributes标注的属性。如果隐含模型中没有,session必须有,否则会抛出异常。
三.@ModelAttribute
3.1 使用场景(所有的有关数据库的操作都模拟进行)
在书城项目中,有一个修改图书信息的模块。图书的名称是不可修改的。
页面:
<h1>修改图书</h1>
<form action="updateBook" method="post">
<input type="hidden" name="id" value="100"/>
书名:西游记<br/>
作者:<input type="text" name="author"/><br/>
价格:<input type="text" name="price"/><br/>
库存:<input type="text" name="stock"/><br/>
销量:<input type="text" name="sales"/><br/>
<input type="submit" value="修改图书"/>
</form>
控制器处理方法:
package com.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.test.bean.Book;
/*
*
* 测试ModelAttribute注解
* 使用场景:书城的图书修改为例
* 1)页面端:显示要修改的图书信息,图书的所以字段都在
* 2)Servlet收到修改请求,调用dao
* String sql = "update t_book set name = ? ,author = ? ,price = ? ,sales = ? ,stock = ? , img_path = ? where id = ?";
* 3)实际场景?
* 并不是全字段修改,只会修改部分字段
* (1)不修改的字段可以在页面进行展示,但是不要提供修改框
* (2)为了简单起见,Controller直接在参数位置来写Book对象
* (3)SpringMVC为我们自动封装Book(没有带的值为null)
* (4)如果接下来调用了全字段更新的dao操作,会将其他字段变为null。
* 如何能保证全字段更新的时候,只更新带来的数据
* 1)修改dao,代价大
* 2)Book对象如何封装了?
* SpringMVC创建一个book对象,每个属性都有默认值,bookName就是null
* 让SpringMVC别创建对象,直接从数据库中先取出一个Id的book。
* 将请求中所有域book对应的属性一一设置过来;
* 使用刚才从数据库取出的book对象,给它里面设置值(请求参数带了哪些值就覆盖之前的值)
* 带来的字段就改为携带的值,没带的值就保持之前的值
* 调用全字段更新就有问题
* 将之前从数据库中查到的对象,并且封装了请求参数的对象,进行保持
*
*/
@Controller
public class ModelAttributeTestController {
/*
* 不要new Book了,刚才保存了一个book
*/
@RequestMapping("/updateBook")
public String updateBook(@ModelAttribute("book")Book book){
System.out.println("页面要提交的图书信息:"+book);
/*
* bookDao.updateBook
* String sql = "update t_book set name = ? ,author = ? ,price = ? ,sales = ? ,stock = ? , img_path = ? where id = ?";
*/
return "success";
}
}
在这个方法的注释中阐述了使用了全字段更新会出错的原因。
SpringMVC提出的解决方法:SpringMVC要封装请求参数的Book对象不应该是自己new出来的,而应该是从数据库拿到准备好的对象,再来使用这个对象封装参数
3.2 @ModelAttribute注解
SpringMVC使用@ModelAttribute注解来解决上面问题。
- 在方法定义上使用@ModelAttribute注解:SpringMVC在自动调用目标方法前,会先逐个调用在方法级上标注了@ModelAttribute的方法。
- 在方法的入参前使用@ModelAttribute注解:可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数绑定到对象中,再传入入参。
实验代码:
package com.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import com.test.bean.Book;
@Controller
public class ModelAttributeTestController {
/*
* 不要new Book了,刚才保存了一个book
*/
@RequestMapping("/updateBook")
public String updateBook(@ModelAttribute("book")Book book){
System.out.println("页面要提交的图书信息:"+book);
/*
* bookDao.updateBook
* String sql = "update t_book set name = ? ,author = ? ,price = ? ,sales = ? ,stock = ? , img_path = ? where id = ?";
*/
return "success";
}
/*
* 1)SpringMVC要封装请求参数的Book对象不应该是自己new出来的
* 而应该是从数据库拿到准备好的对象
* 2)再来使用这个对象封装参数
*
* ModelAttribute
* 参数:取出刚才保存的数据
* 方法位置:这个方法就会提前于目标方法先运行
* 1)我们可以提前查出数据库中图书的信息
* 2)将这个图书信息保存起来(方便下一个方法还能使用)
*/
@ModelAttribute
public void myModelAttribute(Model model){
Book book=new Book();
book.setBookName("西游记");
book.setAuthor("吴承恩");
book.setId(100);
book.setPrice(11.11);
book.setSales(22);
book.setStock(33);
System.out.println("数据库中查到的图书信息是:"+book);
model.addAttribute("book", book);
System.out.println("modelAttribute方法...查询了图书并给你保存起来...");
}
}
提交一次表单,来看输出结果:
那么为什么这个修改图书的时候用的是之前保存的book?分析源码,@ModelAttribute标注的方法会提前运行并把方法运行的结果放在隐含模型中,放的时候使用一个key:
- 如果@ModelAttribute(value="")指定了,就用指定的key
- 如果没指定,就用返回值类型的首字母小写作为key
四.视图解析
在HelloWord项目中,一个请求完成后,会返回字符串。配置的视图解析器会将这个字符串拼串成要去的地址,进行跳转。下面就是那个视图解析器的配置。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
在学习视图解析器的源码之前,首先来学习以下视图解析的应用。
4.1 转发与重定向
4.1.1 forward前缀指定一个转发操作
在之前的HelloWorld的项目中,视图解析器配置的地址前缀是WEB-INF/pages,这就说明一旦跳转,页面都是这个目录下的。那么如果有页面在WebContent目录下怎么办?
- 使用相对路径
@RequestMapping("/hello")
public String handle(){
// /hello.jsp
//WEB-INF/pages/hello.jsp
System.out.println("测试");
//在WebContent目录下的
//相对路径
return "../../hello";
}
- 使用forward前缀指定一个转发操作
/*
* forward:转发到一个页面
* /hello.jsp:转发到当前项目下的hello
* 这个返回值不会由视图解析器拼串
* 如果不加/,就是相对路径,容易出问题
*/
@RequestMapping("/handle01")
public String handle01(){
return "forward:/hello.jsp";
}
@RequestMapping("/handle02")
public String handle02(){
return "forward:/handle01";
}
4.1.2 redirect前缀指定重定向到页面
/*
* 重定向hello.jsp页面
* 重定向 redirect:/重定向路径
* 原生的Servlet重定向需要加上项目名才能成功
* redirect:/hello.jsp:就是从当前项目下开始,SpringMVC会为路径自动的拼接上项目
*/
@RequestMapping("/handle03")
public String handle03(){
System.out.println("重定向操作");
return "redirect:/hello.jsp";
}
@RequestMapping("/handle04")
public String handle04(){
return "redirect:/handle03";
}
4.2 视图和视图解析器
分析视图解析的源码后,可以发现请求返回一个ModelAndView对象。对于那些返回String,View或ModelMap等类型的处理方法,SpringMVC也会在内部将它们装配成一个ModelAndView对象,它包含了逻辑名和模型对象的视图。
SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP,也可以是Execel,JFfreeChart等各种表现形式的视图。
对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC的充分解耦。
4.2.1 视图
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。为了实现视图模型和具体实现技术的解耦,Spring在org.springframework.web.servlet包中定义了一个高度抽象的View接口:
视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题。
4.2.1.1常用的视图实现类
4.2.2 视图解析器
SpringMVC为逻辑视图名的解析提供了不同的策略,可以在Spring WEB上下文配置一种或多种解析器,并指定它们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。
视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
所有的视图解析器都必须实现ViewResolver接口:
4.2.2.1 常用的视图解析器实现类
- 程序员可以选择一种视图解析器或混用多种视图解析器。
- 每个视图解析器都实现了Ordered接口并开放出一个order属性,可以通过order属性指定解析器的优先顺序,order越小优先级越高。
- SpringMVC会按照视图解析器顺序的优先顺序对逻辑视图名进行解析,知道解析成功并返回视图对象,否则会抛出ServletException异常。
- JSP最常见的视图技术,可以使用InternalResourceViewResolver作为视图解析器。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
4.2.3 JstlView
若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView (断点调试,将JSTL的jar包增加到项目中,视图解析器会自动修改为JstlView)
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar
除了导包可以自动修改,也可以使用配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
</bean>
JstlView可以支持快速国际化。
1.Java Web国际化步骤:
1)得到一个Locale对象
2)使用ResourceBundle绑定国际化资源文件
3)使用ResourceBundle.getString("key")获取国际化配置文件中的值
4)Web页面的国际化,fmt标签库来做
<fmt:setLocale>
<fmt:setBundle>
<fmt:message>
2.JstlView国际化的步骤:
1)让Spring管理国家资源
2)直接去页面使用
实验:
先写两个国际化资源文件(处于源码文件夹下):
i18n_zh_CN.properties:
welcomeinfo=\u6B22\u8FCE
username=\u7528\u6237\u540D
password=\u5BC6\u7801
loginBtn=\u767B\u5F55
i18n_en_US.properties:
welcomeinfo=WELCOME
username=USERNAME
password=PASSWORD
loginBtn=login
若使用JSTL的fmt标签则需要在SpringMVC的配置文件中配置国际化资源文件:
<!-- 让SpringMVC管理国际化资源文件 ,配置一个资源文件管理器-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- basename指定基础名 -->
<property name="basename" value="i18n"></property>
</bean>
login页面:
<%@ 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>
<h1>
<fmt:message key="welcomeinfo"/>
</h1>
<form action="">
<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前缀(会创建的InternalResourceView而不是JstlView)。
4.2.4 mvc:view-controller标签
若希望直接响应通过SpringMVC渲染的页面,可以使用mvc:view-controller标签实现。
<!-- 发送一个请求,直接来到WEB-INF下的login页面
mvc名称空间有一个请求映射标签
直接配置响应的页面,无需经过控制器来执行结果
-->
<!-- path指定哪个请求
view-name:指定映射哪个视图
-->
<mvc:view-controller path="/toLogin" view-name="login"/>
配置这个标签会导致其他请求路径失效,解决方法:
<!-- 开启MVC注解驱动模式 -->
<mvc:annotation-driven></mvc:annotation-driven>
4.3 自定义视图和自定义视图解析器
步骤:
1)编写自定义的视图解析器和视图实现类
2)视图解析器必须放在IOC容器中
实验:
1.页面链接
<a href="handleplus">下载资源</a>
2.控制器方法
package com.test.Controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyViewResolverController {
@RequestMapping("/handleplus")
public String handleplus(Model model){
List<String> name=new ArrayList<String>();
name.add("你好");
name.add("hh");
List<String> imgs=new ArrayList<String>();
imgs.add("哈哈哈");
model.addAttribute("video", name);
model.addAttribute("imgs", imgs);
return "mei:/login";
}
}
3.自定义视图
package com.test.view;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.View;
/*
* 自定义视图
*/
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);
response.setContentType("text/html");
List<String> vn=(List<String>) model.get("video");
response.getWriter().write("哈哈哈哈<h1>即将展现精彩内容</h1>");
for(String string:vn)
{
response.getWriter().write("<a>下载"+string+"</a>");
}
}
}
4.自定义视图解析器
视图解析器必须放在IOC容器内。还记得解析源码时,根据视图名来获取视图对象。
for (ViewResolver viewResolver : this.viewResolvers) {
//viewResolver视图解析器根据方法的返回值,得到一个View对象
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
所以必须设置视图解析器的优先级。如果不设置优先级的话,第一个获得View对象就是InternalResourceViewResolver。
<bean class="com.test.view.MyMeiViewResolyer">
<property name="order" value="1"></property>
</bean>
<!--已知InternalResourceViewResolver默认的优先级,private int order=Integer.MAX_VALUE"-->
package com.test.view;
import java.util.Locale;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
public class MyMeiViewResolyer implements ViewResolver,Ordered{
private Integer order=0;
@Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
// TODO Auto-generated method stub
//根据视图名返回视图对象
//这个视图名有很多
if(viewName.startsWith("mei")){
return new MyView();
}
else
{
//如果不能处理,返回null
return null;
}
}
@Override
public int getOrder() {
// TODO Auto-generated method stub
return order;
}
/*
* 改变视图解析器的优先级
*/
public void setOrder(Integer order){
this.order=order;
}
}