SpringMVC-数据绑定(日期型)-JSR-303 Validation验证-json处理

InitBinder注解来绑定表单数据

在使用SpingMVC框架的项目中,经常会遇到页面日期时间类型的数据要绑定到控制器的实体,或者控制器需要接受这些数据,如果这类数据类型不做处理的话将无法绑定,而日期型当作字符串类型一点没问题。

这里我们可以使用注解@InitBinder来解决这些问题,这样SpingMVC在绑定表单之前,都会先注册这些编辑器。一般会将这些方法些在BaseController中,需要进行这类转换的控制器只需继承BaseController即可。Spring自己提供了大量的编辑器实现类,诸如CustomDateEditor ,CustomBooleanEditor,CustomNumberEditor等许多,基本上够用。
1.需要在SpringMVC的配置文件加上

如果要格式化并且转换数据类型的话加上服务,该服务为3中配置的格式化数据且转换数据类型的bean

xml 复制代码
 <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

2.有这些日期这些特殊类型的绑定需求的Contoller类上继承BaseController

3.springmvc.xml中配置数据类型转换器bean,要用FormattingConversionServiceFactoryBean

xml 复制代码
 <!-- 配置自定义的数据类型转换器 ,这个bean还要放在上面的mvc:annotation-driven里,即要格式化,还要转换数据类型的话,要用到FormattingConversionServiceFactoryBean
   -->
   <!--<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">  -->
      <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
       <property name="converters">
          <set>
             <bean class="cn.ybzy.sshweb.web.StringConvetUser"></bean>
          </set>
       </property>
   
   </bean>

例:用户类中有一个出生日期

我们来进行增加修改等操作。
User.java(参考自已做的项目sshweb---sts3.9开发工具,在百度网盘中有)

该项目使用spring,springmvc,hibernate框架.

java 复制代码
public class User {
	private int id;
	private String username;
	private String password;
	private Department dpt;   //部门
	// @DateTimeFormat(pattern="yyyy-MM-dd")这个可以替代@InitBinder注解的方法,
	private Date birthday;  //java.util.*
	//GET,SET,TOSTIRNG无参构造等方法
}
-------------------------------User.hbm.xml---------------------------------------
<hibernate-mapping>
    <class name="cn.ybzy.sshweb.model.User" table="t_users">
        <id name="id" type="int">
            <column name="id" />
            <generator class="native" />
        </id>
        <property name="username" type="java.lang.String">
            <column name="username" />
        </property>
        <property name="password" type="java.lang.String">
            <column name="password" />
        </property>
        <many-to-one name="dpt" class="cn.ybzy.sshweb.model.Department" fetch="join">
            <column name="dpt_id" />
        </many-to-one>
        <property name="birthday" type="java.util.Date">
          <column name="birthday"></column>
        </property>
    </class>
</hibernate-mapping>

users.jsp显示所有用户信息,addUserView.jsp,updateUserView.jsp

html 复制代码
<body>
 
   <h4>显示所有用户信息</h4>
   <table border="1">
      <tr>
         <td>id</td>
         <td>用户名</td>
         <td>用户密码</td>
         <td>用户部门</td>
         <td>生日</td>
         <td>对用户进行操作</td>
      </tr>
     <c:forEach var="user" items="${users}"> <!-- users 在控制类中,映射数据据表通过Model注入到了域中 -->
         <tr>
         <td>${user.id }</td>
         <td>${user.username }</td>
         <td>${user.password }</td>
         <td>${user.dpt.dptName }</td>
         <td>${user.birthday }</td>
         <td><a href="${pageContext.request.contextPath}/user/${user.id}">修改</a><a class="delete" href="${pageContext.request.contextPath}/user/${user.id}"> 删除</a></td>
      </tr>
     </c:forEach>
   </table>
   
   <h4>自定义数据类型转换器如下把几个字段封装成一个对象</h4>
   <form action="${pageContext.request.contextPath }/convetUser" method="post">
               字符串形式的User(username,password,dpt_id):id自增<input type="text" name="user"><br>
     <input type="submit" value="提交">
   </form>
</body>
---------------------------------------------addUserView.jsp-----------
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>添加用户</title>
</head>
<body>
   <h4>添加用户</h4>
