Skip to content

Commit 3db5c70

Browse files
committed
Make sure binder properly resolve resources
This commit makes sure that `@ConfigurationProperties` binding resolves resources properly. In particular, any `ProtocolResolver` registered on the `ApplicationContext` is now honoured. Closes gh-11569
1 parent 712e562 commit 3db5c70

File tree

3 files changed

+93
-4
lines changed

3 files changed

+93
-4
lines changed

spring-boot/src/main/java/org/springframework/boot/bind/PropertiesConfigurationFactory.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,9 @@
3131
import org.springframework.beans.PropertyValues;
3232
import org.springframework.beans.factory.FactoryBean;
3333
import org.springframework.beans.factory.InitializingBean;
34+
import org.springframework.beans.support.ResourceEditorRegistrar;
35+
import org.springframework.context.ApplicationContext;
36+
import org.springframework.context.ApplicationContextAware;
3437
import org.springframework.context.MessageSource;
3538
import org.springframework.context.MessageSourceAware;
3639
import org.springframework.core.convert.ConversionService;
@@ -52,7 +55,7 @@
5255
* @author Dave Syer
5356
*/
5457
public class PropertiesConfigurationFactory<T>
55-
implements FactoryBean<T>, MessageSourceAware, InitializingBean {
58+
implements FactoryBean<T>, ApplicationContextAware, MessageSourceAware, InitializingBean {
5659

5760
private static final char[] EXACT_DELIMITERS = { '_', '.', '[' };
5861

@@ -73,6 +76,8 @@ public class PropertiesConfigurationFactory<T>
7376

7477
private Validator validator;
7578

79+
private ApplicationContext applicationContext;
80+
7681
private MessageSource messageSource;
7782

7883
private boolean hasBeenBound = false;
@@ -149,6 +154,11 @@ public void setTargetName(String targetName) {
149154
this.targetName = targetName;
150155
}
151156

157+
@Override
158+
public void setApplicationContext(ApplicationContext applicationContext) {
159+
this.applicationContext = applicationContext;
160+
}
161+
152162
/**
153163
* Set the message source.
154164
* @param messageSource the message source
@@ -265,6 +275,11 @@ private void doBindPropertiesToTarget() throws BindException {
265275
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
266276
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
267277
customizeBinder(dataBinder);
278+
if (this.applicationContext != null) {
279+
ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(
280+
this.applicationContext, this.applicationContext.getEnvironment());
281+
resourceEditorRegistrar.registerCustomEditors(dataBinder);
282+
}
268283
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
269284
Set<String> names = getNames(relaxedTargetNames);
270285
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,

spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -312,6 +312,7 @@ private void postProcessBeforeInitialization(Object bean, String beanName,
312312
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
313313
target);
314314
factory.setPropertySources(this.propertySources);
315+
factory.setApplicationContext(this.applicationContext);
315316
factory.setValidator(determineValidator(bean));
316317
// If no explicit conversion service is provided we add one so that (at least)
317318
// comma-separated arrays of convertibles can be bound automatically

spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2017 the original author or authors.
2+
* Copyright 2012-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,6 +40,10 @@
4040
import org.springframework.context.annotation.Bean;
4141
import org.springframework.context.annotation.Configuration;
4242
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
43+
import org.springframework.core.io.ClassPathResource;
44+
import org.springframework.core.io.ProtocolResolver;
45+
import org.springframework.core.io.Resource;
46+
import org.springframework.core.io.ResourceLoader;
4347
import org.springframework.mock.env.MockEnvironment;
4448
import org.springframework.test.context.support.TestPropertySourceUtils;
4549
import org.springframework.test.util.ReflectionTestUtils;
@@ -52,6 +56,12 @@
5256

5357
import static org.assertj.core.api.Assertions.assertThat;
5458
import static org.junit.Assert.fail;
59+
import static org.mockito.BDDMockito.given;
60+
import static org.mockito.Matchers.any;
61+
import static org.mockito.Matchers.anyString;
62+
import static org.mockito.Matchers.eq;
63+
import static org.mockito.Mockito.mock;
64+
import static org.mockito.Mockito.verify;
5565

5666
/**
5767
* Tests for {@link ConfigurationPropertiesBindingPostProcessor}.
@@ -366,6 +376,38 @@ private void assertBindingFailure(int errorCount) {
366376
}
367377
}
368378

379+
@Test
380+
public void customProtocolResolverIsInvoked() {
381+
this.context = new AnnotationConfigApplicationContext();
382+
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
383+
"test.resource=application.properties");
384+
ProtocolResolver protocolResolver = mock(ProtocolResolver.class);
385+
given(protocolResolver.resolve(anyString(), any(ResourceLoader.class)))
386+
.willReturn(null);
387+
this.context.addProtocolResolver(protocolResolver);
388+
this.context.register(PropertiesWithResource.class);
389+
this.context.refresh();
390+
verify(protocolResolver).resolve(eq("application.properties"),
391+
any(ResourceLoader.class));
392+
}
393+
394+
@Test
395+
public void customProtocolResolver() {
396+
this.context = new AnnotationConfigApplicationContext();
397+
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
398+
"test.resource=test:/application.properties");
399+
this.context.addProtocolResolver(new TestProtocolResolver());
400+
this.context.register(PropertiesWithResource.class);
401+
this.context.refresh();
402+
Resource resource = this.context.getBean(PropertiesWithResource.class)
403+
.getResource();
404+
assertThat(resource).isNotNull();
405+
assertThat(resource).isInstanceOf(ClassPathResource.class);
406+
assertThat(resource.exists()).isTrue();
407+
assertThat(((ClassPathResource) resource).getPath())
408+
.isEqualTo("application.properties");
409+
}
410+
369411
@Configuration
370412
@EnableConfigurationProperties
371413
public static class TestConfigurationWithValidatingSetter {
@@ -819,4 +861,35 @@ public void setName(String name) {
819861

820862
}
821863

864+
@Configuration
865+
@EnableConfigurationProperties
866+
@ConfigurationProperties(prefix = "test")
867+
public static class PropertiesWithResource {
868+
869+
private Resource resource;
870+
871+
public Resource getResource() {
872+
return this.resource;
873+
}
874+
875+
public void setResource(Resource resource) {
876+
this.resource = resource;
877+
}
878+
879+
}
880+
881+
private static class TestProtocolResolver implements ProtocolResolver {
882+
883+
public static final String PREFIX = "test:/";
884+
885+
@Override
886+
public Resource resolve(String location, ResourceLoader resourceLoader) {
887+
if (location.startsWith(PREFIX)) {
888+
String path = location.substring(PREFIX.length(), location.length());
889+
return new ClassPathResource(path);
890+
}
891+
return null;
892+
}
893+
}
894+
822895
}

0 commit comments

Comments
 (0)