`
wsjjasper
  • 浏览: 13542 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

5.Spring 里的数据校验

阅读更多

 

Spring 里的数据校验

 

 

相信大家都知道什么是数据校验吧,简单说就是对数据处理前进行验证,包括有效性验证,格式验证,完整性验证,等等。Spirng对此主要提供了两种验证支持:


1.使用spring validator 接口


2.使用JSR-303, Bean Validation API


下面让我们一个一个来看:

使用spring validator 接口:这种方式主要是通过实现Validator接口。假设有个Contact类,里面的first name 属性不能为空, 代码如下

 

package com.apress.prospring3.ch14.domain;

import java.net.URL;
import org.joda.time.DateTime;

public class Contact {
	private String firstName;
	private String lastName;
	private DateTime birthDate;
	private URL personalSite;

	
	// Getter/setter methods omitted
	public String toString() {
		return "First name: " + getFirstName() + " - Last name: "
				+ getLastName() + " - Birth date: " + getBirthDate()
				+ " - Personal site: " + getPersonalSite();
	}
}
  

对此我们来创建一个数据校验类,代码如下:

 

package com.apress.prospring3.ch14.validator;

import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.apress.prospring3.ch14.domain.Contact;

@Component("contactValidator")
public class ContactValidator implements Validator {
	public boolean supports(Class<?> clazz) {
		return Contact.class.equals(clazz);
	}

	public void validate(Object obj, Errors e) {
		ValidationUtils.rejectIfEmpty(e, "firstName", "firstName.empty");
	}
}
  

可以看到这个类实现了接口Validator的2个方法:support()代表这个validator类所支持的类型;validate()方法用来对传入的对象进行校验,返回的结果会存入org.springframework.validation.Errors。可以看到在上面我们使用了spring自带的校验方法ValidationUtils.rejectIfEmpty()来校验属性是否为空,最后的firstName.empty表示错误代码,可以被用于国际化。


接下来需要配置下spring,因为配置了标注,只需让spring自行扫描即可:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	<context:annotation-config />
	<context:component-scan base-package="com.apress.prospring3.ch14.validator" />
</beans>

 

这样一个简单的validator就写完了,下面是测试代码:

 

package com.apress.prospring3.ch14.validator;

import java.util.List;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.apress.prospring3.ch14.domain.Contact;

public class SpringValidatorSample {
	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		ctx.load("classpath:spring-validator-app-context.xml");
		ctx.refresh();
		Contact contact = new Contact();
		contact.setFirstName(null);
		contact.setLastName("Ho");
		Validator contactValidator = ctx.getBean("contactValidator",
				Validator.class);
		BeanPropertyBindingResult result = new BeanPropertyBindingResult(
				contact, "Clarence");
		ValidationUtils.invokeValidator(contactValidator, contact, result);
		List<ObjectError> errors = result.getAllErrors();
		System.out.println("No of validation errors: " + errors.size());
		for (ObjectError error : errors) {
			System.out.println(error.getCode());
		}
	}
}
  

可以看见测试代码里创建的Contact对象的firstName=null。为了捕获校验结果,我们创建了BeanPropertyBindingResult类,并且使用了ValidationUtils.invokeValidator()来手动调用validator。跑完程序结果应该如下:

 

No of validation errors: 1
firstName.empty

 

使用JSR-303, Bean Validation API

 

Spring 3对JSR-303的支持非常棒,Bean Validation API里定义了大量的校验标注,使用起来非常方便。比如@NotNull。

口说无凭,还是通过例子来学习,我们创建一个Customer类:

 

package com.apress.prospring3.ch14.domain;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Customer {
	@NotNull
	@Size(min = 2, max = 60)
	private String firstName;
	private String lastName;
	@NotNull
	private CustomerType customerType;
	private Gender gender;

	// Getter/setter methods omitted
	public boolean isIndividualCustomer() {
		return this.customerType.equals(CustomerType.INDIVIDUAL);
	}
}
  

可以看到,我们在属性上定义了@NotNull,用来表示这个属性不能为空。以及@Size表示这个属性的长度必须为大于2小于60.对了,里面还用到了CustomerType和Gender,代码如下:

 

package com.apress.prospring3.ch14.domain;

public enum CustomerType {
	INDIVIDUAL("I"), CORPORATE("C");
	private String code;

	private CustomerType(String code) {
		this.code = code;
	}

	public String toString() {
		return this.code;
	}
}
 
package com.apress.prospring3.ch14.domain;

public enum Gender {
	MALE("M"), FEMALE("F");
	private String code;

	private Gender(String code) {
		this.code = code;
	}

	public String toString() {
		return this.code;
	}
}
  

老样子,接下来该编写spring配置文件了:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	<context:annotation-config />
	<context:component-scan base-package="com.apress.prospring3.ch14.jsr303.service" />
	<bean id="validator"
		class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans>
  

唯一不同就是我们添加了一个LocalValidatorFactoryBean的validator bean。为了能够调用,我们最好再加个简单的校验service,如下:

 

package com.apress.prospring3.ch14.jsr303.service;

import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.apress.prospring3.ch14.domain.Customer;

@Service("myBeanValidationService")
public class MyBeanValidationService {
	@Autowired
	private Validator validator;

	public Set<ConstraintViolation<Customer>> validateCustomer(Customer customer) {
		return validator.validate(customer);
	}
}
 

在sevice类里我们注入了一个validator,一旦LocalValidatorFactoryBean被定义了,你可以在任何地方注入这个bean。执行校验也很简单,调用validate方法即可,校验的结果将被封装为ConstraintViolation<T>对象集合返回。OK,让我们测试,代码如下:

 

package com.apress.prospring3.ch14.jsr303;

import java.util.HashSet;
import java.util.Set;
import javax.validation.ConstraintViolation;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.apress.prospring3.ch14.domain.Customer;
import com.apress.prospring3.ch14.domain.CustomerType;
import com.apress.prospring3.ch14.jsr303.service.MyBeanValidationService;

public class Jsr303Sample {
	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		ctx.load("classpath:jsr303-app-context.xml");
		ctx.refresh();
		MyBeanValidationService myBeanValidationService = ctx.getBean(
				"myBeanValidationService", MyBeanValidationService.class);
		Customer customer = new Customer();
		// Test basic constraints
		customer.setFirstName("C");
		customer.setLastName("Ho");
		customer.setCustomerType(null);
		customer.setGender(null);
		validateCustomer(customer, myBeanValidationService);
	}

	private static void validateCustomer(Customer customer,
			MyBeanValidationService myBeanValidationService) {
		Set<ConstraintViolation<Customer>> violations = new HashSet<ConstraintViolation<Customer>>();
		violations = myBeanValidationService.validateCustomer(customer);
		listViolations(violations);
	}

	private static void listViolations(
			Set<ConstraintViolation<Customer>> violations) {
		System.out.println("No. of violations: " + violations.size());
		for (ConstraintViolation<Customer> violation : violations) {
			System.out.println("Validation error for property: "
					+ violation.getPropertyPath() + " with value: "
					+ violation.getInvalidValue() + " with error message: "
					+ violation.getMessage());
		}
	}
}
  

测试代码没什么复杂的,可以看见直接调用了validateCustomer()方法,结果正如我们期望的,有2条校验失败记录:

 

No. of violations: 2
Validation error for property: firstName with value: C with error message: size must be
between 2 and 60
Validation error for property: customerType with value: null with error message: may not be
Null
  

创建自定义的Validator

 

虽然JSR-303很给力,但有些比较特别的需求还是得自己来定义,下面我们来展示下如何通过自定义的方法创建Validator。自定义的步骤主要分2步,首先自定义一个validate标注,然后编写这个标注的实现代码。例子来了,这次我们稍微做下变动,提个扭曲的需求。还是刚才的Customer对象,lastName和Gender不能为空。首先自定义标注:

 

package com.apress.prospring3.ch14.jsr303.validator;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Constraint(validatedBy = IndividualCustomerValidator.class)
@Documented
public @interface CheckIndividualCustomer {
	String message() default "Individual customer should have gender and last name defined";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};
}
  

里面有很多标注,我们来解释下。@Target(ElementType.TYPE)表示我们的标注只能用于class这一层级。@Constraint代表这是一个校验用标注,里面的validatedBy标识了哪个实现类来实现校验逻辑,这里是IndividualCustomerValidator。

然后是里面的三个属性:

 

Message定义了返回的错误信息,当然也可以通过标注的方式定义。

Groups用于定义校验用于哪些校验小组。

Payload用于添加一些额外的约束信息(比如校验顺序神马的)


OK,下面是第二步,创建实现:

 

package com.apress.prospring3.ch14.jsr303.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.apress.prospring3.ch14.domain.Customer;

public class IndividualCustomerValidator implements
		ConstraintValidator<CheckIndividualCustomer, Customer> {
	public void initialize(CheckIndividualCustomer constraintAnnotation) {
	}

	public boolean isValid(Customer customer, ConstraintValidatorContext context) {
		boolean result = true;
		if (customer.getCustomerType() != null
				&& (customer.isIndividualCustomer() && (customer.getLastName() == null || customer
						.getGender() == null))) {
			result = false;
		}
		return result;
	}
}
  

可以发现,IndividualCustomerValidator实现了ConstraintValidator<CheckIndividualCustomer, Customer>,也就是说这个校验类是用@CheckIndividualCustomer标注来校验Customer的。方法isValid()用来存放校验逻辑,校验customer对象,并通过返回boolean类型来代表通过还是失败。

好了,到此为止,自定义已经完成了。现在我们只需要在原来的Customer类添加上我们自定义的标注@CheckIndividualCustomer即可了:

 

package com.apress.prospring3.ch14.domain;
// Import statements omitted
@CheckIndividualCustomer
public class Customer {
// Other code omitted
}

测试代码片段:

customer.setFirstName("Clarence");
customer.setLastName("Ho");
customer.setCustomerType(CustomerType.INDIVIDUAL);
customer.setGender(null);
validateCustomer(customer, myBeanValidationService);

  

运行结果:

No. of violations: 1
Validation error for property: with value: com.apress.prospring3.ch14.domain.Customer@d3f136e
with error message: Individual customer should have gender and last name defined	
  

用AssertTrue来自定义Validator

 

除了自定义标注的方式外,JSR-303还能通过@AssertTrue来自定义validator。

例子来了,还是刚才的Customer类,把@CheckIndividualCustomer删除,在isValidIndividualCustomer()方法上添加如下@AssertTrue,别加错地方了。如下代码片段:

 

// Codes omitted
	@AssertTrue(message = "Individual customer should have gender and last name defined")
	private boolean isValidIndividualCustomer() {
		boolean result = true;
		if (getCustomerType() != null
				&& (isIndividualCustomer() && (gender == null || lastName == null)))
			result = false;
		return result;
	}
  

当调用validate()时,系统会检查这个方法是否返回true,如果不是则抛出错误message。运行一下,结果应该是一样的。

 

最后,总结下这么多的自定义校验方式究竟该使用哪种。从易用性来说,@AssertTrue无疑是最简单的,如果你的逻辑够简单,那就用它。但是如果逻辑比较复杂,那就要用自定义标注或者实现spring validation 接口的方法了,仅此而已,希望对各位有用。


下一节: Spring Remoting (-) http://wsjjasper.iteye.com/blog/1574590

分享到:
评论

相关推荐

    从入门到高级实战-深度探索SpringSecurity安全框架视频.zip

    09.Spring Security 用户数据入库 10.Spring Security+Spring Data Jpa 11.RememberMe 功能展示 12.RememberMe 实现原理分析 13.RememberMe 持久化令牌方案 14.RememberMe 二次校验 15.Spring Security+MyBatis 做...

    Spring MVC数据校验.docx

    数据校验在Spring MVC中通常借助于Hibernate Validator实现,它提供了丰富的校验注解,如: - `@NotNull`:检查值是否为null。 - `@NotBlank`:检查字符串是否非空且非空白。 - `@NotEmpty`:检查集合、数组或Map...

    大三(二)springmvc数据校验.zip

    在本资料包 "大三(二)springmvc数据校验.zip" 中,我们主要探讨的是 Spring MVC 中的数据校验机制。 在 Spring MVC 中,数据校验是非常重要的一环,它确保了从客户端提交到服务器的数据是准确无误的。数据校验...

    springMVC数据校验.zip

    SpringMVC JSR 数据校验完整jar包下载,一共六个jar包,classmate-1.0.0.jar,hibernate-validator-5.0.0.final.jar,hibernate-validator-annotation-processor-5.0.1.final.jar,hibernate-validator-cdi-5.0.0....

    Spring-Reference_zh_CN(Spring中文参考手册)

    12.6.5. 事务管理 12.6.6. JpaDialect III. Web 13. Web框架 13.1. 介绍 13.1.1. 与其他web框架的集成 13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. AbstractController 和 ...

    spring 常用的26个包

    14. `org.springframework.web.bind`:处理HTTP参数绑定和数据校验的类。 15. `org.springframework.web.context`:Web应用上下文相关类,扩展了`org.springframework.context`,增加了ServletContext的访问。 16....

    Spring In Action MVC 提交表单(2)-数据绑定和数据校验

    本篇文章将深入探讨Spring MVC中关于数据绑定和数据校验的知识点。 1. **数据绑定** 数据绑定是Spring MVC中一个强大的特性,它允许我们将HTTP请求参数自动映射到控制器方法的参数上。例如,当用户提交一个HTML...

    spring4.1核心包

    5. spring-context-support-4.1.1.RELEASE.jar 这个jar文件包含支持缓存Cache(ehcache)、JCA、JMX、邮件服务(Java Mail、COS Mail)、任务计划Scheduling(Timer、Quartz)方面的类。 UI方面的用来与模板...

    [Pro.Spring.3(2012)].Clarence.Ho.文字版

    该章节介绍了Spring框架如何支持数据类型转换、格式化和校验,帮助开发者在数据输入和输出过程中进行有效的数据处理。读者将学会如何利用Spring的类型转换器、格式化器和校验器来提高数据的准确性和用户体验。 ### ...

    Spring 2.0 开发参考手册

    12.6.5. 事务管理 12.6.6. JpaDialect III. Web 13. Web框架 13.1. 介绍 13.1.1. 与其他web框架的集成 13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. ...

    Spring Boot技术知识点:如何使用@Valid注解来对邮箱字段进行数据校验

    在Spring Boot应用中,数据校验是确保输入数据正确性和安全性的重要步骤。`@Valid`注解是Java Bean Validation框架的一部分,它可以帮助我们方便地在控制器层对请求参数进行验证。Spring Boot集成了Bean Validation...

    Spring Boot技术知识点:如何使用@Validated注解来对邮箱字段进行数据校验

    Spring Boot技术知识点:如何使用@Validated注解来对邮箱字段进行数据校验

    spring chm文档

    12.6.5. 事务管理 12.6.6. JpaDialect III. Web 13. Web框架 13.1. 介绍 13.1.1. 与其他web框架的集成 13.1.2. Spring Web MVC框架的特点 13.2. DispatcherServlet 13.3. 控制器 13.3.1. ...

    Spring中文帮助文档

    3.12.5. 为自动检测的组件提供一个作用域 3.12.6. 用注解提供限定符元数据 3.13. 注册一个LoadTimeWeaver 4. 资源 4.1. 简介 4.2. Resource接口 4.3. 内置 Resource 实现 4.3.1. UrlResource 4.3.2. Class...

    Spring API

    3.12.5. 为自动检测的组件提供一个作用域 3.12.6. 用注解提供限定符元数据 3.13. 注册一个LoadTimeWeaver 4. 资源 4.1. 简介 4.2. Resource接口 4.3. 内置 Resource 实现 4.3.1. UrlResource 4.3.2. Class...

    Spring Mvc(1)Spring MVC 校验

    本文将详细介绍 Spring MVC 中的数据校验机制。 1. **基本校验** Spring MVC 支持 JSR-303/JSR-349 规范,即 Bean Validation,这是一个标准的 Java EE 数据校验框架。通过在字段上添加注解,如 `@NotNull`、`@...

    SSM三大框架整合所需jar包+日志/缓存/验证码/ajax/数据校验全部jar包

    【数据校验】 hibernate-validator-5.0.0.cr2.jar hibernate-validator-annotation-processor-5.0.0.cr2.jar classmate-0.8.0.jar jboss-logging-3.1.1.GA.jar validation-api-1.1.0.cr1.jar 【ajax】 ...

    springmvc数据验证

    在Spring MVC中,数据验证是确保输入数据正确性和安全性的关键环节。 数据验证是任何应用程序中不可或缺的部分,它帮助我们防止非法或无效的数据进入系统,从而保护系统免受潜在的攻击。在Spring MVC中,我们可以...

    Spring Boot 数据校验@Valid+统一异常处理的实现

    主要介绍了Spring Boot 数据校验@Valid+统一异常处理的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Global site tag (gtag.js) - Google Analytics