SpringMVC框架总结
在整个web应用的MVC分层开发中,SpringMVC是对于控制层Controller的优化。
控制器的作用在于数据录入,接收数据,调用业务逻辑层,把数据传到业务逻辑层,业务逻辑层再根据当前的请求,到数据库中操作相应数据,再依次回到业务逻辑层,控制层,完成最终结果的渲染。
它并不参与具体业务的处理,它是控制别人干活的。
控制层的功能:
- 接收并封装数据,把数据交给业务逻辑层
- 调用业务逻辑层实现功能,结果又返回控制器
- 根据结果跳转(请求转发、重定向)
springmvc核心处理器:DispatcherServlet
SpringMVC处理流程:
-
向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。
-
DispatcherServlet 根据 -servlet.xml 中的配置对请求的URL进行解析,得到请求资源标识符(URI)。 然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
-
DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。
-
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
-
Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
-
根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。
-
ViewResolver 结合Model和View,来渲染视图。
-
视图负责将渲染结果返回给客户端
SpringMVC框架搭建
- 导入jar包
- 在web.xml文件中注册springmvc,导入springmvc配置文件
- 写相应的请求url和相应映射Controller
SpringMVC所必须的jar包:
spring-aop.jar
spring-beans.jar
spring-context.jar
spring-core.jar
spring-expression.jar
spring-web.jar
spring-webmvc.jar
standard.jar
jstl.jar
日志包:commons-logging.jar
web.xml文件中注册:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数-->
<!-- 实际上也可以不通过contextConfigLocation来配置springmvc的配置文件,而使用默认的
默认的配置文件是/WEB-INF/<servlet-name>-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- web应用加载的时候就创建-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- url是斜杠可以应答所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml文件模板:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<!--<mvc:annotation-driven></mvc:annotation-driven>-->
<context:component-scan base-package="com.rjxy.qiao.controller"></context:component-scan>
<bean id="ResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
普通映射
<a href="springmvc/testRequestMapping">testRequestMapping</a><br>
private static final String SUCCESS = "success";
@RequestMapping("/testRequestMapping")
public String testRequestMapping(){
System.out.println("testRequestMapping");
return SUCCESS;
}
【注意】:@RequestMapping中又四个属性:value(如果不写,默认就是value,请求URL)、method(请求方法)、params(请求参数的映射条件)、heads(请求头的映射条件)
它们直接是“与”的关系,联合使用多个条件可以使得请求映射更加精确化。
@RequestMapping中的参数:使用method属性指定请求方式
@RequestMapping(value = "/testMethod",method = RequestMethod.POST)
public String testMethod(){
System.out.println("testMethod");
return SUCCESS;
}
@RequestMapping中的参数:params和headers
params可以用来限制请求参数,headers用来限制强求头。
例如:
@RequestMapping(value = "/testParamAndHeaders",params = {"username","age!=10"},headers = {"Accept-Language=zh-CN,zh;q=0.9"})
以上语句则表示强求参数必须包含username和age,且age的值不能是10。而且header中Accept-Language的值必须是zh-CN,zh;q=0.9。只要有一个条件不满足,请求就不会被映射到这个方法。
@RequestMapping(value = "/testParamAndHeaders",params = {"username","age!=10"},headers = {"Accept-Language=zh-CN,zh;q=0.9"})
/**
* 设置参数必须包含username,且age不为10
* 设置请求头参数必须是什么
*/
//必须包含username参数且age的值不能是10
public String testParamAndHeaders(){
System.out.println("testParamAndHeaders");
return SUCCESS;
}
ant风格的3中匹配符,?、*、**
- ?:匹配文件名中的一个字符
- *:匹配文件名中的任意字符
- **:匹配多层路径ant
我们测试一下*,匹配任意多个字符
<a href="springmvc/admin/这里写啥都行/ant">testAntPath</a>
@RequestMapping("/admin/*/ant")
public String testAntPath(){
System.out.println("testAntPath");
return SUCCESS;
}
@PathVariable注解
@PathVariable以用来映射URL中的占位符到目标方法的参数中
<a href="springmvc/testPathVariable/100">testPathVariable 01</a>
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id){
System.out.println("testPathVariable--"+id);
return SUCCESS;
}
REST风格的URL
REST:即Representational State Transfer(资源)便线程状态转化,是一种流行的互联网软件架构。
资源(Resources)、表现层(Representation)、状态转化(State Transfer)
* Rest风格的URL
* 以CRUD为例
* 新增: /order POST
* 修改: /order/1 PUT
* 获取: /order/1 GET
* 删除: /order/1 DELETE
如果客户端想要操作服务器,必须通过某种手段,让服务器端发生状态转化(State Transfer)。这种状态是建立在表现层之上的。具体来说,HTTP协议里面,4个边做操作方式的动词:GET(获取资源)、POST(新建资源)、PUT(更新资源)、DELETE(删除资源)。
由于浏览器的form表单只支持GET与POST请求,而PUT和DELETE等method并不支持,spring3.0中添加了一个过滤器(HiddenHttpMethodFilter)可以将这些请求转化为标准HTTP方法,使得支持GET、POST、PUT、DELETE请求。
如何发送一个put请求或delete请求呢?
- 1.配置HiddenHttpMethodFilter
- 2.需要发送的是一个post请求
- 3.需要在提交的时候携带一个隐藏域,它的name属性是"_method",value = “DELETE” 或"PUT"
- 4.如果报错:jsp不支持(JSPs only permit GET POST or HEAD),则方法前加一个@ResponseBody注解。
在web.xml文件中进行配置;
<!-- 配置org.springframework.web.filter.HiddenHttpMethodFilter,作用:可以把POST请求转化为DELETE或PUT请求-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
GET
<a href="springmvc/testRest/1">testRest GET</a>
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.GET)
public String testRestGet(@PathVariable Integer id){
System.out.println("testRest GET"+id);
return SUCCESS;
}
POST
<form action="springmvc/testRest" method="post">
<input type="submit" value="testRest post">
</form>
@RequestMapping(value = "/testRest",method = RequestMethod.POST)
@ResponseBody
public String testRestPost(){
System.out.println("testRest POST");
return SUCCESS;
}
PUT
<form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="testRest PUT">
</form>
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.PUT)
@ResponseBody
public String testRestPut(@PathVariable Integer id){
System.out.println("testRest PUT"+id);
return SUCCESS;
}
DELETE
<form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="testRest delete">
</form>
@RequestMapping(value = "/testRest/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String testRestDelete(@PathVariable Integer id){
System.out.println("testRest DELETE"+id);
return SUCCESS;
}
@RequestParam注解
它绝对是一个非常重要的注解,很常用的哦。
支持3个属性:value、required、defaultValue
<a href="springmvc/testRequestParam?username=tom&age=18">testRequestParam</a>
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(value = "username") String name,
@RequestParam(value = "age",required = false,defaultValue = "0") int age){
//required默认值是true,改成false表示这个参数不是必须的,可以没有它,没有的话接收到的就是null,int型的根本接收不了的,只能用Integer
//如果请求参数没有,还想用int型接收请求参数,可以添加默认值是0,defaultValue = "0"
System.out.println("testRequestParam,username="+name+" age="+age);
return SUCCESS;
}
@RequestHeader注解
<a href="springmvc/testRequestHeader">testRequestHeader</a>
/**
* 利用@RequestHeader可以获取请求头的参数信息,例如获取 :Accept-Language
* 用法同@RequestParam,3个参数,value,required,defaultValue
* 快捷键:ctrl+shift+i 调用谷歌浏览器的开发者工具
*/
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept-Language") String al){
System.out.println("testRequestHeader,Accept-Language = "+al);
return SUCCESS;
}
@CoookieValue注解
这个仅仅了解一个即可:@CoookieValue:映射一个Cookie值,属性同 @RequestParam
了解一下Cookie的含义:
浏览器中“Cookies"是指服务器暂存放在你的电脑里的txt格式的文本文件资料,主要用于网络服务器辨别电脑使用。比如浏览网站时,Cookies 记录下来你输入的一些资料和信息。再访问同一个网站,就会依据Cookie里的内容来判断使用者,送出特定的信息内容给你。
平时上网时都是使用无状态的HTTP协议传输出数据,这意味着客户端与服务端在数据传送完成后就会中断连接。这时我们就需要一个一直保持会话连接的机制。
在session出现前,cookie就完全充当了这种636f70797a6431333431343761角色。也就是,cookie的小量信息能帮助我们跟踪会话。一般该信息记录用户身份。当然cookie也常记录跟踪购物车的商品信息(如数量)、记录用户访问次数等。
Cookies一词用在程序设计中是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术。从本质上讲,它可以看作是你的身份证。保存的信息片断以"名/值"对(name-value pairs)的形式储存,一个"名/值"对仅仅是一条命名的数据。
一个网站只能取得它放在你的电脑中的信息,它无法从其它的Cookies文件中取得信息,也无法得到你的电脑上的其它任何东西。 Cookies中的内容大多数经过了加密处理,因此一般用户看来只是一些毫无意义的字母数字组合,只有服务器的CGI处理程序才知道它们真正的含义。
硬盘中的Cookies文件可以被Web浏览器读取,它的命名格式为:用户名@网站地址[数字].txt。如笔者计算机中的一个Cookies文件名为:ch@163[1].txt。(以上内容来源:百度百科)
<a href="springmvc/testCookieValue">testCookieValue</a>
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID")String cv){
System.out.println("testCookieValue:"+cv);
return SUCCESS;
}
使用POJO对象绑定请求参数值
POJO(Plain Ordinary Java Object)简单的普通的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
如果开发一个表单项提交的话,如果要提交的参数太多,全部使用@RequestParam去映射太麻烦了,我们可以使用一个POJO帮助我们绑定请求参数值。
SpringMVC会按照请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值,同时它还支持 级联属性!!
表单:
<form action="springmvc/testPOJO">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
邮箱:<input type="text" name="email"><br>
age:<input type="text" name ="age"><br>
省份:<input type="radio" name="address.province" value="河北">河北<br>
<input type="radio" name="address.province" value="山西">山西<br>
city:<input type="text" name="address.city"><br>
<input type="submit" value="提交">
</form>
public class User {
private String username;
private String password;
private String email;
private Integer age;
private Address address;//级联属性
get set toString
}
public class Address {
private String province;
private String city;
get set toString
}
@RequestMapping("/testPOJO")
public String testPOJO(User user){
System.out.println("testPOJO:"+user);
return SUCCESS;
}
使用Servlet 原生API作为入参
支持以下ServletAPI类型的参数:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
如果入参是Writer,那么它的write()方法则自动调用的是response的getWriter()方法,在页面上打印字符串。如果打印的是html数据,就相当于jsp那样回传页面了。
@RequestMapping("/testWriter")
public void testWriter(Writer out) throws IOException {
out.write("hello springmvc");
}
处理模型数据
我们知道,SpringMVC只负责控制层,它不负责具体的业务逻辑,那么它调用业务逻辑后,得到的返回值,一个对象或者一个集合,然后转发到页面。这些数据要想在页面上显示,就必须存入到域对象里,这个域对象通常指的是请求域。
springmvc中提供了如下4中方法输出模型数据。
- ModelAndView
- Map及Model
- @SessionAttributes:将模型数据暂存入HttpSession中,以便多个请求之间可以共享这个属性。
- @ModelAttribute:方法入参标注该注解后,入参的对象酒鬼放到数据模型中。
ModelAndView
springmvc会把ModelAndView的model中的数据放入到request域对象中。
实际上springmvc所有的视图对象最终都封装成ModelAndView返回。
<a href="springmvc/testModelAndView" >testModelAndView</a>
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
String viewName = SUCCESS;
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("time", new Date());
return mav;
}
<h4>success!!hello springmvc</h4>
当前时间:${requestScope.time}
Map或Model
map类型的参数:
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map){
String name = map.getClass().getName();
System.out.println(name);//org.springframework.validation.support.BindingAwareModelMap
map.put("names", Arrays.asList("Tom","Jerry","Mike"));
return SUCCESS;
}
名字names :${requestScope.names}
目标方法可以添加Map类型,实际上也可以是Model类型或ModelMap类型的参数。
@SessionAttributes
如果我们希望多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes。SpringMVC将在模型中找到对应的属性暂存入HttpSession中。
基本用法:
@SessionAttributes("user")
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
在控制器类上加注解:@SessionAttributes,指明哪个对象需要存入session域中,也就是map中的键名。
@RequestMapping("testSessionAttributes")
public String testSessionAttributes(Map<String,Object> map){
User u = new User("Tom","123","[email protected]",18);
map.put("user",u);
return SUCCESS;
}
session scope 中的User:${sessionScope.user}
@SessionAttributes除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性会放到会话中。
@SessionAttributes(type = User.class)会将隐含域中所有类型为User.class的属性都添加到会话中。
例如:把隐含域中全部String类型的属性全部放入会话:
@SessionAttributes(value = "user",types = String.class)
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
这样就不用一个value一个value去指定了。
需要注意这个注解只能放在类的上面。
@ModelAttribute
假设这样的一个需求:数据库的订单表中有一项(比如创建时间)不论在任何情况下都不允许修改,而如果我们订单表中的某一条记录,该如何做呢?
如果有3条字段有一个不能修改,那么我们表单传入的只能是2个字段。按照我们以前的经验呢,我们应该在目标方法中传入对应类的一个对象,这个对象在表单赋值之前是新创建的,表单把请求参数赋值给这个对象的对应属性,注意,只赋了一个属性,有一个属性值就为空,拿着这样的一个对象去更新的话,更新后的效果就是那个不能修改的属性被置空了,还是修改了,如何避免呢?
如果使用隐藏域,隐藏域有两个问题,1.如果该字段是一个比较敏感的字段,比如密码;2.如果不能修改的字段比较多,这么做,就有点麻烦。
还有一种方案:就是在更新之前,在数据库中拿到这个不能被修改的字段值,再赋值给那个对象,这样做更新操作就不会有问题了。
但是呢,那样做还是显得有些麻烦。
我们有一个优雅的解决方案:
从数据库中得到该条记录全部属性值,封装成一个对象,表单中只出入两个属性,它把能修改的字段赋上值,不能修改的字段不用动,再做更新操作即可。
以上就是@ModelAttribute的使用场景。
模拟修改数据时候ajax回显的数据:
<form action="springmvc/testModelAttribute" method="post"><br>
用户名:<input type="text" name = "username" value="Tom"><br>
邮箱:<input type="text" name = "email" value="[email protected]"><br>
年龄:<input type="text" name="age" value="18"><br>
<input type="submit" value="提交">
</form>
修改的映射:
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("修改"+user);
return SUCCESS;
}
@ModelAttribute:
@ModelAttribute
public void getUser(@RequestParam(value = "username")String name,Map<String,Object> map){
if ((name != null)) {
//模拟数据库中获取的对象
User u = new User("Tom","123456","[email protected]",18);
System.out.println("从数据库中获取一个对象:"+u);
map.put("user",u);
}
修改时没有填写密码,但是密码没有被置空,还是原来的密码,是不是很神奇呢?这就是 @ModelAttribute的强大之处。
被@ModelAttribute标记的方法,会在所有的@RequestMapping所注解的方法执行前被springmvc调用。
它的运行流程为:
- 执行@ModelAttribute注解修饰的方法,从数据库中取出对象,把对象放入Map中,键为user
- SpringMVC从Map中取出User对象,并把表单的请求参数赋给该User对象对应的属性*
- SpringMVC把上述对象传入目标方法的参数。
【注意】:在@ModelAttribute修饰的方法中,放入到Map时的键名,需要和目标方法入参的类型的第一个字母小写的字符串一致!也就是存入的键名是“user”,入参的类型是User,要相对应。
源码分析的流程:
- 调用@ModelAttribute注解修饰的方法,实际上把@ModelAttribute方法中Map中的数据放在了implicitModel中
- 解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性。
- 创建WebDataBinder对象:
- 确定objectName属性:若传入的attrName属性为"",则objectName为第一个字母小写的类名。【注意】:attrName。若目标方法的POJO属性使用了@ModelAttribute来修饰[ 即public String testModelAttribute(@ModelAttribute (“user” User user)) ],则attrName值为@ModelAttribute的value属性值。
- 确定target属性:在implicitModel中查找attrName对应的属性值,如果存在,ok。如果不存在,则验证当前Handler是否使用了@SessionAttributes来进行修饰,若使用了,则尝试从session中获取attrName所对应的属性值。若session中没有对应的属性值,则抛出异常。若Handler没有使用@SessionAttributes进行修饰,或@SessionAttributes中没有使用value值指定的key和attrName 相匹配,则通过反射创建POJO对象。
- SpringMVC调用doBind方法把表单的请求参数赋给了WebDataBinder 的target对应的属性。
- SpringMVC会把WebDataBinder 的attrName 和target给到implicitModel,进而传到request 域对象中。
- 把WebDataBinder 的target 作为参数传递给目标方法的入参。
总结SpringMVC确定目标方法POJO类型入参的过程:
-
确定一个key:
- 若目标方法的POJO类型的参数没有使用@ModelAttributes作为参数,则key为POJO类名第一个字母的小写
- 若使用了@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值。
-
在implicitModel中查找key,对应的对象,若存在,则作为入参传入。
- 若在@ModelAttribute标记的方法中在Map中保存过,且key和1中确定的key一致,则会获取到。
-
若implicitModel中不存在key对应的对象,则检查当前的Handler是否使用@SessionAttributes注解修饰,若使用了该注解,且@SessionAttributes注解的value属性值中包含了key,则会从HttpSession中获取key所对应的value值,若存在则直接传入到目标方法的入参中,若不存在则抛出异常。
-
若Handler没有标识@SessionAttributes注解或@SessionAttributes注解的value值中不包含key,则会通过创建POJO类型的参数,传入目标方法的参数。
-
SpringMVC会把key和POJO类型的对象保存到 implictModel中,进而会保存到request中。
-
有*@ModelAttribute:标记的方法**,会在每个目标方法执行之前被SpringMVC调用!
-
@ModelAttribute注解也可以来修饰目标方法POJO类型的入参,其value属性值有如下的作用:
- SpringMVC会使用value属性值在implicitModel中查找对应的对象,若存在,则会直接传入到目标方法的入参中。
- SpringMVC会以value为key,POJO类型的对象为value,存入到request域中。
保存的键名为"abc",只要我在目标方法中加入一个@ModelAttribute的注解,注解值也为"abc",即可完成同样的功能。否则的话,保存的键名就必须是我POJO类名首字母小写的名字。
实际上无论我们的入参有没有添加@ModelAttribute注解,SpringMVC都会把POJO类型的入参放入请求域里面。如果没有添加@ModelAttribute注解,那个键就是类名第一个字母小写;如果放了@ModelAttribute注解,就是那个value值(“abc”)。
说了一大推,了解即可,开发中一般不会在这出错的。
视图与视图解析器
无论我们的目标方法返回的是一个String类型,是一个View,还是一个ModelAndView,SpringMVC都会把它装配成一个ModelAndView对象,它包含了逻辑名和模型对象的视图。
它包含两个核心的api,一个是View接口,一个是ViewResolver接口。
SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP,也可能是Excel、JFreeChart等各种表现形式的视图。
对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC的充分解耦。
视图对象由视图解析器负责实例化。由于视图是无状态的,所以它们不会有线程安全的问题。
ViewResolver的作用就是把逻辑视图转为物理视图。
InternalResourceViewResolver
我们常用的视图:InternalResourceView,它是展示jsp的。支持它的解析器叫做:InternalResourceViewResolver。
默认视图就用的是InternalResourceView。
若项目中使用了JSTL,则SpringMVC会自动把视图由InternalResourceView转为JstlView。
只要导入了jstl的jar包,当前的那个view就会自动变成JstlView。
若使用JSTL的fmt标签则需要在SpringMVC的配置文件中配置国际化资源文件。
SpringMVC完成国际化:
i18n.properties:
i18n.username = Username
i18n.password = Password
i18n_zh_CN.properties:
i18n.username=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
i18n_en_US.properties:
i18n.username = Username
i18n.password = Password
配置国际化资源文件:
<!-- 配置国际化资源文件-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
success.jsp中使用fmt标签:
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
success.jsp中展示:
<fmt:message key="i18n.username"></fmt:message>
<fmt:message key="i18n.password"></fmt:message>
我们使用火狐浏览器(我的谷歌设置语言没有生效),在选项里找到语言,修改语言为English(美国),然后重启浏览器,启动tomcat,运行:
可见能够正常显示英文,国际化配置成功。
配置直接转发的页面
如果我们想要直接相应(打开)某一页面,中间不经过handler,我们就需要配置直接转发的页面:
<!-- 配置直接转发的页面-->
<mvc:view-controller path="/success" view-name="success"></mvc:view-controller>
这样一来我们在浏览器地址栏中输入项目启动时的路径再加上/success即可直接进入success.jsp页面。
它可以直接响应转发的页面,而无需再经过Handler的方法。
但是!!!!!!!!!!
这样配置以后,以前所有的请求映射都不能够到达controller了。
如何解决呢?
在实际开发中,通常都需要配置 mvc:annotation-driven 标签
<!-- 配置mvc:annotation-driven标签-->
<mvc:annotation-driven></mvc:annotation-driven>
这样配置以后,所有的请求映射都全部好使了。
自定义视图
为什么要做自定义视图?
因为springmvc在整合其他视图,比如Excel、JFreeChart等,都需要经过自定义视图来完成。
新建一个Java类,用来充当视图:
@Component
public class HelloView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.getWriter().print("hello view,time:"+new Date());
}
}
配置视图解析器BeanNameViewResolver:
<!-- 配置视图解析器BeanNameViewResolver,使用视图的名字来解析视图-->
<!-- 通过order属性来定义视图解析器的优先级,order值越小,优先级越高-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"></property>
</bean>
前面配置的视图解析器InternalResourceViewResolver,它的order属性其实是Integer的最大值,所以它的优先级是最低的。
我这里随便配置一个视图解析器,order随便给定给定一个值就好。
渲染视图时,会优先优先我新配置的这个BeanNameViewResolver,如果它搞不定,再使用InternalResourceViewResolver。
在控制器中写对应的映射方法:
@RequestMapping("/testView")
public String testView(){
System.out.println("testView");
return "helloView";//类名第一个字母小写
}
【这个并未执行成功,原因未知】
如果想要整个Excel视图,则继承抽象类 AbstractExcelView,实现抽象方法buildExcelDocument()即可。
重定向
redirect重定向、forward转发
@RequestMapping("testRedirect")
public String testRedirect(){
System.out.println("testRedirect");
//重定向无法访问到 WEB-INF下的资源
return "redirect:/index.jsp";
}