Использование пользовательского ResourceBundle с Hibernate Validator

Я пытаюсь настроить пользовательский источник сообщений для 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. Вход в ResourceBundleMessageInterpolator.interpolateMessage Я отмечаю, что в javadoc указано:

Запускает интерполяцию сообщения в соответствии с алгоритмом, указанным в JSR 303.

Примечание. Поиск в пользовательских пакетах является рекурсивным, а поиск в пакете по умолчанию — нет!

Мне кажется, что рекурсия всегда будет иметь место для указанного пользователем пакета, поэтому на самом деле я не могу переводить стандартные сообщения, такие как сообщение для размера.

Как я могу подключить свой собственный источник сообщений и иметь возможность заменять параметры в сообщении?


person Robert Munteanu    schedule 23.11.2010    source источник
comment
Я не понимаю, почему тот факт, что пользовательский пакет берется первым, мешает вам переводить сообщения для @Size. Не могли бы вы уточнить?   -  person Grzegorz Oledzki    schedule 23.11.2010
comment
@Grzegorz - сначала рассматривается не пользовательский пакет, который, на мой взгляд, является проблемой, а тот факт, что токены рекурсивно разрешаются в пользовательском пакете.   -  person Robert Munteanu    schedule 23.11.2010


Ответы (1)


Мне кажется, что рекурсия всегда будет иметь место для указанного пользователем пакета, поэтому на самом деле я не могу переводить стандартные сообщения, такие как сообщение для размера.

ResourceBundleMessageInterpolator Hibernate Validator создает два экземпляра ResourceBundleLocator (т. е. PlatformResourceBundleLocator), один для сообщений проверки UserDefined — userResourceBundleLocator, а другой — для стандартных сообщений проверки JSR-303 — defaultResourceBundleLocator.

Любой текст, который появляется внутри двух фигурных скобок, например. {someText} в сообщении рассматривается как replaceToken. ResourceBundleMessageInterpolator пытается найти соответствующее значение, которое может заменить replaceToken в ResourceBundleLocators.

  1. сначала в UserDefinedValidationMessages (что является рекурсивным),
  2. затем в DefaultValidationMessages (что НЕ является рекурсивным).

Итак, если вы поместите стандартное сообщение JSR-303 в пользовательский ResourceBundle, скажем, validation_erros.properties, оно будет заменено вашим настраиваемым сообщением. См. в этом ПРИМЕР Стандартное сообщение проверки NotNull "может не быть нулевым" было заменено пользовательским сообщением «MyNotNullMessage».

Как я могу подключить свой собственный источник сообщений и иметь возможность заменять параметры в сообщении?
my.message=недопустимое свойство {prop}

Пройдя через оба ResourceBundleLocators, ResourceBundleMessageInterpolator находит дополнительные replaceTokens в разрешенном сообщении (разрешенном обоими пакетами). Эти replaceToken представляют собой не что иное, как имена атрибутов аннотации. Если такие replaceToken найдены в разрешенном сообщении, они заменяются значениями соответствующих атрибутов аннотации.

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
person dira    schedule 23.11.2010
comment
Это очень тщательный ответ. Завтра первым делом проверю. Спасибо! - person Robert Munteanu; 23.11.2010
comment
Ну, проблема была не в этом, а в моей реализации MessageSource, которая возвращала не 'null' для ключей, которые не были найдены, а сам ключ. Один раз поправил, все заработало. Но ваш ответ привел меня на правильный путь, так что это победитель. - person Robert Munteanu; 25.11.2010