Bean Validation完结篇:你必须关注的边边角角(约束级联、自定义约束、自定义校验器、国际化失败消息...)【享学Spring】(中)

简介: Bean Validation完结篇:你必须关注的边边角角(约束级联、自定义约束、自定义校验器、国际化失败消息...)【享学Spring】(中)

了解了这些之后,想自定义失败消息message,就简直不要太简单了好不好,例子如下:

@Min(value = 10, message = "{com.fsx.my.min.message}")
    private Integer age;

写一个资源属性文件,命名为ValidationMessages.properties放在类路径下,文件内容如下:

// 此处可以使用占位符{value}读取注解对应属性上的值
com.fsx.my.min.message=[自定义消息]最小值必须是{value}


运行测试用例,打印输出如下失败消息:

age [自定义消息]最小值必须是10: -1


完美(自定义的生效了)


说明:因为我的平台是中文的,因此文件命名为ValidationMessages_zh_CN.properties的效果也是一样的,因为Hibernate Validation提供了Locale国际化的支持


Spring环境下自定义国际化消息


上面使用的是Hibernate Validation内置的对国际化的支持,由于大部分情况下我们都是在Spring环境下使用数据校验,因此有必要讲讲Spring加持情况下的国家化做法。我们知道Spring MVC是有专门做国际化的模块的,因此国际化这个动作当然也是可以交给Spring自己来做的,此处我也给一个Demo吧:


说明:即使在Spring环境下,你照常使用Hibernate Validation的国际化方案,依旧是没有问题的~


1、向容器内配置验证器(含有自己的国际化资源文件):


@Configuration
public class RootConfig {
    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        // 使用Spring加载国际化资源文件
        //ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        //messageSource.setBasename("MyValidationMsg");
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("MyValidationMsg"); // 注意此处名字就随意啦,毕竟交给spring了`.properties`就不需要了哦
        messageSource.setCacheSeconds(120); // 缓存时长
        // messageSource.setFileEncodings(); // 设置编码 UTF-8
        localValidatorFactoryBean.setValidationMessageSource(messageSource);
        return localValidatorFactoryBean;
    }
}


运行单测:


@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RootConfig.class})
public class TestSpringBean {
    @Autowired
    private LocalValidatorFactoryBean localValidatorFactoryBean;
    @Test
    public void test1() {
        Person person = new Person();
        person.setAge(-5);
        Validator validator = localValidatorFactoryBean.getValidator();
        Set<ConstraintViolation<Person>> result = validator.validate(person);
        // 输出错误消息
        result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
                .forEach(System.out::println);
    }
}


打印校验失败消息如下(完美生效):

age [自定义消息]最小值必须是10: -5


说明:若是Spring应用,如果你还需要考虑国际化的话,我个人建议使用Spring来处理国际化,而不是Hibernate~(有种Spring的脑残粉感觉有木有,当然这不是强制的)


Spring MVC中如何自定义全局校验器Validator


Spring MVC默认配置的(使用的)校验器的执行代码如下:

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {\
  ...
  @Bean
  public Validator mvcValidator() {
    Validator validator = getValidator();
    if (validator == null) {
      if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
        Class<?> clazz;
        try {
          String className = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean";
          clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader());
        } catch (ClassNotFoundException | LinkageError ex) {
          throw new BeanInitializationException("Failed to resolve default validator class", ex);
        }
        validator = (Validator) BeanUtils.instantiateClass(clazz);
      } else {
        validator = new NoOpValidator();
      }
    }
    return validator;
  }
  ...
}


代码很简答,就不逐行解释了。我归纳如下:


  1. Spring MVC中校验要想自动生效,必须导入了javax.validation.Validator才行,否则是new NoOpValidator()它木有校验行为
  2. Spring MVC最终默认使用的校验器是OptionalValidatorFactoryBean(LocalValidatorFactoryBean的子类)~
  3. 显然,要想校验生效@EnableWebMvc也是必须的(SpringBoot环境另说)


那如何自定义一个全局的校验器呢?最佳做法如下:


@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
  ...
    @Override
    public Validator getValidator() {
        // return "global" validator
        return new LocalValidatorFactoryBean();
    }
  ...
}


当然,你还可以使用@InitBinder来设置,甚至可以细粒度设置到只与当前Controller绑定的校验器都是可行的(比如你可以使用自定校验器实现各种私有的、比较复杂的逻辑判断)


说到这自定义Validator了,此处再说一下自定义MessageCodesResolver消息状态码解析器吧。

MessageCodesResolver:Spring进行数据校验失败时,会通过MessageCodesResolver生成错误码放入Errors错误对象。Spring默认使用的逻辑完全同上~


public interface MessageCodesResolver {
  String[] resolveMessageCodes(String errorCode, String objectName);
  String[] resolveMessageCodes(String errorCode, String objectName, String field, @Nullable Class<?> fieldType);
}


它的唯一实现类是:DefaultMessageCodesResolver。它的两个方法做的事情比较简单,效果如下(注意:此处所谓的错误码就是这些字符串):


image.png


需要注意的是:这两个组件虽然都是在Spring里的,但是如果你向如上方式来提供,它就单属于Spring MVC容器的(SpringBoot另说)


自定义约束


JSR和Hibernate支持的约束条件已经足够强大,应该是能满足我们绝大部分情况下的基础验证的。如果还是不能满足业务需求,我们还可以自定义约束,也很简单一事。


JSR和Hibernate提供的约束注解解释说明:【小家Java】深入了解数据校验(Bean Validation):从深处去掌握@Valid的作用(级联校验)以及常用约束注解的解释说明


自定义一个约束分如下三步(说是2步也成):


  1. 自定义一个约束注解
  2. 实现一个校验器(实现接口:ConstraintValidator)
  3. 定义默认的校验错误信息


给个Demo:此处以自定义一个约束注解来校验集合的长度范围:@CollectionRange


1、自定义注解(此处使用得比较高级)

@Documented
@Constraint(validatedBy = {})
@SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(value = CollectionRange.List.class)
@Size // 校验动作委托给Size去完成  所以它自己并不需要校验器~~~
@ReportAsSingleViolation // 组合组件一般建议标注上
public @interface CollectionRange {
    // 三个必备的基本属性
    String message() default "{com.fsx.my.collection.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    // 自定义属性  @OverridesAttribute这里有点方法覆盖的意思~~~~~~ 子类属性覆盖父类的默认值嘛
    @OverridesAttribute(constraint = Size.class, name = "min")
    int min() default 0;
    @OverridesAttribute(constraint = Size.class, name = "max")
    int max() default Integer.MAX_VALUE;
    // 重复注解
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        CollectionRange[] value();
    }
}


2、实现一个校验器

此例用不着(下面会有)


3、自定义错误消息

当然,你可以写死在message属性上,但是本处使用配置的方式来~


com.fsx.my.collection.message=[自定义消息]你的集合的长度必须介于{min}和{max}之间(包含边界值)



相关文章
|
1月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
129 26
|
3月前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
3月前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
3月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
3月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
224 14
|
3月前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
84 6
|
3月前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
217 4
|
3月前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
56 1
|
Java Spring 测试技术
Spring Boot(15)——自动配置Validation
自动配置Validation 当应用中的Classpath下存在javax.validation的实现时,Spring Boot的org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration将会自动配置用于validate的LocalValidatorFactoryBean,前提是在用户没有自己定义javax.validation.Validator类型的bean的情况下,LocalValidatorFactoryBean也是实现了javax.validation.Validator接口的。
2016 0

热门文章

最新文章