diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
index a8a9e46268..87c76bee83 100644
--- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
@@ -782,7 +782,9 @@ annotations to your `@ConfigurationProperties` class:
----
You can also add a custom Spring `Validator` by creating a bean definition called
-`configurationPropertiesValidator`.
+`configurationPropertiesValidator`. There is a
+{github-code}/spring-boot-samples/spring-boot-sample-property-validation[Validation sample]
+so you can see how to set things up.
TIP: The `spring-boot-actuator` module includes an endpoint that exposes all
`@ConfigurationProperties` beans. Simply point your web browser to `/configprops`
diff --git a/spring-boot-samples/README.adoc b/spring-boot-samples/README.adoc
index 1186970424..1f4bb2037d 100644
--- a/spring-boot-samples/README.adoc
+++ b/spring-boot-samples/README.adoc
@@ -70,6 +70,8 @@
-- A spring integration application
* link:spring-boot-sample-profile[spring-boot-sample-profile]
-- example showing Spring's `@profile` support
+* link:spring-boot-sample-property-validation[spring-boot-sample-property-validation]
+ -- example showing the usage of `@ConfigurationProperties` with a Spring `Validator`
* link:spring-boot-sample-parent-context[spring-boot-sample-parent-context]
-- example showing an `ApplicationContext` with a parent
* link:spring-boot-sample-aop[spring-boot-sample-aop]
diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml
index 00ed00afbd..db59230868 100644
--- a/spring-boot-samples/pom.xml
+++ b/spring-boot-samples/pom.xml
@@ -67,6 +67,7 @@
spring-boot-sample-metrics-redis
spring-boot-sample-parent-context
spring-boot-sample-profile
+ spring-boot-sample-property-validation
spring-boot-sample-secure
spring-boot-sample-secure-oauth2
spring-boot-sample-servlet
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/pom.xml b/spring-boot-samples/spring-boot-sample-property-validation/pom.xml
new file mode 100644
index 0000000000..d67b931ae0
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-samples
+ 1.3.0.BUILD-SNAPSHOT
+
+ spring-boot-sample-property-validation
+ Spring Boot Property Validation Sample
+ Spring Boot Property Validation Sample
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SampleProperties.java b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SampleProperties.java
new file mode 100644
index 0000000000..0f79a28ece
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SampleProperties.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.propertyvalidation;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "sample")
+public class SampleProperties {
+
+ /**
+ * Sample host.
+ */
+ private String host;
+
+ /**
+ * Sample port.
+ */
+ private Integer port = 8080;
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+}
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertiesValidator.java b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertiesValidator.java
new file mode 100644
index 0000000000..8e4feb9e41
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertiesValidator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.propertyvalidation;
+
+import java.util.regex.Pattern;
+
+import org.springframework.validation.Errors;
+import org.springframework.validation.ValidationUtils;
+import org.springframework.validation.Validator;
+
+public class SamplePropertiesValidator implements Validator {
+
+ final Pattern pattern = Pattern.compile("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
+
+ @Override
+ public boolean supports(Class> type) {
+ return type == SampleProperties.class;
+ }
+
+ @Override
+ public void validate(Object o, Errors errors) {
+ ValidationUtils.rejectIfEmpty(errors, "host", "host.empty");
+ ValidationUtils.rejectIfEmpty(errors, "port", "port.empty");
+
+ SampleProperties properties = (SampleProperties) o;
+ if (properties.getHost() != null &&
+ !pattern.matcher(properties.getHost()).matches()) {
+ errors.rejectValue("host", "Invalid host");
+ }
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertyValidationApplication.java b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertyValidationApplication.java
new file mode 100644
index 0000000000..44e061af47
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/src/main/java/sample/propertyvalidation/SamplePropertyValidationApplication.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.propertyvalidation;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.Validator;
+
+@SpringBootApplication
+@EnableConfigurationProperties
+public class SamplePropertyValidationApplication {
+
+ @Bean
+ public Validator configurationPropertiesValidator() {
+ return new SamplePropertiesValidator();
+ }
+
+ @Service
+ @Profile("app")
+ static class Startup implements CommandLineRunner {
+
+ @Autowired
+ private SampleProperties properties;
+
+ @Override
+ public void run(String... args) {
+ System.out.println("=========================================");
+ System.out.println("Sample host: " + this.properties.getHost());
+ System.out.println("Sample port: " + this.properties.getPort());
+ System.out.println("=========================================");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ new SpringApplicationBuilder(SamplePropertyValidationApplication.class)
+ .profiles("app").run(args);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-property-validation/src/main/resources/application.properties
new file mode 100644
index 0000000000..b28381563f
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+sample.host=192.168.0.1
+sample.port=7070
\ No newline at end of file
diff --git a/spring-boot-samples/spring-boot-sample-property-validation/src/test/java/sample/propertyvalidation/SamplePropertyValidationApplicationTests.java b/spring-boot-samples/spring-boot-sample-property-validation/src/test/java/sample/propertyvalidation/SamplePropertyValidationApplicationTests.java
new file mode 100644
index 0000000000..b822fc6aad
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-property-validation/src/test/java/sample/propertyvalidation/SamplePropertyValidationApplicationTests.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample.propertyvalidation;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.test.EnvironmentTestUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link SamplePropertyValidationApplication}.
+ *
+ * @author Lucas Saldanha
+ * @author Stephane Nicoll
+ */
+public class SamplePropertyValidationApplicationTests {
+
+ @Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+
+ @After
+ public void closeContext() {
+ context.close();
+ }
+
+ @Test
+ public void bindValidProperties() {
+ this.context.register(SamplePropertyValidationApplication.class);
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "sample.host:192.168.0.1", "sample.port:9090");
+ this.context.refresh();
+
+ SampleProperties properties = this.context.getBean(SampleProperties.class);
+ assertEquals("192.168.0.1", properties.getHost());
+ assertEquals(Integer.valueOf(9090), properties.getPort());
+ }
+
+ @Test
+ public void bindInvalidHost() {
+ this.context.register(SamplePropertyValidationApplication.class);
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "sample.host:xxxxxx", "sample.port:9090");
+
+ thrown.expect(BeanCreationException.class);
+ thrown.expectMessage("xxxxxx");
+ this.context.refresh();
+ }
+
+ @Test
+ public void bindNullHost() {
+ this.context.register(SamplePropertyValidationApplication.class);
+
+ thrown.expect(BeanCreationException.class);
+ thrown.expectMessage("null");
+ thrown.expectMessage("host");
+ this.context.refresh();
+ }
+
+ @Test
+ public void validatorOnlyCalledOnSupportedClass() {
+ this.context.register(SamplePropertyValidationApplication.class);
+ this.context.register(ServerProperties.class); // our validator will not apply here
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "sample.host:192.168.0.1", "sample.port:9090");
+ this.context.refresh();
+
+ SampleProperties properties = this.context.getBean(SampleProperties.class);
+ assertEquals("192.168.0.1", properties.getHost());
+ assertEquals(Integer.valueOf(9090), properties.getPort());
+ }
+
+}