Commit 9104ea81 authored by Stephane Nicoll's avatar Stephane Nicoll

Expose Validator bean

This commit improves ValidationAutoConfiguration so that a `Validator`
bean is exposed if JSR 303 is available. This has the side effect of
automatically enable Spring Data Couchbase's entity validation rather
than requiring to expose a `Validator` bean.

Closes gh-5178
parent 28e86272
...@@ -22,9 +22,10 @@ import com.couchbase.client.java.Bucket; ...@@ -22,9 +22,10 @@ import com.couchbase.client.java.Bucket;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -41,7 +42,8 @@ import org.springframework.data.couchbase.repository.CouchbaseRepository; ...@@ -41,7 +42,8 @@ import org.springframework.data.couchbase.repository.CouchbaseRepository;
*/ */
@Configuration @Configuration
@ConditionalOnClass({ Bucket.class, CouchbaseRepository.class }) @ConditionalOnClass({ Bucket.class, CouchbaseRepository.class })
@AutoConfigureAfter(CouchbaseAutoConfiguration.class) @AutoConfigureAfter({ CouchbaseAutoConfiguration.class,
ValidationAutoConfiguration.class })
@EnableConfigurationProperties(CouchbaseDataProperties.class) @EnableConfigurationProperties(CouchbaseDataProperties.class)
@Import({ CouchbaseConfigurerAdapterConfiguration.class, @Import({ CouchbaseConfigurerAdapterConfiguration.class,
SpringBootCouchbaseDataConfiguration.class }) SpringBootCouchbaseDataConfiguration.class })
...@@ -52,7 +54,7 @@ public class CouchbaseDataAutoConfiguration { ...@@ -52,7 +54,7 @@ public class CouchbaseDataAutoConfiguration {
public static class ValidationConfiguration { public static class ValidationConfiguration {
@Bean @Bean
@ConditionalOnBean(Validator.class) @ConditionalOnSingleCandidate(Validator.class)
public ValidatingCouchbaseEventListener validationEventListener( public ValidatingCouchbaseEventListener validationEventListener(
Validator validator) { Validator validator) {
return new ValidatingCouchbaseEventListener(validator); return new ValidatingCouchbaseEventListener(validator);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.validation; package org.springframework.boot.autoconfigure.validation;
import javax.validation.Validation; import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator; import javax.validation.executable.ExecutableValidator;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -32,6 +33,7 @@ import org.springframework.context.annotation.Conditional; ...@@ -32,6 +33,7 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
/** /**
...@@ -42,14 +44,22 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess ...@@ -42,14 +44,22 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
* @since 1.5.0 * @since 1.5.0
*/ */
@ConditionalOnClass(ExecutableValidator.class) @ConditionalOnClass(ExecutableValidator.class)
@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
@Conditional(ValidationAutoConfiguration.OnValidatorAvailableCondition.class)
public class ValidationAutoConfiguration { public class ValidationAutoConfiguration {
@Bean @Bean
@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
@Conditional(OnValidatorAvailableCondition.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public MethodValidationPostProcessor methodValidationPostProcessor() { public Validator validator() {
return new MethodValidationPostProcessor(); return new LocalValidatorFactoryBean();
}
@Bean
@ConditionalOnMissingBean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator);
return processor;
} }
@Order(Ordered.LOWEST_PRECEDENCE) @Order(Ordered.LOWEST_PRECEDENCE)
......
...@@ -18,20 +18,17 @@ package org.springframework.boot.autoconfigure.data.couchbase; ...@@ -18,20 +18,17 @@ package org.springframework.boot.autoconfigure.data.couchbase;
import java.util.Set; import java.util.Set;
import javax.validation.Validator;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseTestConfigurer; import org.springframework.boot.autoconfigure.couchbase.CouchbaseTestConfigurer;
import org.springframework.boot.autoconfigure.data.couchbase.city.City; import org.springframework.boot.autoconfigure.data.couchbase.city.City;
import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration; import org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration;
...@@ -44,7 +41,6 @@ import org.springframework.data.couchbase.repository.support.IndexManager; ...@@ -44,7 +41,6 @@ import org.springframework.data.couchbase.repository.support.IndexManager;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link CouchbaseDataAutoConfiguration}. * Tests for {@link CouchbaseDataAutoConfiguration}.
...@@ -79,11 +75,9 @@ public class CouchbaseDataAutoConfigurationTests { ...@@ -79,11 +75,9 @@ public class CouchbaseDataAutoConfigurationTests {
@Test @Test
public void validatorIsPresent() { public void validatorIsPresent() {
load(ValidatorConfiguration.class); load(CouchbaseTestConfigurer.class);
ValidatingCouchbaseEventListener listener = this.context assertThat(this.context.getBeansOfType(ValidatingCouchbaseEventListener.class))
.getBean(ValidatingCouchbaseEventListener.class); .hasSize(1);
assertThat(new DirectFieldAccessor(listener).getPropertyValue("validator"))
.isEqualTo(this.context.getBean(Validator.class));
} }
@Test @Test
...@@ -132,22 +126,12 @@ public class CouchbaseDataAutoConfigurationTests { ...@@ -132,22 +126,12 @@ public class CouchbaseDataAutoConfigurationTests {
context.register(config); context.register(config);
} }
context.register(PropertyPlaceholderAutoConfiguration.class, context.register(PropertyPlaceholderAutoConfiguration.class,
CouchbaseAutoConfiguration.class, CouchbaseDataAutoConfiguration.class); ValidationAutoConfiguration.class, CouchbaseAutoConfiguration.class,
CouchbaseDataAutoConfiguration.class);
context.refresh(); context.refresh();
this.context = context; this.context = context;
} }
@Configuration
@Import(CouchbaseTestConfigurer.class)
static class ValidatorConfiguration {
@Bean
public Validator myValidator() {
return mock(Validator.class);
}
}
@Configuration @Configuration
static class CustomCouchbaseConfiguration extends AbstractCouchbaseDataConfiguration { static class CustomCouchbaseConfiguration extends AbstractCouchbaseDataConfiguration {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.validation; package org.springframework.boot.autoconfigure.validation;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import org.junit.After; import org.junit.After;
...@@ -24,6 +25,7 @@ import org.junit.Rule; ...@@ -24,6 +25,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -54,6 +56,7 @@ public class ValidationAutoConfigurationTests { ...@@ -54,6 +56,7 @@ public class ValidationAutoConfigurationTests {
@Test @Test
public void validationIsEnabled() { public void validationIsEnabled() {
load(SampleService.class); load(SampleService.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
SampleService service = this.context.getBean(SampleService.class); SampleService service = this.context.getBean(SampleService.class);
service.doSomething("Valid"); service.doSomething("Valid");
this.thrown.expect(ConstraintViolationException.class); this.thrown.expect(ConstraintViolationException.class);
...@@ -63,10 +66,15 @@ public class ValidationAutoConfigurationTests { ...@@ -63,10 +66,15 @@ public class ValidationAutoConfigurationTests {
@Test @Test
public void userDefinedMethodValidationPostProcessorTakesPrecedence() { public void userDefinedMethodValidationPostProcessorTakesPrecedence() {
load(SampleConfiguration.class); load(SampleConfiguration.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
Object userMethodValidationPostProcessor =
this.context.getBean("testMethodValidationPostProcessor");
assertThat(this.context.getBean(MethodValidationPostProcessor.class)) assertThat(this.context.getBean(MethodValidationPostProcessor.class))
.isSameAs(this.context.getBean("testMethodValidationPostProcessor")); .isSameAs(userMethodValidationPostProcessor);
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)) assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
.hasSize(1); .hasSize(1);
assertThat(this.context.getBean(Validator.class)).isNotSameAs(
new DirectFieldAccessor(userMethodValidationPostProcessor).getPropertyValue("validator"));
} }
public void load(Class<?> config) { public void load(Class<?> config) {
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.validation; package org.springframework.boot.autoconfigure.validation;
import javax.validation.Validator;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -50,6 +52,7 @@ public class ValidationAutoConfigurationWithHibernateValidatorMissingElImplTests ...@@ -50,6 +52,7 @@ public class ValidationAutoConfigurationWithHibernateValidatorMissingElImplTests
public void validationIsDisabled() { public void validationIsDisabled() {
this.context = new AnnotationConfigApplicationContext( this.context = new AnnotationConfigApplicationContext(
ValidationAutoConfiguration.class); ValidationAutoConfiguration.class);
assertThat(this.context.getBeansOfType(Validator.class)).isEmpty();
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)) assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
.isEmpty(); .isEmpty();
} }
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.validation; package org.springframework.boot.autoconfigure.validation;
import javax.validation.Validator;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -49,6 +51,7 @@ public class ValidationAutoConfigurationWithoutValidatorTests { ...@@ -49,6 +51,7 @@ public class ValidationAutoConfigurationWithoutValidatorTests {
public void validationIsDisabled() { public void validationIsDisabled() {
this.context = new AnnotationConfigApplicationContext( this.context = new AnnotationConfigApplicationContext(
ValidationAutoConfiguration.class); ValidationAutoConfiguration.class);
assertThat(this.context.getBeansOfType(Validator.class)).isEmpty();
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)) assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
.isEmpty(); .isEmpty();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment