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
This commit is contained in:
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…
Reference in New Issue