公式リファレンスにはこうあった。
Spring-driven Method Validation
The method validation feature supported by Bean Validation 1.1, and as a custom extension also by Hibernate Validator 4.3, can be integrated into a Spring context through a MethodValidationPostProcessor bean definition:
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
In order to be eligible for Spring-driven method validation, all target classes need to be annotated with Spring’s @Validated annotation, optionally declaring the validation groups to use. Check out the MethodValidationPostProcessor javadocs for setup details with Hibernate Validator and Bean Validation 1.1 providers.
Additional Configuration Options
The default LocalValidatorFactoryBean configuration should prove sufficient for most cases. There are a number of configuration options for various Bean Validation constructs, from message interpolation to traversal resolution. See the LocalValidatorFactoryBean javadocs for more information on these options.
ここだけ読むとMethodValidationPostProcessorとLocalValidatorFactoryBeanをBean登録したら動くのかなと勘違いして、ちょっとハマってしまった。実際には、MethodValidationPostProcessorインスタンスにLocalValidatorFactoryBeanインスタンスを設定してやらないと、デフォルトからの変更、たとえばここでのようにMessageSourceとしてmessage.propertiesを追加したい場合などが、反映されない。
Bean ValidationのMethod Validationをコントローラのメソッドで使う。メッセージ本文がプロパティファイルにあり、そのキーを指定するときは"{}"でキーを囲む。NotEmptyはorg.hibernate.validator.NotEmptyというHibernate Validatorが独自に提供するValidation。
@Controller @Validated public class HogeController { @RequestMapping(path = "hoge", method = RequestMethod.POST) public String hoge(@NotEmpty(message = "{valid.required}")) { }
Springの設定では、さきほどの説明のようにMethodValidationPostProcessorインスタンスの生成時にLocalValidatorFactoryBeanインスタンスをvalidatorとしてセットする。
@Bean public LocalValidatorFactoryBean localValidatorFactoryBean() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource(); reloadableResourceBundleMessageSource.setBasename("classpath:/messages"); localValidatorFactoryBean.setValidationMessageSource(reloadableResourceBundleMessageSource); return localValidatorFactoryBean; } @Bean public MethodValidationPostProcessor methodValidationPostProcessor(LocalValidatorFactoryBean localValidatorFactoryBean) { MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor(); methodValidationPostProcessor.setValidator(localValidatorFactoryBean); return methodValidationPostProcessor; }
Method Validationでは、値が無効な場合javax.validation.ConstraintViolationExceptionオブジェクトがスローされる。これをExceptionHandlerで処理する。たとえばコントローラ共通で扱うときは@ControllerAdviceアノテーションをつけたクラスで、@ExceptionHandler(value = { ConstraintViolationException.class })アノテーションをつけたメソッドに処理を記述する。入力値やメッセージ文字列は、getConstraintViolations()を呼び出して各ConstraintViolationオブジェクトから取得できる。
@ControllerAdvice public class GlobalDefaultExceptionHandler { @ExceptionHandler(value = { ConstraintViolationException.class }) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public String handleIfInvalid(ConstraintViolationException e) { Set<ConstraintViolation<?>> violations = e.getConstraintViolations(); violations.stream().forEach(v -> logger.debug("=======" + v.getMessage())); return null; } }