<form:form action="${pageContext.request.contextPath}/user">
     
     用户名: <form:input path="username"/><br><br>
     密码:  <form:input path="password"/><br><br>
     生日: <form:input path="birthday"/><br><br>
     部门: <form:select path="dpt.id" itemValue="id" itemLabel="dptName" items="${dpts}"></form:select><br><br>
      <input type="submit" value="添加用户"/>
   </form:form>
</body>
</html>
--------------------------------UpdateUserView.jsp-------------------------------------
<body>
   
   <h4>修改用户信息</h4>
   <form:form action="${pageContext.request.contextPath}/user" method="put" modelAttribute="user">
     
              用户号: <form:hidden path="id"/><br><br>
              密码: <form:input path="password"/><br><br>
                 生日: <form:input path="birthday"/><br><br>
              部门: <form:select path="dpt.id" itemValue="id" itemLabel="dptName" items="${dpts}"></form:select><br><br>
      <input type="submit" value="提交修改"/>
   </form:form>
</body>

BaseController.java

java 复制代码
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
/*
 * 该控制类是由其它控制要用到处理日期格式时,用 extends 继之用
 * // @DateTimeFormat(pattern="yyyy-MM-dd")这个可以替代@InitBinder注解的方法,参考User.java的brithday字段的注解
 */
public class BaseController {
	  @InitBinder
	  public void InitBinder(WebDataBinder binder) {
		   /*
		     WebDataBinder是用来绑定请求参数到指定的属性编辑器,由于前台传到controller里的值是String类型的,当往Model里Set这个值的时候,
		            如果set的这个属性是个对象,spring就会找到对应的editor进行编辑转换,然后再set进去。需要在Springmvc.xml中加上<mvc:annotation-driven />,事实上这是一新建项目就加了。
		    */
		    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		    sdf.setLenient(false);   //false:日期格式遵守严格格式:如:2025-07-33会报错,如果true, 会自动转换成2025-08-02
		    binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
	      }

}

UserController.java

java 复制代码
@Controller
public class UserController extends BaseController {
     @Autowired
	  private UserService userService;
	 @Autowired
	  private DepartmentService departmentService;
	    /**
	   * rest风格,传统风格是:getAll方法访问,这里是直接输入用户可获取。
	   * url: xxxx/users 请求方法为get,获取所有用户
	   * @return
	   */
		@RequestMapping(value="/users",method=RequestMethod.GET)
	   public String getAllUsers(Model model) {
		   List<User> users = userService.getAllUsers();
		   model.addAttribute("users",users);
		   return "users";             //返回到users.jsp页面中
		   
	   }
//Rest风格修改一条记录,有两步:一,先获取到一条记录,二,再真正修改记录
		/**
		 *符合rest风格 :   /user/1,获取到第一条的一条记录,get请示.例:在users.jsp列表页中,点击第行的修改,会链接成/user/1,
		 * @param id
		 * @param model
		 * @return
		 */
		@RequestMapping(value="/user/{id}",method=RequestMethod.GET)
		public String updateUserView(@PathVariable("id") Integer id,Model model) {
			if(id!=null) {
				User user=userService.getOne(id);
				model.addAttribute("user",user);
						
			}
			List<Department> dpts = departmentService.getAllDepartment();
			model.addAttribute("dpts",dpts);
			return "updateUserView";  //获取一条记录到修改页
			
		}
		@ModelAttribute
		public void getUser(@RequestParam(value="id",required=false) Integer id,@RequestParam(value="dpt.id",required=false) Integer dptid,Model model)
		{
			if(id!=null && dptid!=null) {
				User user=userService.getOne(id);
				Department dpt = departmentService.getOne(dptid);
				user.setDpt(dpt);
				model.addAttribute("user",user);
			}
		}
		/**
		 * 真正修改rest风格:  /user控制层,PUT请示方式
		 * @param user
		 * @return
		 */
		@RequestMapping(value="/user",method=RequestMethod.PUT)
		public String updateUser(User user) {   //自动获取由updateUserView.jsp提交的信息封装到user对象中
			System.out.println("修改前获取到的一条User对象记录:"+user);  //因为获取到的user对象中username为null和关联部门的部门名为null,所以不可用它修改
			//1。传统方法修改
			/*User user2 = userService.getOne(user.getId());
			Department dpt2=departmentService.getOne(user.getDpt().getId());
			user2.setPassword(user.getPassword());
			user2.setDpt(dpt2);
			System.out.println("user2======"+user2);
			userService.update(user2);*/
			//2.springmvc利用注解@ModeAttribute,另定义一方法,获取完整的User对象,与本方法中的user对角自动补全,去掉null
			userService.update(user);
			return "redirect:/users";  //修改成功,跳转到列表页users.jsp
		}
		/*
		 * REST
		 */
		@RequestMapping(value="/user",method=RequestMethod.GET)
		public String addUserView(Model mode) {
			mode.addAttribute("command",new User());
			mode.addAttribute("dpts", departmentService.getAllDepartment());
			return "addUserView";
		}
		
