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
pull/12120/merge
Stephane Nicoll 7 years ago
parent 712e562594
commit 3db5c70b58

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -31,6 +31,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.core.convert.ConversionService;
@ -52,7 +55,7 @@ import org.springframework.validation.Validator;
* @author Dave Syer
*/
public class PropertiesConfigurationFactory<T>
implements FactoryBean<T>, MessageSourceAware, InitializingBean {
implements FactoryBean<T>, ApplicationContextAware, MessageSourceAware, InitializingBean {
private static final char[] EXACT_DELIMITERS = { '_', '.', '[' };
@ -73,6 +76,8 @@ public class PropertiesConfigurationFactory<T>
private Validator validator;
private ApplicationContext applicationContext;
private MessageSource messageSource;
private boolean hasBeenBound = false;
@ -149,6 +154,11 @@ public class PropertiesConfigurationFactory<T>
this.targetName = targetName;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Set the message source.
* @param messageSource the message source
@ -265,6 +275,11 @@ public class PropertiesConfigurationFactory<T>
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
customizeBinder(dataBinder);
if (this.applicationContext != null) {
ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(
this.applicationContext, this.applicationContext.getEnvironment());
resourceEditorRegistrar.registerCustomEditors(dataBinder);
}
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
Set<String> names = getNames(relaxedTargetNames);
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -312,6 +312,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
factory.setPropertySources(this.propertySources);
factory.setApplicationContext(this.applicationContext);
factory.setValidator(determineValidator(bean));
// If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -40,6 +40,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.util.ReflectionTestUtils;
@ -52,6 +56,12 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link ConfigurationPropertiesBindingPostProcessor}.
@ -366,6 +376,38 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
}
}
@Test
public void customProtocolResolverIsInvoked() {
this.context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"test.resource=application.properties");
ProtocolResolver protocolResolver = mock(ProtocolResolver.class);
given(protocolResolver.resolve(anyString(), any(ResourceLoader.class)))
.willReturn(null);
this.context.addProtocolResolver(protocolResolver);
this.context.register(PropertiesWithResource.class);
this.context.refresh();
verify(protocolResolver).resolve(eq("application.properties"),
any(ResourceLoader.class));
}
@Test
public void customProtocolResolver() {
this.context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"test.resource=test:/application.properties");
this.context.addProtocolResolver(new TestProtocolResolver());
this.context.register(PropertiesWithResource.class);
this.context.refresh();
Resource resource = this.context.getBean(PropertiesWithResource.class)
.getResource();
assertThat(resource).isNotNull();
assertThat(resource).isInstanceOf(ClassPathResource.class);
assertThat(resource.exists()).isTrue();
assertThat(((ClassPathResource) resource).getPath())
.isEqualTo("application.properties");
}
@Configuration
@EnableConfigurationProperties
public static class TestConfigurationWithValidatingSetter {
@ -819,4 +861,35 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
}
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "test")
public static class PropertiesWithResource {
private Resource resource;
public Resource getResource() {
return this.resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
}
private static class TestProtocolResolver implements ProtocolResolver {
public static final String PREFIX = "test:/";
@Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
if (location.startsWith(PREFIX)) {
String path = location.substring(PREFIX.length(), location.length());
return new ClassPathResource(path);
}
return null;
}
}
}

Loading…
Cancel
Save