Here’s an example of how to customize the javax.validation.Validator
instance in Spring 5. Validator is the Java API for bean validation that was introduced in the JSR-303 specification. Hibernate Validator is bundled with Spring Boot 2, and is the de facto reference implementation for bean validation.
Why would you need to do something like this? In this case, I wanted to configure temporal validation tolerance, which provides an error margin when validating annotations like @Past
and @Future
, and as of Hibernate Validator 6.0 is a global configuration setting. This tolerance can help if system clocks aren’t perfectly in sync, for example.
There are several different ways to accomplish this, but I wanted to stick with a Java based Spring configuration approach. I also wanted a default ClockProvider
implementation that could be overridden in a unit test using @MockBean
, in order to test HibernateValidatorConfiguration.TEMPORAL_VALIDATION_TOLERANCE
.
Conveniently, Spring 5 has a LocalValidatorFactoryBean#postProcessConfiguration()
method that makes this easy. Here’s an example:
@Bean public LocalValidatorFactoryBean localValidatorFactoryBean() { return new LocalValidatorFactoryBean() { @Override protected void postProcessConfiguration(Configuration<?> configuration) { configuration.clockProvider(clockProvider()); configuration.addProperty(HibernateValidatorConfiguration.TEMPORAL_VALIDATION_TOLERANCE, String.valueOf(temporalValidationTolerance)); } }; } @Bean public ClockProvider clockProvider() { return Clock::systemUTC; }
In tests, the ClockProvider can be mocked to return a fixed time clock:
// For testing the clock, return a fixed time. when(clockProvider.getClock()).thenReturn(new Clock() { @Override public ZoneId getZone() { return ZoneId.of("UTC"); } @Override public Clock withZone(ZoneId zone) { throw new UnsupportedOperationException(); } @Override public Instant instant() { return fixedTime; } });
A full Spring Boot demo application using Gradle can be found here.