一、MVC思想
-
MVC思想简介:
MVC并不是java所特有的设计思想,也不是Web应用所特有的思想,它是所有面向对象程序设计语言都应该遵守的规范;MVC思想将一个应用部分分成三个基本部分:Model(模型)、View(视图)和Controller(控制器),这三个部分以最少的耦合协同工作,从而提高应用的可扩展性和可维护性;
-
MVC特点:
-
多视图对应一个模型。按MVC模式,一个模型对应多个视图,可以减少代码的复制和维护量,这样一旦模式改变,易于维护;
-
模型返回的数据和显示的逻辑分离。模型数据可以使用任何的显示技术,例如jsp、velocity模板或直接产生excel文档等;
-
应用被分割为三层,降低了各层之间的耦合,提高了应用的可扩展性;
-
控制层的概念很有效,他把不同的模型和不同的视图组合在一起,完成不同的请求。因此,控制层可以说包含了用户请求权限的概念;
-
MVC更符合软件工程化管理的精神,每一层的组件具有相似的特点,有利于通过工程化和工具化的方法产生管理程序代码;
二、SpringMvc的前端控制器DispatcherServlet
在许多的MVC框架中,都包含一个用于调度控制的Servlet。Spring MVC也提供了一个名为org.springframework.web.servlet.DispatcherServlet的Servlet充当前端控制器,所有的请求驱动都围绕这个DispatcherServlet来分派请求。
DispatcherServlet是一个Servlet(继承自httpServlet基类),在使用时需要把他配置到web.xml文件中,配置信息如下:
<servlet>
<!-- servlet的名称 -->
<servlet-name>springmvc</servlet-name>
<!-- servlet对应的java类 -->
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 当前Servlet的参数信息 -->
<init-param>
<!-- contextConfigLocation是参数名称,该参数的值包含SpringMVC的核心配置文件路径-->
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-config.xml</param-value>
</init-param>
<!-- 在tomcat启动时立即加载servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- servlet映射声明 -->
<servlet-mapping>
<!-- 请求对用的servlet名称 -->
<servlet-name>springmvc</servlet-name>
<!-- 监听当前所有请求 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
三、SpringMvc的后端控制器Handle(两种配置方式)
- 实现Controller接口
- 注解(重点)
DispatcherServlet在Spring当中充当前端控制器的角色,他的核心功能是分发请求,请求会被分发给对应处理的java类,Spring MVC 中称为Handle;Controller接口的实现类只能处理一个单一请求动作,而Spring2.5之后新增的基于注解的控制器可以支持同时处理多个请求动作,并且无需实现任何接口,更加灵活
四、Spring MVC注解
-
@Controller注解
org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器,@Controller用于标记一个类,使用它标记的类就是一个SpringMVC Controller对象,即一个控制器类Spring使用扫描机制查找应用程序中所有基于注解的控制器类。分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解,使用了的方法才是真正处理请求的处理器。 保证spring能找到控制器,需完成两件事:
-
在SpringMVC核心配置文件头文件引入spring-context;
-
使用context:component-scan/开启注解扫描(扫描被@Controller、@Service、@repository、@component等注解的类);
-
@RequestMapping注解
org.springframework.web.bind.annotation.RequestMapping注解类型指示Spring用哪一个类或方法来请求动作,该注解可用于类或方法
@RequestMapping可以用来注释一个控制器类,在这种情况下,所有方法都将映射为相对于类级别的请求,如下:
@Controller
@RequestMapping(value="/user")
public class UserController{
@RequestMapping(value="/register")
public String register(){
return "register";
}
@RequestMapping(value="/login")
public String login(){
return "login";
}
}
由于UserController类中加了value="/user"的@RequestMapping注解,因此所有相关路径都要加上“/user”,此时方法被映射到如下请求:
http://localhost:8080/user/register
http://localhost:8080/user/login
-
- @RequestMapping注解支持的常用属性
-
value属性:是@RequestMapping注解默认属性,若只有唯一属性,则可省略属性名
@RequestMapping(value="/hello")= @RequestMapping("/hello")
-
- @RequestMapping注解支持的常用属性
-
-
-
method属性:表明该方法仅处理的HTTP请求方式
@RequestMapping(value="/hello",method=RequestMethod.POST),只支持post;如果没有指定method属性,则方法可以处理任意HTTP请求方式
-
-
-
- consumes属性:表明处理请求的提交内容类型;
- produces属性:表明返回的内容类型;
- params属性:指定request中必须包含某些参数时,才让该方法处理;
- headers属性:指定request中必须包含某些指定的header值,才让该方法处理请求;
-
- 请求处理方法可以出现的参数类型(SpringMVC默认支持的参数,可出现可不出现,取决于是否用到)_Spring+Mybatis企业应用实战P30
- HttpServletRequest对象
- HttpServletResponse对象
- HttpSession对象
- Model对象
- ModelMap对象
- …
- 请求处理方法可以出现的参数类型(SpringMVC默认支持的参数,可出现可不出现,取决于是否用到)_Spring+Mybatis企业应用实战P30
-
@RequestParam注解
org.springframework.web.bind.annotation.RequestParam注解类型用于将指定的请求参数赋值给方法中的形参
-
- @RequestParam注解支持的常用属性
-
name:String类型;指定请求头绑定的名称
-
value:String类型;name属性的别名
-
required:boolean类型;指定参数是否必须绑定(默认值为true);
-
defaultValue:String类型;如果没有传递参数而是用的默认值
-
- @RequestParam注解支持的常用属性
@RequestMapping(value="/login")
public ModelAndView login(@RequestParam("loginname") String loginname,
@RequestParam("password") String password){
return ......;
}
-
@PathVariable注解
org.springframework.web.bind.annotation.PathVariable注解类型可以非常方便地获得请求URL的中的动态参数;
-
- @PathVariable注解支持属性
- value:String类型,表示绑定的名称,若省略则默认绑定同名参数
- @PathVariable注解支持属性
@RequestMapping(value="/pathVariableTest/{userId}")
public void pathVariableTest(@PathVariable Integer userId)
假如请求的URL为“http://localhost:8080/DataBindingTest/pathVariableTest/1”,则自动将URL中的模板变量{userId}绑定到通过@PathVariable注解的同名参数上,即userId变量将被赋值为1。
-
@RequestHeader注解
org.springframework.web.bind.annotation.RequestHeader注解类型用于将请求的头信息区数据映射到功能处理方法的参数上
-
- @RequestHeader注解支持的属性
- name:String类型;指定请求头绑定的名称
- value:String类型;name属性的别名
- required:boolean类型;指定参数是否必须绑定;
- defaultValue:String类型;如果没有传递参数而是用的默认值
- @RequestHeader注解支持的属性
@RequestMapping(value="/requestHeaderTest")
public void requestHeaderTest(@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value="Accept") String[] accepts)
以上配置自动将请求头“User-Agent”的值赋到userAgent变量上,并将“Accept”请求头的值赋到accepts参数上
-
@CookieValue注解
org.springframework.web.bind.annotation.CookieValue用于将请求的Cookie数据映射到功能处理方法的参数上
-
- @CookieValue注解支持的属性
- name:String类型;指定请求头绑定的名称
- value:String类型;name属性的别名
- required:boolean类型;指定参数是否必须绑定;
- defaultValue:String类型;如果没有传递参数而是用的默认值
- @CookieValue注解支持的属性
@RequestMapping(value="/cookieValueTest")
public void cookieValueTest(@CookieValue(value="JSESSIONID",defaultValue="") String sessionId)
以上配置会自动将JSESSIONID值设置到sessionId参数上,defaultValue表示Cookie中没有JSESSIONID时默认为空
-
@SessionAttributes注解(只能声明类,不能声明方法)
org.springframework.web.bind.annotation.SessionAttributes注解类型允许我们有选择的指定Model中的哪些属性需要转存到HttpSession当中
-
- @SessionAttributes注解支持的属性
-
names属性:String[]类型,Model中属性名称,即存储在HttpSession当中的属性名称;
-
value属性:String[]类型,names属性的别名;
-
types属性:Class<?>[]类型,用来指定放入HttpSession当中的对象类型
-
- @SessionAttributes注解支持的属性
@Controller
//将Model中属性名为user的属性放入HttpSession对象当中
@SessionAttributes("user")
public class SessionAttributesController{
@RequestMapping(value="/login")
public String login(@RequestParam("loginname") String loginname,
@RequestParam("password") String password,
Model model){
//创建User对象,装载用户信息
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
//将user对象添加到Model当中
model.addAttribute("user",user);
return "welcome";
}
}
JSP页面测试以上@SessionAttributes注解:
${requestScope.user.username}//页面显示admin
${sessionScope.user.username}//页面显示admin
结论:User对象被成功设置到了HttpSession对象当中
@SessionAttributes注解如下写法: @SessionAttributes(types={User.class},value=“user”)
还可以设置多个对象到HttpSession当中:
-
@ModelAttribute注解
org.springframe.web.bind.annotation.ModelAttribute注解类型将请求参数绑定到Model对象
-
- @ModelAttribute注解支持的属性(仅一个)
- value属性:String类型,表示绑定的属性名称
- @ModelAttribute注解支持的属性(仅一个)
Notice:被@ModelAttribute注释的方法会在Cobtroller每个方法执行前被执行,因此在一个Controller映射到多个URL时,要谨慎使用!
@ModelAttribute注解使用情况:
-
-
@ModelAttribute(value="")注释返回具体类的方法@ModelAttribute(“loginname”) public String userModel1(@RequestParam(“logginname”) String loginname){ return loginname; }
-
@ModelAttribute注释void返回值的方法
@ModelAttribute public void userModel2(@RequestParam("loginname") String loginname, @RequestParam("password") String password, Model model){ model.addAttribute("loginname",loginname); model.addAttribute("password",password); }
-
@ModelAttribute注释返回具体类的方法
@ModelAttribute public User userModel3(@RequestParam("loginname") String loginname, @RequestParam("password") String password){ return new User(loginname,password); }
-
@ModelAttribute和@RequestMapping同时注释一个方法@RequestMapping(value="/login") @ModelAttribute(value=“username”) public String login4(){ return “admin”; }
-
@ModelAttribute注释一个方法的参数
@ModelAttribute("user") public User userModel5(@RequestParam("loginname") String loginname, @RequestParam("password") String password){ User user = new User(); user.setLoginname(loginname); user.setPassword(password); return user; } @RequestMapping(value="/login5") public String login5(@ModelAttribute("user") User user){ user.setUsername("管理员"); return "result5"; }
不管@ModelAttribute注解在什么情况下使用,model对象中的属性名都是@ModelAttribute的属性value值(有value存在情况下)或返回类型对象名(没有value存在情况下);model对象中的值是方法返回值(有返回值情况下)或自己填充model(没有返回值情况下)
-
五、SpringMVC框架乱码解决方案
在web.xml中添加过滤器:
<filter>
<filter-name>CharacterEncodingFilter</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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-
GET请求中文乱码解决(两种方法)
-
-
修改tomcat配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
-
对参数进行重新编码:String userName = new String(request.getParamter(“userName”).getBytes(“ISO8859-1”),“utf-8”)ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
-
六、SpringMVC框架运转在执行handler(Controller)时,Spring会额外做一些工作:
-
消息转换:将请求信息(如json、xml等数据)封装成一个对象,将对象转换为指定的相应信息;
-
数据转换:对请求信息进行数据转换,如String转换成Integer、Double等;
-
数据格式化:如将字符串转化成格式化数字或格式化日期;
-
数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中;
-
特殊情况(解决日期类型参数转换问题):由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型。所以需要自定义参数绑定。前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定。在springmvc这可以在处理器适配器上自定义Converter进行参数绑定。如果使用mvc:annotation-driven/可以在此标签上进行扩展。
解决方案:
自定义转换器Converter
Public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
Return null;
}
}
配置Converter(在SpringMVC核心配置文件中)——第一种
<!-- 加载注解驱动 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 转换器配置 -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<!-- 自己配置的日期转换器,如果还有其他转换器,可以继续在这里添加bean -->
<bean class="cn.itcast.spring mvc.convert.DateConverter"/>
</set>
</property>
</bean>
第二种配置方式:此方法需要独立配置处理器映射器、适配器,不再使用mvc:annotation-driven/
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 扫描带Controller注解的类 -->
<context:component-scanbase-package="cn.itcast.springmvc.controller"/>
<!-- 转换器配置 -->
<beanid="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<propertyname="converters">
<set>
<beanclass="cn.itcast.springmvc.convert.DateConverter"/>
</set>
</property>
</bean>
<!-- 自定义webBinder -->
<beanid="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<propertyname="conversionService"ref="conversionService"/>
</bean>
<!--注解适配器 -->
<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<propertyname="webBindingInitializer"ref="customBinder"></property>
</bean>
<!-- 注解处理器映射器 -->
<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 加载注解驱动 -->
<!-- <mvc:annotation-driven/> -->
<!-- 视图解析器 -->
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<!-- jsp前缀 -->
<propertyname="prefix"value="/WEB-INF/jsp/"/>
<!-- jsp后缀 -->
<propertyname="suffix"value=".jsp"/>
</bean>
</beans>
七、消息转换:将JSON、XML消息封装成对象,转换为相应的响应信息
-
@RequestBody注解
org.springframework.web.bind.annotation.RequestBody注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到Controller中方法的参数上; 前台页面get或post提交数据时,数据编码格式由请求头的ContentType制定,可分以下情况:
-
application/x-www-form-urlencoded,这种情况的数据@RequestParam、@ModelAttribute也可以处理,@RequestBody也能处理(name1=value1&name2=value2…)
-
multipart/form-data,@RequestBody不能处理这种数据(有file处理时)
-
application/json、application/xml等格式数据,必须用@RequestBody处理
-
转换Json数据
Spring MVC提供了处理JSON格式请求/响应的HttpMessageConverter: MappingJackson2HttpMessageConverter。
利用Jackson开源类包处理JSON格式的请求和响应消息(Spring官方说明,Spring MVC默认使用MappingJackson2HttpMessageConverter转换JSON格式数据,jackson开源类包可将java对象转换成json对象和xml文档,或逆转)
-
- 示例:接收JSON格式的数据
** JSP页面:**
<%@ page language="java" contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>测试接收JSON格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">
$(document).ready(function(){
testRequestBody();
});
function testRequestBody(){
$.ajax("${pageContext.request.contextPath}/json/testRequestBody"//请求url
{dataType:"json", //预期服务器返回的数据类型
type:"post", //请求方式post或get
contentType:"application/json" //发送信息至服务器时的内容编码格式
//发送到服务器的数据
data:JSON.stringify({id:1,name:"Spring MVC企业应用实战"}),
async:true,//默认设置下,所有请求为异步,若false,则发送同步请求
//请求成功后的回掉函数
success:function(data){
console.log(data);
$("#id").html(data.id);
$("#name").html(data.name);
$("#author").html(data.author);
},
//请求出错时调用的函数
error:function(){
alert("数据发送失败");
}
});
}
</script>
</head>
<body>
编号:<span id="id"></apan>
书名:<span id="name"></span>
作者:<span id="author></span>
</body>
</html>
Controller:
@Controller
@RequestMapping("/json")
public class BookController{
private static final Log logger = LogFactory.getLog(BookController.class);
//@RequestBody根据json数据,转换成对应的Object
@RequestMapping(value="/testRequestBody")
public void setJson(@RequestBody Book book,
HttpServletResponse response) throws Exception{
//ObjectMapper类是jackson库的主要类。他提供一些功能将java对象转换成对应的JSON格式数据
ObjectMapper mapper = new ObjectMapper();
//将book对象转换成为json输出
logger.info(mapper.writeValueAsString(book));
book.setAuthor("肖文姬");
response.setContentType("text/html;charset=UTF-8");
//将book对象转换成json写出到客户端
response.getWriter().println(mapper.writeValueAsString(book));
}
}
setJson方法的第一个参数@RequestBody Book book表示,使用@RequestBody注解获取到json数据后,将json数据设置到对应的book对象属性中去,参数response用于输出响应数据到客户端。
Book类:定义三个属性(id、name、author)
** SpringMVC核心配置文件:**
<!-- 注解扫描 -->
<context:component-scan base-package="org.fkit.controller"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 使用默认的Servlet来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 视图解析器 -->
<bean id="viewResoler" class="org.springframework.web.servlet.view.InternalResourceViewResoler">
<!-- 前缀 -->
<property name="prefix">
<value>/WEB-INF/content/</value>
</property>
<!-- 后缀 -->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
mvc:default-servlet-handler/使用默认的Servlet来响应静态文件,因为在web.xml文件中使用了DispatcherServlet截获所有请求url,而引入
-
- 示例:自定义HttpMessageConverter接收JSON格式数据(不使用默认jackson开源类包):说明:使用其他开源类包配置HttpMessageConverter(fastjson)
Controller:
@Controller
@RequestMapping("/json")
public class BookController{
private static final Log logger = LogFactory.getLog(BookController.class);
//@RequestBody根据json数据,转换成对应的Object
@RequestMapping(value="/testRequestBody")
public void setJson(@RequestBody Book book,
HttpServletResponse response) throws Exception{
//JSONObject-lib包是一个beans,collections,maps,java arrays和xml和JSON互相转换的包
//使用JSONObject将book对象转换成为json输出
logger.info(JSONObject.toJSONString(book));
book.setAuthor("肖文姬");
response.setContentType("text/html;charset=UTF-8");
//将book对象转换成json写出到客户端
response.getWriter().println(JSONObject.toJSONString(book));
}
}
Springmvc-config.xml(核心配置文件):
<!-- 注解扫描 -->
<context:component-scan base-package="org.fkit.controller"/>
<!-- 注解驱动,摒弃默认配置HttpMessageConverter,自定义配置HttpMessageConverter -->
<mvc:annotation-driven>
<!-- 设置不使用默认的消息转换器 -->
<mvc:message-converter register-defaults="false">
<!-- 配置Spring的转换器 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
<!-- 配置fastjson中实现HttpMessageConverter接口的转换器 -->
<!-- FastJsonHttpMessageConverter是fastjson中实现了HttpMessageConverter接口的类 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 加入支持的媒体类型;返回contentType -->
<property name="supportedMediaTypes">
<list>
<!-- 这里顺序不能反,一定先写text/html,不然IE下会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converter>
</mvc:annotation-driver>
<!-- 使用默认的Servlet来响应静态文件 -->
<mvc:default-servlet-handler/>
<!-- 视图解析器 -->
<bean id="viewResoler" class="org.springframework.web.servlet.view.InternalResourceViewResoler">
<!-- 前缀 -->
<property name="prefix">
<value>/WEB-INF/content/</value>
</property>
<!-- 后缀 -->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
本项目说明:自定义实现HttpMessageConverter时,关闭默认使用的MappingJackson2HttpMessageConverter消息转换器,不使用jackson开源类包,而是选择了更好的fastjson包,把转换器配置成FastJsonHttpMessageConverter类型,这个类实现了HttpMessageConverter接口;
JSP页面和java文件同上一个项目;
总结:由上两个项目可知:处理JSON格式的开源类包使用jackson和fastjson,只是HttpMessageConverter的实现不同;
-
@ResponseBody注解
org.springframework.web.bind.annotation.ResponseBody注解用于将Controller的方法返回的对象,通过适当的消息转换器转为指定格式后,写入Response对象的body数据区。通常当返回的数据不是html标签页面,而是其他某种格式的数据时(如json、xml等)使用它。
-
- 示例:返回JSON格式数据
Controller:
@Controller
@RequestMapping("/json")
public class BookController{
@RequestMapping(value="/testResponseBody")
//@ResponseBody会将集合数据转换为json格式并将其返回客户端
@ResponseBody
public Object getJson(){
List<Book> list = new ArrayList<Book>();
list.add(new Book(1,"Spring MVC 企业应用之战","肖文姬"));
list.add(new Book(2,"轻量级javaee企业应用之战","李刚"));
return list;
}
}
JSP页面:
<%@ page language="java" contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>测试返回JSON格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">
$(document).ready(function(){
testResponseBody();
});
function testResponseBody(){
$.post("${pageContext.request.contextPath}/json/testResponseBody",null,
function(data){
$.each(data,function(){
var tr = $("<tr align='center'/>");
$("<td/>").html(this.id).appendTo(tr);
$("<td/>").html(this.name).appendTo(tr);
$("<td/>").html(this.author).appendTo(tr);
$("#booktable").append(tr);
})
},"json");
}
</script>
</head>
<body>
<table id="booktable" border="1" style="border-collapse:collapse;">
<tr align="center">
<th>编号</th>
<th>书名</th>
<th>作者</th>
</tr>
</table>
</body>
</html>
-
转换XML格式数据
Spring MVC提供了处理XML格式请求/响应的HttpMessageConverter,如jaxb2RootElementHttpMessageConverter通过JAXB2读写XML消息,并将消息转换到注解@XmlRootElement和@XmlType作用的类中;
Spring官方说明,Spring MVC默认使用了Jaxb2RootElementHttpMessageConverter转换xml格式数据,JAXB(Java Architecture for XML Binding)可以将JSON和XML互转;
JAXB是一项可以根据XML Schema产生Java类的技术,在过程中,JAXB提供了将XML实例文档反向生成Java对象的方法,并能将Java对象的内容重新写到XML文档中。
JAXB常用注解:@XmlRootElement、@XmlElement,等等
-
- 示例:接收XML格式的数据
JSP页面:
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>测试接收XML格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">
$(document).ready(function(){
sendXml();
});
function sendXml(){
var xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><book><id>1</id><name>疯狂Java 讲义</name><author>李刚</author></book>";
$.ajax("${pageContext.request.contextPath}/sendXml",//发送请求的URL字符串
{
type:"post",//请求方式post或get
contentType:"application/xml",//发送信息至服务器时的内容编码类型
//发送到服务器的数据
data:xmlData,
async:true,//默认设置下,异步请求;若false则发送同步请求
});
}
</script>
</head>
<body>
</body>
</html>
Book类:
//@XmlRootElement表示XML文档的根元素
@XmlRootElement
public class Book implements Serializable{
private Integer id;
private String name;
private String author;
public Book(){
super();
}
public Book(Integer id,String name,String author){
this.id=id;
this.name=name;
this.author=author;
}
public Integer getId(){
return id;
}
//该属性作为xml的element
@XmlElement
public void setId(Integer id){
this.id=id;
}
public Integer getName(){
return name;
}
//该属性作为xml的element
@XmlElement
public void setName(String name){
this.name=name;
}
public Integer getAuthor(){
return author;
}
//该属性作为xml的element
@XmlElement
public void setAuthor(String author){
this.author =author ;
}
@Override
public String toString(){
return "Book[id="+id+",name="+name+",author="+author+"]";
}
}
** BookController:**
@Controller
public class BookController{
private static final Log logger = LogFactory.getLog(BookController.class);
//@RequestBody会自动将xml数据绑定到Book对象
@RequestMapping(value="/sendXml,method=RequestMethod.POST")
public void sendXml(@RequestBody Book book){
logger.info(book);
logger.info("接收xml数据成功");
}
//@ResponseBody会将Book自动转换为xml数据返回
@RequestMapping(value="/readXml",method=RequestMethod.POST)
public @ResponseBody Book readXml() throws Exception{
//通过JAXBContext的newInstance()方法,传递一个class就可以获得一个上下文
JAXBContext context = JAXBContext.newInstance(Book.class);
//创建一个Unmarshall对象
Unmarshaller unmar = context.createUnmarshaller();
InputStream is = this.getClass().getResourceAsStream("/book.xml");
//Unmarshall对象的unmarshall方法可以进行xml到Java对象的转换
Book book = (Book)unmar.unmarshall(is);
logger.info(book);
return book;
}
}
springmvc-config.xml核心配置文件中注解驱动mvc:annotation-driven/,默认配置了jaxb2RootElementHttpMessageConverter作为转换器处理xml数据转换
-
- 示例:返回xml格式数据
JSP页面:
<%@ page language="java" contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>测试接收XML格式的数据</title>
<script type="text/javascript" src="js/jquery-1.11.0.min.js"/>
<script type="text/javascript" src="js/json2.js"/>
<script type="text/javascript">
$(document).ready(function(){
readXml();
});
function readXml(){
$.ajax("${pageContext.request.contextPath}/readXml",//发送请求的URL字符串
{
dataType:"text",//预期服务器返回的数据类型
type:"post",//请求方式post或get
async:true,//默认设置下,异步请求;若false则发送同步请求
success:function(xml){
//获得xml数据的id,name,author
var id=$("id",xml).text();
var name=$("name",xml).text();
var author=$("author",xml).text();
var tr = $("<tr align='center'/>");
$("<td/>").html(id).appendTo(tr);
$("<td/>").html(name).appendTo(tr);
$("<td/>").html(author).appendTo(tr);
$("#booktable").append(tr);
},
//请求出错时调用的函数
error:function(){
alert("数据接收失败");
}
});
}
</script>
</head>
<body>
<table id="booktable" border="1" style="border-collapse:collapse;">
<tr align="center">
<th>编号</th>
<th>书名</th>
<th>作者</th>
</tr>
</table>
</body>
</html>
八、数据绑定流程(把请求数据 安全的,没有任何问题的 赋值给处理方法参数)
Spring MVC通过反射机制对目标处理方法的签名进行分析,并将请求信息绑定到处理方法的参数中。数据绑定的核心部件是DataBinder,其运行机制如下:
说明:Spring MVC框架将ServletRequest对象及处理方法的参数对象实例传递给DataBinder,DataBinder调用装配在Spring Web上下文中的ConversionService组件进行数据类型转换、数据格式化工作,并将ServletRequest中的消息填充到参数对象。然后再调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果BindingResult对象。BingdingResult对象包含已完成数据绑定的参数对象,还包含相应的校验错误对象,Spring MVC抽取BindingResult中的参数对象及校验错误对象,将他们赋给处理方法的相应参数。
九、数据转换(两种方式)
Spring MVC在支持新的转换器框架的同时,也支持JavaBean的PropertyEditor
自定义属性编辑器DateEditor:
//自定义属性编辑器
public class DateEditor extends PropertyEditorSupport{
//将传入的字符串数据转换成Date类型
@Override
public void setAsText(String text) throws IllegalArgumentException{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try{
Date date = dateFormat.parse(text);
setValue(date);
}catch(ParseException e){
e.printStackTrace();
}
}
}
UserController:
//在控制器初始化时注册属性编辑器
@InitBinder
public void initBinder(WebDataBinder binder){
//注册自定义编辑器 局部(仅在该控制器作用)
binder.registerCustomEditor(Date.class,new DateEditor());
}
-
- 示例:使用WebBindingInitializer注册全局自定义编辑器转换数据
DateBindingInitializer:
//实现WebBindingInitializer接口
public class DateBindingInitializer implements WebBindingInitializer{
@Override
public void initBinder(WebDateBinder binder,WebRequest request){
//注册自定义编辑器
binder.registerCustomEditor(Date.class,new DateEditor());
}
}
UserController类不需要使用@InitBinder注解的方法,而是在springmvc-config.xml配置文件中配置全局的自定义编辑器
springmvc-config.xml:
<!-- 通过AnnotationMethodHandlerAdapter装配自定义编辑器 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.fkjava.binding.DateBindingInitializer"/>
</property>
</bean>
-
ConversionService接口(spring所有)
-
org.springframework.core.convert.ConversionService是Spring类型转换的核心接口,接口中有4个方法
-
Spring在org.springframework.core.convert.converter包中定义了3种类型的转换器接口,使用时,只需实现其中任意一个接口,并将它作为自定义转换器注册到ConversionServiceFactoryBean当中。
- Converter<S,T>接口:T convert(S source):将S类型的对象转换为T类型的对象(常用)
- ConverterFactory<S,R>接口:说明:将一种类型的对象转换为另一种类型及其子类对象,比如将String转换为Number以及Number子类Integer、double等对象,就需要一系列的Converter,如 StringToInteger、StingToDouble等。此接口的作用就是将相同系列多个Converter封装在一起。
- Converters<S,T> getConverter(Class targetType)S为转换的源类型,R为目标类型的基类,T为R的子类。
- GenericConverter接口:略
-
示例:使用ConversionService转换数据
-
registerForm.jsp页面
<h3>注册页面</h3>
<form action="register" method="post">
<table>
<tr>
<td><label>登录名:</label></td>
<td><input type="text" id="loginname" name="loginname"></td>
</tr>
<tr>
<td><label>生日:</label></td>
<td><input type="text" id="birthday" name="birthday"></td>
</tr>
<tr>
<td><input type="submit" id="submit" value="登陆"></td>
</tr>
</table>
</form>
JavaBean——User
public class User implements Serializable{
private String loginname;
private Date birthday;
public User(){super();}
...省略seter/geter...
}
说明:User用于接收jsp页面传入的loginname和birthday属性,而jsp页面传入的数据都是String类型,分析出须将String转换为Date类型
UserController
@Controller
public class UserController{
private static final Log logger = LogFactory.getLog(UserController.class);
@RequestMapping(value="/{formName}")
public String loginForm(@PathVariable String formName){
//跳转动态页面
return formName;
}
@RequestMapping(value="/register",method=RequestMethod.POST)
public String register(@ModelAttribute User user,Model model){
logger.info(user);
model.addAttribute("user",user);
return "success";
}
}
异常原因:此时,在页面输入数据,点击登陆,由于页面传入的是字符串数据“2017-12-29”,而处理方法中的参数User对象的birthday属性类型是Date,因此此时会出现数据转换异常;
自定义转换器,将传递的字符串转换成Date类型
//实现Converter<S,T>接口
public class StringToDateConverter implements Converter<String,Date>{
//日期类型模版:如yyyy-MM-mm
private String datePattern;
public void setDatePattern(String datePattern){
this.datePattern = datePattern;
}
//Converter<S,T>接口的类型转换方法
@Override
public Date convert(String date){
try{
SimpleDateFormat dateFormat = new SimpleDateFormat(this.datePattern);
//将日期字符串转换为日期返回
return dateFormat.parse(date);
}catch(Exception e){
e.printStackTrace();
System.out.println("日期转换失败");
return null;
}
}
}
在springmvc-config.xml核心配置文件中加入自定义字符转换器
<!-- 装配自定义的类型转换器 -->
<mvc:annotation-driven conversion-service="convertionService"/>
<!-- 自定义的类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="org.fkit.converter.StringToDateConverter" p:datePattern="yyyy-MM-dd"/>
</list>
</property>
</bean>
说明:mvc:annotation-driven/标签还会注册一个默认的ConversionService,即FormattingConversionServiceFactoryBean,以满足大多数类型转换的需求,现在需要注册一个自定义的StringToDateConverter转换类,因此,需要显示定义一个ConverterService覆盖mvc:annotation-driven/ 中的默认实现类,这一步需要通过设置converters属性完成
** success.jsp**
<body>
登录名:${requestScope.user.loginname}//jack
生日:<fmt:formatDate value="${requestScope.user.birthday}" pattern="yyyy年MM月dd日"/>//2016年01月01日
</body>
@InitBinder装配的局部自定义编辑器——>spring的ConversionService装配的自定义转换器 ——>WebBinderInitializer接口装配的全局自定义编辑器
十、数据格式化
Spring使用Converter转换器进行源类型对象到目标类型对象的转换,Spring转换器并不承担输入以及输出信息格式化的工作,在不同的本地化环境中,同一类型的数据还会相应的呈现不同的显示格式。
Spring格式化框架:格式化数据(in)——>真正有用的数据——>格式化数据(out);位于org.springframework.format包,其中最重要的接口是 **Formatter**接口;
| | Converter | Formatter |
| 转换类型 | 任意Object——>任意Object | 任意Object——>String |
| 适用范围 | Web层、service层、dao层 | web层 |
| 建议 |
在Spring MVC的应用程序当中,如果向转换表单中的用户输入,
则建议使用Formatter,而不是Converter
|
-
Formatter格式转换是Spring通过用的,定义在org.springframework.format包中,包中定义的接口有:
-
Printer接口:格式化接口,其中T类型对象根据Locale信息以某种格式进行字符串格式输出
- String print(T object,Locale locale)
-
Parser接口:根绝Locale信息解析字符串到T类型对象。
-
T parse(String text,Locale locale) throws ParseException
-
Formatter接口:格式化接口,继承自Printer和Parser接口,可以完成T类型对象的格式化和解析功能;
-
FormatterRegistrar接口:注册格式化转换器。该接口定义了一个registerFormatters方法,其参数是FormatterRegistry对象,用于注册多个格式化转换器。
-
void registerFormatters(FormatterRegistry registry)
-
AnnotationFormatterFactor y接口:略
-
-
使用Formatter格式化数据
自定义格式化转换器DateFormatter:
//实现Formatter<T>接口
public class DateFormatter implements Formatter<Date>{
//日期类型模板:如:yyyy-MM-dd
private String datePattern;
//日期格式化对象
private SimpleDateFormat dateFormat;
//构造器,通过依赖注入的日期类型创建日期格式化对象
public DateFormatter(String datePattern){
this.datePattern = datePattern;
this.dateFormat = new SimpleDateFormat(datePattern);
}
//显示Formatter<T>的T类型对象
@Override
public String print(Date date,Locale locale){
return dateFormat.format(date);
}
//解析文本字符串,返回一个Formatter<T>的T类型对象
@Override
public Date parse(String source,Locale locale){
try{
return dateFormat.parse(source);
}catch(Exception e){
e.printStackTrace();
throw new IllegalArgumentException();
}
}
}
说明:DateFormatter类实现了org.springframework.format.Formatter接口,实现了接口中的两个方法:parse()和print();日期类型模板会通过配置文件的依赖注入设置
在springmvc-config.xml中加入自定义的格式化转换器:
<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 格式化转换器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="org.fkit.formatter.DateFormatter" c:_0="yyyy-MM-dd"/>
</list>
</property>
</bean>
说明:以上配置使用FormattingConversionServiceFactoryBean对自定义的格式转换器DateFormatter进行了注册。FormattingConversionServiceFactoryBean类有一个属性converters,可以用它注册Converter;有一个属性formatters,可以用它注册Formatter
以上使用实现Formatter接口的方式完成转换数据
Spring提供了很多常用的Formatter实现。对于这些实现类,不需要自定义转换功能,只需要在springmvc-config.xml配置文件中配置好就能用:
-
在org.springframework.format.datetime包中
-
DateFormatter:用于时间对象格式化
-
在org.springframework.format.number包中
-
NumberFormatter:用于数字类型对象格式化
-
CurrencyFormatter:用于货币类型对象格式化
-
PercentFormatter:用于百分数数字类型对象格式化
例如:使用org.springframework.format.datetime包中的DateFormatter完成字符串到日期对象的转化,如下配置即可:
在springmvc-config.xml中加入自定义的格式化转换器:
<!-- 装配自定义格式化转换器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 格式化转换器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="org.springframework.format.datetime.DateFormatter" p:pattern="yyyy-MM-dd"/>
</list>
</property>
</bean>
Spring为开发者提供了注解驱动的属性对象格式化功能:在Bean属性中设置、Spring MVC处理方法参数绑定数据、模型数据输出时自动通过注解应用格式化的功能
在org.springframework.format.annotation包下面定义了两个格式化的注解类型:
-
- DateTimeFormat
@DateTimeFormat注解可以对java.util.Date、java.util.Calendar等时间类型的属性进行标注;它支持一下几个互斥的属性。具体说明如下:
-
- iso。类型为DateTimeFormat.ISO。以下几个常用可选值:
- DateTimeFormat.ISO.DATE:格式为yyyy-MM-dd
- DateTimeFormat.ISO.DATE_TIME:格式为yyyy-MM-dd hh:mm:ss.SSSZ
- DateTimeFormat.ISO.TIME:格式为hh:mm:ss.SSSZ
- DateTimeFormat.ISO.NONE:表示不使用ISO格式的时间
- pattern。类型为String,使用自定义的时间格式化字符串,如“yyyy-MM-dd hh:mm:ss”
- style。
- iso。类型为DateTimeFormat.ISO。以下几个常用可选值:
-
- NumberFormat
@NumberFormat可对类似数字类型的属性进行标记,他有两个互斥属性,具体如下:
-
-
- pattern。类型为String,使用自定义的数字格式化串,如“##,###。##”
- style。类型为NumberFormat.Style,以下常用可选值:
-
-
- NumberFormat.CURRENCY:货币类型
- NumberFormat.NUMBER:正常数字类型
- NumberFormat.PERCENT:百分数类型
-
-
testForm.jsp:
<h3>测试表单数据格式化</h3>
<form action="test" method="post">
<table>
<tr>
<td><label>日期类型:</label></tr>
<td><input type="text" id="birthday" name="birthday"/></td>
</tr>
<tr>
<td><label>整数类型:</label></tr>
<td><input type="text" id="total" name="total"/></td>
</tr>
<tr>
<td><label>百分数类型:</label></tr>
<td><input type="text" id="discount" name="discount"/></td>
</tr>
<tr>
<td><label>货币类型:</label></tr>
<td><input type="text" id="money" name="money"/></td>
</tr>
<tr>
<td><input type="submit" value="提交"/></td>
</tr>
</table>
</form>
User——注解:
public class User implements Serializable{
//日期类型
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday;
//正常数字类型
@NumberFormat(style=Style.NUMBER,pattern="#,###")
private int total;
//百分数类型
@NumberFormat(style=Style.PERCENT)
private double discount;
//货币类型
//@NumberFormat(style=Style.CURRENCY)
private double money;
.....省略set/get方法
}
说明:User类的多个属性使用了DateTimeFormat和NumberFormat注解,用于将页面传递的String转换成对应的格式化数据;
UserController:
@Controller
public class FormatterController{
private static final Log logger = LogFactory.getLog(FormatterController.class);
@RequestMapping(value="/{formName}")
public String loginForm(@PathVariable String formName){
//动态跳转页面
return formName;
}
@RequestMapping(value="/test" method=RequestMethod.POST)
public String test(@ModelAttribute User user,Model model){
logger.info(user);
model.addAttribute("user",user);
return "success";
}
}
success.jsp页面:
<%@ taglib prefix="form" url="http://www.springframework.org/tags/form" %>
<h3>测试表单数据格式化</h3>
<form:form modelAttribute="user" method="post" action="" />
<table>
<tr>
<td>日期类型:</td>
<td><form:input path="birthday"/></td>
</tr>
<tr>
<td>整数类型:</td>
<td><form:input path="total"/></td>
</tr>
<tr>
<td>百分数类型:</td>
<td><form:input path="discount"/></td>
</tr>
<tr>
<td>货币类型:</td>
<td><form:input path="money"/></td>
</tr>
</table>
注意:如果希望在视图页面中将模型属性数据以格式化的方式进行渲染,则需使用Spring的页面标签显示模型数据。所以在success.jsp中受用了<form:form modelAttribute=“user”>标签,并且绑定了user对象
springmvc-config.xml: mvc:annotation-driven/
说明:默认配置注解驱动,则也是默认创建了ConversionService实例FotrmattingConversionServiceFactoryBean,这样就可以支持注解驱动的格式化功能了
十一、数据校验
数据校验是所有web应用必须处理的问题,因为数据异常输入,轻则导致系统中断,重则导致系统崩溃;
输入校验分为客户端校验和服务器端校验,客户端校验主要是过滤正常用户的“误操作”,通常通过“JavaScript”代码完成,降低服务器的负载;服务器端校验是整个应用阻止非法数据的最后防线,主要通过在应用编程实现;
-
Spring MVC提供的数据校验功能:
- Spring自带的Validation校验框架;
- JSR 303(Java验证规范);
-
Spring的Validation校验框架
- Spring的校验框架在org.springframework.vlidation包中,重要的接口和类如下:
- Validator(最重要的接口)。两个方法:
- boolean supports(Class<?> clazz):该校验器能够对clazz类型的对象进行校验;
- void validate(Object target,Error error):对目标类target校验,并将校验错误记录在error中;
- Errors:Spring用来存放错误信息的接口
- Spring MVC框架在将请求数据绑定到入参对象后,就会调用校验框架实施校验,校验结果保存在处理方法的入参对象之后的对象当中。这个保存校验结果的参数对象必须是Errors或者BindingResult类型。一个Errors对象中包含了一系列的FieldError和ObjectError对象。FieldError表示与被校验的对象中的某个属性相关的一个错误。BindingResult扩展了Errors接口,同时可以获取数据绑定结果对象的信息
- ValidationUtils
- Spring提供的一个关于校验的工具类。他提供了多个给Errors对象保存错误的方法;
- LocalValidatorFactoryBean
- 位于org.springframework.validation.beanvalidation包中,该类既实现了Spring的Validator接口,也实现了JSR 303的Validator接口。只要在Spring容器中定义一个LocalValidatorFactoryBean即可将其注入到需要数据校验的Bean中
- 注意: mvc:annotation-driven/会默认装配好一个LocalValidatorFactoryBean所以在实际开发中不需要手动配置LocalValidatorFactoryBean。需要注意的是,Spring 本身没有提供JSR 303的实现,如果需要使用JSR 303完成验证,则必须将 JSR 303的实现(注入Hibernate Validator)jar文件加入到应用程序的类路径下,这样Spring会自动加载并装配好JSR 303 的实现。
- Validator(最重要的接口)。两个方法:
- Spring的校验框架在org.springframework.vlidation包中,重要的接口和类如下:
** 示例:测试Spring的Validation校验**
** loginForm.jsp:**
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head?
<meta http-equiv="Content-Type" content="text/html;charset="UTF-8">
<title>测试Validator接口验证</title>
</head>
<body>
<h3>登录页面</h3>
<!-- 绑定user -->
<form:form modelAttribute="user" method="post" action="login">
<table>
<tr>
<td>登录名:</td>
<td><form:input path="loginname"/></td>
<!-- 显示loginname属性的错误信息 -->
<td><form:errors path="loginname" cssStyle="color:red"/></td>
</tr>
<tr>
<td>密码:</td>
<td><form:input path="password"/></td>
<!-- 显示password属性的错误信息 -->
<td><form:errors path="password" cssStyle="color:red"/></td>
</tr>
<tr>
<td><input type="submit" value="提交" /></td>
</tr>
</table>
</form:form>
页面用form:errors标签显示属性的错误信息
User:
public class User implements Serializable{
private String loginname;
private String password;
//省略set/get方法
}
UserValidator:
//实现Spring的Validator接口
@Repository("userValidator")
public class UserValidator implements Validator{
//该校验器能够对clazz类型的对象进行校验
@Override
public boolean supports(Class<?> clazz){
//User指定的Class参数所表示的类或接口是否相同,或是否是其超类或超接口
return User.class.isAssignableFrom(clazz);
}
//对目标类target进行校验,并将校验错误记录在errors当中
@Override
public void validate(Object target,Errors errors){
/**
使用ValidationUtils中的一个静态方法rejectIfEmpty()来对loginname属性进行校验,
假若‘loginname’属性是null或者空字符串的话,就拒绝验证通过。
*/
ValidationUtils.rejectIfEmpty(errors,"loginname",null,"登录名不能为空");
ValidationUtils.rejectIfEmpty(errors,"password",null,"密码不能为空");
User user = (User)target;
if(user.getLoginname().length()>10){
//使用Errors的rejectValue方法验证
errors.rejectValue("loginname",null,"用户名不能超过10个字符");
}
if(user.getPassword() !=null
&& !user.getPassword().equals("")
&& user.getPassword().length()<6){
errors.rejectValue("password",null."密码不能小于6位");
}
}
}
UserController:
//注入UserValidator对象
@Autowired
@Qualifier("userValidator")
private UserValidator userValidator;
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(@ModelAttribute User user,Model model,Errors errors){
logger.info(user);
model.addAttribute("user",user);
//调用userValidator的验证方法
userValidator。validate(user,errors);
//如果验证不通过跳转到loginForm视图
if(errors.hasErrors()){
return "loginForm".
}
return "success";
}
测试结果:如果输入不符合规范的信息,则会在输入框后面进行提示,并且登陆失败;
JSR 303校验简介:
-
- JSR 303是为Bean数据合法性校验所提供的一个标准,叫做Bean Validation。2009年Java EE 6发布,Bean Validation作为一个重要特性被包含其中,用于对Java Bean中的字段值进行验证。
- 在应用程序中,通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标注的验证接口对Bean进行验证。Bean Validation是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回;
- JSR 303是一个规范,核心接口是javax.validation.Validator.JSR 303目前有两个实现,第一个实现是Hibernate Validator;第二个是Apache bval。
JSR 303中定义了一套可标注在成员变量、属性方法上的校验注解,Hibernate Validator扩充了JSR 303;如下:
示例:测试JSR 303校验(HibernateValidator)
registerForm.jsp:
<%@ taglib prefix= "form" uri= "http://www.springframework.org/tags/form" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>测试JSR 303</title>
</head>
<body>
<h3>注册页面</h3>
<form:form modelAttribute="user" method="post" action="login" >
<table>
<tr>
<td>登录名:</td>
<td><form:input path="loginname"/></td>
<td><form:errors path="loginname" cssStyle="color:red"/></td>
</tr>
<tr>
<td>密码:</td>
<td><form:input path="password"/></td>
<td><form:errors path="password" cssStyle="color:red"/></td>
</tr>
<tr>
<td>用户名:</td>
<td><form:input path="username"/></td>
<td><form:errors path="username " cssStyle="color:red"/></td>
</tr>
<tr>
<td>年龄:</td>
<td><form:input path="age"/></td>
<td><form:errors path="age" cssStyle="color:red"/></td>
</tr>
<tr>
<td>邮箱:</td>
<td><form:input path="email"/></td>
<td><form:errors path="email" cssStyle="color:red"/></td>
</tr>
<tr>
<td>生日:</td>
<td><form:input path="birthday"/></td>
<td><form:errors path="birthday" cssStyle="color:red"/></td>
</tr>
<tr>
<td>电话:</td>
<td><form:input path="phone"/></td>
<td><form:errors path="phone" cssStyle="color:red"/></td>
</tr>
<tr>
<td><input type="submit" value="提交"/></td>
</tr>
</table>
</form:from>
</body>
</html>
说明:注册页面,用户提交注册信息,后台使用JSR 303进行验证这些注册信息
User:
public class User implements Serializable{
@NotBlank(message="登录名不能为空")
private String loginname;
@NotBlank(message="密码不能为空")
@Length(min=6,max=8,message="密码长度必须在6位到8位之间")
private String password;
@NotBlank(message="用户名不能为空")
private String username;
@Range(min=15,max=60,message="年龄必须在15岁到60岁之间")
private int age;
@Email(message="必须是合法的邮箱地址")
private String email;
@DateTimeFormat(pattern="yyyy-MM-dd")
@Past(message="生日必须是一个过去的日期")
private Date birthday;
@Pattern(regexp="[1][3,8][3,6,9][0-9]{8}",message="无效的电话号码")
private String phone;
//省略set/get方法
}
说明:User类使用了Hibernate Validator的注解对前台提交的数据进行验证
@Controller
public class UserController{
@RequestMapping(value="/{formName}")
public String loginname(@PathVariable String formName,Model model){
User user = new User();
model.addAttribute("user",user);
//动态跳转页面
return formName;
}
//数据校验使用@Valid,后面跟着Errors对象保存校验信息
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(@Valid @ModelAttribute User user,Errors errors,Model model){
logger.info(user);
if(errors.hasErrors()){
return "registerForm";
}
model.addAttribute("user",user);
return "success";
}
}
说明:在UserController中使用@Valid注解对提交的数据进行校验,后面跟着Errors对象保存校验信息。如果errors中有错误信息,则返回registerForm页面,验证通过则跳转到success页面
success.jsp:
<%@ taglib uri="http.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset="UTF-8">
</head>
<body>
<h3>测试JSR 303</h3>
登录名:${requesyScope.user.loginname}
密码:${requestScope.user.password}
用户名:${requestScope.user.username}
年龄:${requestScope.user.age}
邮箱:${requestScope.user.email}
生日:<fmt:formatDate value = "${requestScope.user.birthday}" pattern="yyyy年MM月dd日"/>
电话:${requestScope.user.phone}
</body>
</html>
由于mvn:annotation-driven/会默认装配一个LocalValidatorFactoryBean因此springmvc-config.xml中不用进行特殊配置。
十二、Spring MVC的拦截器
-
Interceptor拦截器:拦截用户请求并处理。例如权限验证等
-
Spring MVC的Interceptor是可插拔式的设计。需要时配置文件配上,否则去掉;
-
Spring MVC的Interceptor拦截请求是通过实现HandlerInterceptor接口来完成的;所以在Spring MVC中定义一个Interceptor只需要实现HandlerInterceptor接口或继承HandlerInterceptorAdapter;
-
HandlerInterceptor接口中定义三个方法,Spring MVC就是通过这三个方法来对用户的请求进行拦截处理的:
示例:拦截器实现用户权限验证
功能描述:用户只有登陆后才能访问网站首页,如果没有登陆就直接访问网站首页,拦截器会拦截,并将请求重新转发到登陆页面,同时提醒用户需要先登录再访问网站;
loginForm.jsp:
<h3>登录界面</h3>
<form action="login" method="post">
<!--提示信息 -->
<font color="red">${requestScope.message}</font>
<table>
<tr>
<td><label>登录名:</label></td>
<td><input type="text" id="loginname" name="loginname"/><td>
</tr>
<tr>
<td><label>密码:</label></td>
<td><input type="password" id="password" name="password"/><td>
</tr>
<tr>
<td><input type="submit" value="登陆"/></td>
</tr>
</table>
</form>
UserController:
@Controller
public class UserController{
/**
处理/login请求
*/
@RequestMapping(value="/login")
public ModelAndView login(String loginname,String password,
ModelAndView mv,HttpSession session){
//模拟数据库根据登录名和密码查找用户,判断用户是否登陆
if(loginname != null && loginname.equals("fkit")
&& password != null && password.equals("123456")){
//模拟创建用户
User user = new User();
user.setPassword(password);
user.setLoginname(loginname);
user.setUsername("管理员");
//登陆成功,将user对象设置到HttpSession作用域
session.setAttribute("user",user);
//转发到main请求
mv.setViewName("redirect:main");
}else{
//登录失败,设置失败信息,并跳转页面到登录界面
mv.addObject("message","登录名或密码错误,请重新输入!");
mv.setViewName("loginForm");
}
return mv;
}
}
BookController:
@Controller
public class BookController{
/**
处理/main请求
*/
@RequestMapping(value="/main")
public String main(Model model){
//根据数据库获得所有图书的集合
List<Book> bookList = new ArrayList<Book>();
bookList.add(new Book("java.jpg","疯狂java讲义","李刚",74.3));
bookList.add(new Book("java.jpg","疯狂java讲义","李刚",74.3));
bookList.add(new Book("java.jpg","疯狂java讲义","李刚",74.3));
//将图书集合添加到model中
model.addAttribute("bookList",bookList);
//跳转到main页面
return "main";
}
}
main.jsp:
<h3>欢迎【${sessionScope.user.username}】访问</h3>
<table border="1">
<tr>
<th>封面</th><th>书名</th><th>作者</th><th>价格</th>
</tr>
<c:forEach items="${requestScope.bookList}" var="book">
<tr>
<td><img src="images/${book.image}" height="60"></td>
<td>${book.name}</td>
<td>${book.author}</td>
<td>${book.price}</td>
</tr>
</c:forEach>
</table>
设计Interceptor验证用户是否登陆,如果没有登录,不可以访问除登陆界面的其他界面
AuthorizationInterceptor:
/**
拦截器必须实现HandlerInterceptor接口
*/
public class AuthorizationInterceptor implements HandlerInterceptor{
//不拦截"/loginForm"和"/login"请求
private static final String[] IGNORE_URI = {"/loginForm","/login"};
/**
该方法将在整个请求完成之后执行,主要作用于清理资源,
该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行。
*/
@Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
Object handler,Exception e){
System.out.println("AuthorizationInterceptor afterCompletion -->");
}
/**
该方法将在Controller的方法调用之后执行,方法中可以对ModelAndView进行操作
该方法只能在当前Interceptor的preHandler方法的返回值为true时才会执行
*/
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,
Object handler,ModelAndView mv){
System.out.println("AuthorizationInterceptor postHandle -->");
}
/**
preHandle方法是进行处理器拦截用的,该方法将在Controller处理之前执行,
该方法的返回值为true拦截器才会继续往下执行,该方法的返回值为false时整个请求终止
*/
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
Object handler) throws Exception{
System.out.println("AuthorizationInterceptor preHandle -->");
//flag变量用于判断用户是否登陆,默认为false
boolean flag = false;
//获取请求的路径进行判断
String servletPath = request.getServletPath();
//判断请求是否需要拦截
for(String s:IGNORE_URI){
if(servletPath.contains(s)){
flag = true;
break;
}
}
//拦截请求
if(!flag){
//1.获取session中的用户
User user = (User)request.getSession.getAttribute("user");
//2.判断用户是否已经登陆
if(user == unll){
//如果用户没有登陆,则设置提示信息,跳转到登录界面
System.out.println("AuthorizationInterceptor拦截请求:");
request.setAttribute("message","请先登录网站");
request.getRequestDispatcher("loginForm").forward(request,response);
}else{
//如果用户已经登陆,则验证通过,放行
System.out.println("AuthorizationInterceptor放行请求:");
flag = true;
}
}
return flag;
}
}
在springmvc-config.xml中配置拦截器:
<!-- Spring MVC拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有的请求 -->
<mvc:mapping path="/*"/>
<!-- 使用bean定义一个Interceptor -->
<bean class="org.fkit.interceptor.AuthorizationInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>