【问题标题】:Using a custom ResourceBundle with Hibernate Validator使用带有 Hibernate Validator 的自定义 ResourceBundle
【发布时间】:2023-04-11 08:15:02
【问题描述】:

我正在尝试为 Hibernate Validator 4.1 到 Spring 3.0 设置自定义消息源。我已经设置了必要的配置:

<!-- JSR-303 -->
<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="validationMessageSource" ref="messageSource"/>
 </bean>

翻译是从我的消息源提供的,但似乎消息本身中的替换标记是在消息源中查找的,即:

my.message=the property {prop} is invalid

在 messageSource 中有查找“prop”的调用。进入 ResourceBundleMessageInterpolator.interpolateMessage 我注意到 javadoc 声明:

根据 JSR 303 中指定的算法运行消息插值。

注意: 用户包中的查找是递归的,而默认包中的查找不是!

在我看来,递归将始终针对用户指定的包进行,因此实际上我无法翻译标准消息,如 Size 的消息。

如何插入我自己的消息源并能够在消息中替换参数?

【问题讨论】:

  • 我不明白为什么首先获取用户特定的捆绑包会阻止您翻译@Size 的消息。你能详细说明一下吗?
  • @Grzegorz - 在我看来,首先查看的不是特定于用户的捆绑包,而是令牌在用户捆绑包中递归解析的事实。

标签: spring bean-validation hibernate-validator


【解决方案1】:

这在我看来就像递归 将永远发生 用户指定的捆绑包,所以实际上我 无法翻译标准消息 就像尺寸一样。

Hibernate Validator 的 ResourceBundleMessageInterpolator 创建两个 ResourceBundleLocator 实例(即 PlatformResourceBundleLocator),一个用于 UserDefined 验证消息 - userResourceBundleLocator,另一个用于 JSR-303 标准验证消息 - defaultResourceBundleLocator。

出现在两个大括号内的任何文本,例如消息中的{someText} 被视为replacementToken。 ResourceBundleMessageInterpolator 尝试在 ResourceBundleLocators 中寻找可以替换replacementToken 的匹配值。

  1. 首先在 UserDefinedValidationMessages(递归)中,
  2. 然后在 DefaultValidationMessages(非递归)中。

因此,如果您将标准 JSR-303 消息放入自定义 ResourceBundle 中,例如 validation_erros.properties,它将被您的自定义消息替换。请参阅此 EXAMPLE 标准 NotNull 验证消息“可能不是 null”已被自定义“MyNotNullMessage”消息替换。

如何插入自己的消息 来源并能够有参数 在消息中被替换?
my.message=属性 {prop} 是 无效

经过两个 ResourceBundleLocators 后,ResourceBundleMessageInterpolator 在resolvedMessage 中找到更多replaceTokens(由两个bundle 解析)。这些replacementToken只不过是Annotation的属性名称,如果在resolvedMessage中找到这样的replaceToken,它们将被匹配的Annotation属性的值替换

ResourceBundleMessageInterpolator.java [第 168 行,4.1.0.Final]

resolvedMessage = replaceAnnotationAttributes( resolvedMessage, annotationParameters );

提供一个用自定义值替换{prop}的例子,希望对你有所帮助....

MyNotNull.java

@Constraint(validatedBy = {MyNotNullValidator.class})
public @interface MyNotNull {
    String propertyName(); //Annotation Attribute Name
    String message() default "{myNotNull}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default {};
}

MyNotNullValidator.java

public class MyNotNullValidator implements ConstraintValidator<MyNotNull, Object> {
    public void initialize(MyNotNull parameters) {
    }

    public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
        return object != null;
    }
}

User.java

class User {
    private String userName;

    /* whatever name you provide as propertyName will replace {propertyName} in resource bundle */
   // Annotation Attribute Value 
    @MyNotNull(propertyName="userName") 
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

validation_errors.properties

notNull={propertyName} cannot be null 

测试

public void test() {
    LocalValidatorFactoryBean factory = applicationContext.getBean("validator", LocalValidatorFactoryBean.class);
    Validator validator = factory.getValidator();
    User user = new User("James", "Bond");
    user.setUserName(null);
    Set<ConstraintViolation<User>> violations = validator.validate(user);
    for(ConstraintViolation<User> violation : violations) {
        System.out.println("Custom Message:- " + violation.getMessage());
    }
}

输出

Custom Message:- userName cannot be null

【讨论】:

  • 这是一个非常彻底的答案。明天我会首先验证它。谢谢!
  • 嗯,问题不在于那里,而是在我的 MessageSource 实现中,它没有为未找到的键返回“null”,而是键本身。一旦我纠正它一切正常。但你的回答让我走上了正确的道路,所以它是赢家。
猜你喜欢
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 2016-05-08
  • 1970-01-01
相关资源
最近更新 更多