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:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.PropertyValues;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
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.MessageSource;
|
||||||
import org.springframework.context.MessageSourceAware;
|
import org.springframework.context.MessageSourceAware;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
@@ -52,7 +55,7 @@ import org.springframework.validation.Validator;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*/
|
*/
|
||||||
public class PropertiesConfigurationFactory<T>
|
public class PropertiesConfigurationFactory<T>
|
||||||
implements FactoryBean<T>, MessageSourceAware, InitializingBean {
|
implements FactoryBean<T>, ApplicationContextAware, MessageSourceAware, InitializingBean {
|
||||||
|
|
||||||
private static final char[] EXACT_DELIMITERS = { '_', '.', '[' };
|
private static final char[] EXACT_DELIMITERS = { '_', '.', '[' };
|
||||||
|
|
||||||
@@ -73,6 +76,8 @@ public class PropertiesConfigurationFactory<T>
|
|||||||
|
|
||||||
private Validator validator;
|
private Validator validator;
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private MessageSource messageSource;
|
private MessageSource messageSource;
|
||||||
|
|
||||||
private boolean hasBeenBound = false;
|
private boolean hasBeenBound = false;
|
||||||
@@ -149,6 +154,11 @@ public class PropertiesConfigurationFactory<T>
|
|||||||
this.targetName = targetName;
|
this.targetName = targetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the message source.
|
* Set the message source.
|
||||||
* @param messageSource the message source
|
* @param messageSource the message source
|
||||||
@@ -265,6 +275,11 @@ public class PropertiesConfigurationFactory<T>
|
|||||||
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
|
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
|
||||||
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
|
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
|
||||||
customizeBinder(dataBinder);
|
customizeBinder(dataBinder);
|
||||||
|
if (this.applicationContext != null) {
|
||||||
|
ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(
|
||||||
|
this.applicationContext, this.applicationContext.getEnvironment());
|
||||||
|
resourceEditorRegistrar.registerCustomEditors(dataBinder);
|
||||||
|
}
|
||||||
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
|
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
|
||||||
Set<String> names = getNames(relaxedTargetNames);
|
Set<String> names = getNames(relaxedTargetNames);
|
||||||
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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>(
|
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
|
||||||
target);
|
target);
|
||||||
factory.setPropertySources(this.propertySources);
|
factory.setPropertySources(this.propertySources);
|
||||||
|
factory.setApplicationContext(this.applicationContext);
|
||||||
factory.setValidator(determineValidator(bean));
|
factory.setValidator(determineValidator(bean));
|
||||||
// If no explicit conversion service is provided we add one so that (at least)
|
// If no explicit conversion service is provided we add one so that (at least)
|
||||||
// comma-separated arrays of convertibles can be bound automatically
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
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.mock.env.MockEnvironment;
|
||||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
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.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
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}.
|
* 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
|
@Configuration
|
||||||
@EnableConfigurationProperties
|
@EnableConfigurationProperties
|
||||||
public static class TestConfigurationWithValidatingSetter {
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user