		@RequestMapping(value="/user",method=RequestMethod.POST)
		public String addUser(User user) {
			userService.add(user);
			System.out.println("增加用户:==="+user);
			
			return "redirect:/users";
		}
			
		/*
		 * rest风格删除一条用户记录
		 * 
		 */
	    @RequestMapping(value="/user/{id}",method=RequestMethod.DELETE)
		public String deleteUser(@PathVariable(value="id") Integer id) {
			if(id!=null) {
				//System.out.println("userService.delete中的id"+id);
				userService.delete(id);
			}
			return "redirect:/users";
		}
		
		
	    
	    @RequestMapping({ "/index", "/", " " }) // 为了测试方例,我只要输入项目名即可转到index.jsp页中
		public String index() {
			return "index";
		}
}

如果没有写相应的BaseController.java,那么就要在增加,修改方法中用字段接收之,但这样就失去了我们的springmvc的便利性,即增加时可能会出现问题 ,如我们输入1999-01-01,会不出现秒时分等。


写了的话话,再继承的话就是如下两图所示

用@DateTimeFormat注解代替@initBinder注解及方法:这个用的更多

JSR-303 Validation验证


一、JSR-303简介

JSR-303只是一个规范,而Spring也没有对这一规范进行实现,那么当我们在SpringMVC中使用JSR-303的时候就需要我们提供一个对JSR-303规范的实现,Hibernate Validator就是实现了这一规范的具体框架。(这个和Hibernate ORM框架没关系啊!)

JSR-303的校验是基于注解的,它内部已经定义好了一系列的限制注解,我们只需要把这些注解标记在需要验证的实体类的属性上或是其对应的get方法上。
二、JSR 303 基本的校验规则

  1. 空检查
    @Null 验证对象是否为null
    @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
    @NotBlank 检查约束字符串是不是Null还有被Trim后的长度是否大于0,只对字符串,且会去掉前后空格.
    @NotEmpty 检查约束元素是否为NULL或者是EMPTY.
  2. Booelan检查
    @AssertTrue 验证 Boolean 对象是否为 true
    @AssertFalse 验证 Boolean 对象是否为 false
  3. 长度检查
    @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
    @Length(min=, max=) Validates that the annotated string is between min and max included.
  4. 日期检查
    @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
    @Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
    @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。
  5. 数值检查
    建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为""时无法转换为int,但可以转换为Stirng为"",Integer为null
    @Min 验证 Number 和 String 对象是否大等于指定的值
    @Max 验证 Number 和 String 对象是否小等于指定的值
    @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
    @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
    @Digits 验证 Number 和 String 的构成是否合法
    @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
    @Range(min=, max=) 被指定的元素必须在合适的范围内
    @Range(min=10000,max=50000,message="range.bean.wage")
    @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
    @CreditCardNumber信用卡验证
    @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
    @ScriptAssert(lang= ,script=, alias=)
    @URL(protocol=,host=, port=,regexp=, flags=)
    三,导包
xml 复制代码
<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>6.0.10.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>6.0.10.Final</version>
</dependency>

例:我们只对添用户作了有效性验证
User.java

java 复制代码
public class User {
	private int id;
	@NotEmpty   //不能為空,也不能為空格,這個用javax.validation.constraints.NotEmpty;
	private String username;
	@NotEmpty
	private String password;
	@NonNull
	private Department dpt;   //部门
	@Past
	//@DateTimeFormat(pattern="yyyy-MM-dd")//这个可以替代@InitBinder注解的方法,
	private Date birthday;
	@Email
	private String email;
	//....get,set,toString()等方法
}

UserController.java---添加用户方法作修改

java 复制代码
@Controller
public class UserController extends BaseController {


	 @Autowired
	  private UserService userService;
	 @Autowired
	  private DepartmentService departmentService;
	  
     @RequestMapping(value="/user",method=RequestMethod.GET)  //REST风格,添加记录,首先用get方式,http://localhost/sshweb/user,到达添加页面
		public String addUserView(Model mode) {
			mode.addAttribute("user",new User());//這是springmvc的表單要的,不然會出錯,當表單提交接的是空的時候
			mode.addAttribute("dpts", departmentService.getAllDepartment());
			return "addUserView";
		}
		
		@RequestMapping(value="/user",method=RequestMethod.POST)  
		public String addUser(@Valid User user,Errors err,Model model) {
			if(err.getErrorCount()>0) {
				System.out.println("某些數據不符合數據有效性校驗!");
				for(FieldError fe:err.getFieldErrors()) {
					System.out.println(fe.getField()+":"+fe.getDefaultMessage());
				}
				model.addAttribute("dpts",departmentService.getAllDepartment());
				return "addUserView";
			}
			userService.add(user);
	        return "redirect:/users";
		}
}

addUserView.jsp

html 复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>添加用户</title>
</head>
<body>
   <h4>添加用户</h4>
<form:form action="${pageContext.request.contextPath}/user" modelAttribute="user" >
     
     用户名: <form:input path="username"/><form:errors path="username"></form:errors><br><br>
   密码:   <form:input path="password"/><form:errors path="password"></form:errors><br><br>
     生日: <form:input path="birthday"/><form:errors path="birthday"></form:errors><br><br>
     郵箱: <form:input path="email"/><br><form:errors path="email"></form:errors><br>
     部门: <form:select path="dpt.id" itemValue="id" itemLabel="dptName" items="${dpts}"></form:select><form:errors path=""></form:errors><br><br>
      <input type="submit" value="添加用户"/>
   </form:form>
</body>
</html>

测试:localhost:8081/sshweb/user---打开添加页面

有些系统默认的错误信息不好用,我们一般都自己写错误提示信息

①用注解,在注解中带message参数

②用国际化配置文件:i18n.properties,注意这个你的自定义的错误信息会覆盖注解上自定义的错误信息

如下图,当日期输出错误时显示的信息很不友好,

处理方法

重启动统,再添加用户信息,故意设置错误,可以看到如下信息

SpringMVC返回json数据的方法

JSON(JavaScript Object Notation,JavaScript对象表示法)是基于ECMAScript的一个子集设计的,是一种开放标准的文件格式和数据交换格式,它易于人阅读和编写,同时也易于机器解析和生成。

一个无序的键值对集合。

以下是一个包含各种类型和结构的JSON示例:

json 复制代码
var s={
"title":"JSON Example",
  "author": {
   "name":"John Doe",
   "age": 35,
   "isVerified":true
  },
"tags":["json", "syntax", "example"],
   "rating": 4.5,
   "isPublished":false,
   "comments": null
}

这种格式,通常与ajax技术一起用,如股票实时更新场景。springmvc返回的是web页面形式的数据,如html格式,xml格式,我们如何返回json格式数据呢。
方式一:使用JSON工具将对象序列化成json,常用工具Jackson,fastjson,gson。然后利用HttpServletResponse,然后获取response.getOutputStream()或response.getWriter(),直接输出。
1. 导包

xml 复制代码
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-core</artifactId>
	<version>2.9.6</version>
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-annotations</artifactId>
	<version>2.9.6</version>
</dependency>
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.6</version>
</dependency>

2.写控制类JsonTestController.java,页面jsonTestViest.jsp
JsonTestController.java

java 复制代码
@Controller
public class JsonTestController {
	@Autowired
	  private UserService userService;
	 @RequestMapping(value="/testjson",method=RequestMethod.POST)
	 public void testjson(HttpServletResponse resp,PrintWriter out) throws JsonProcessingException {
		 resp.setContentType("application/json;charset=utf-8");
		 resp.setHeader("pragma", "no-cache");
		 resp.setHeader("cache-control", "no-cache");
		 List<User> users = userService.getAllUsers();
		 ObjectMapper mapper=new ObjectMapper();
		 String jsonStr=mapper.writeValueAsString(users);
		 System.out.println("===json格式的所有用户信息:"+jsonStr);
		 out.print(jsonStr);   //向浏览器响应了json格式数据(jsonTestView.jsp)
		 
	 }
	 @RequestMapping("/jsontestview") //起进入jsonTestView.jsp页面的作用
	 public String jsonTestView() {
		 return "jsonTestView";
		 
	 }

}

jsonTestVIew.jsp

html 复制代码
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jq/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
   $(function(){
	   //alert("xiong");
	   $("#jsonid").click(function(){
		   var href=this.href;
		   var args= {};
		   //ajax,href:目的地;args:带出去的参数;data:目标方法返回的值
		   $.post(href,args,function(data){
			   for(var i=0;i<data.length;i++)
			   alert(data[i].username);
			   
		   });
		   return false; //a 标签原来的功能失效
	   });
   });

</script>
</head>
<body>
   <a id="jsonid" href="${pageContext.request.contextPath}/testjson">json Test jsp a</a>
</body>
</html>

测试:进入页面,是由控制层的映射到jsonTestVIew.jsp页中,localhot:8081/sshweb/jsontestview回车,进入页面,点击链接,会显示如下图所示内

问题:hibernate懒加载民常,由于没有关闭懒加载,所以当我们一访问用户时,首先打访问用户信息, 而关联的部门信息会延迟加载,但是这里我们是用json时,会因为没有部门信息而发生异常,所以我们要在User.hmb.xml中的多对一字段中,加lazy="false"
方式二:前面的方法,太老土了,我们使用SpringMVC,它给我们提供更傻的方式,只需要用一个注解@ResponseBody,就搞定:

java 复制代码
@Controller
public class JsonTestController {
	@Autowired
	  private UserService userService;
	
	 @ResponseBody
	 @RequestMapping(value="/testjson2",method=RequestMethod.POST)
	 public List<User> testjson2()  {
		 List<User> users = userService.getAllUsers();
		 return users;
		 
	 }
	 
	@RequestMapping("/jsontestview")
	 public String jsonTestView() {
		 return "jsonTestView";
		 
	 }

}

springMVC中框架对HttpMessageConverter的选择原理

在SpringMVC的 Controller层经常会用到@RequestBody和@ResponseBody,通过这两个注解,可以在Controller中直接使用Java对象作为请求参数和返回内容(这里直接将返回内容直接写入到Response对象的body数据区,从而绕过来前面我们讲的视图解析器,不通过视图解析器直接将数据响应给浏览器),而在写入之前必须要转换响应的数据格式,完成这之间转换作用的便是HttpMessageConverter。

相关推荐
Gogo8162 小时前
k8s 跟 nacos 关于服务注册以及服务发现
java·nacos·k8s
yujkss2 小时前
23种设计模式之【工厂方法模式】-核心原理与 Java实践
java·设计模式·工厂方法模式
风语者日志2 小时前
创建者模式:工厂方法模式
java·设计模式
allione2 小时前
Spring-AI简单实践
java·人工智能·spring
爱读源码的大都督3 小时前
2000字源码分析,聊聊Spring的扫描机制底层到底是如何实现的?
java·后端·spring
new_daimond3 小时前
设计模式-责任链模式详解
java·设计模式·责任链模式
Eiceblue3 小时前
使用 C# 操作 Excel 工作表:添加、删除、复制、移动、重命名
服务器·开发语言·c#·excel
维尔切3 小时前
Apache Tomcat 部署与配置
java·linux·运维·tomcat·apache
对不起初见i3 小时前
MyBatis-Plus 全方位深度指南:从入门到精通
java·数据库·mybatis