Merge branch '6.2.x'
This commit is contained in:
@@ -26,12 +26,12 @@ import org.springframework.beans.factory.BeanInitializationException;
|
|||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
|
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
|
||||||
import org.springframework.context.EnvironmentAware;
|
import org.springframework.context.EnvironmentAware;
|
||||||
|
import org.springframework.core.env.CompositePropertySource;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.core.env.ConfigurablePropertyResolver;
|
import org.springframework.core.env.ConfigurablePropertyResolver;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
import org.springframework.core.env.PropertyResolver;
|
|
||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
import org.springframework.core.env.PropertySources;
|
import org.springframework.core.env.PropertySources;
|
||||||
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
||||||
@@ -131,27 +131,23 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS
|
|||||||
if (this.propertySources == null) {
|
if (this.propertySources == null) {
|
||||||
this.propertySources = new MutablePropertySources();
|
this.propertySources = new MutablePropertySources();
|
||||||
if (this.environment != null) {
|
if (this.environment != null) {
|
||||||
PropertyResolver propertyResolver = this.environment;
|
PropertySource<?> environmentPropertySource;
|
||||||
// If the ignoreUnresolvablePlaceholders flag is set to true, we have to create a
|
if (this.environment instanceof ConfigurableEnvironment configurableEnvironment) {
|
||||||
// local PropertyResolver to enforce that setting, since the Environment is most
|
environmentPropertySource = new CompositePropertySource(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME,
|
||||||
// likely not configured with ignoreUnresolvablePlaceholders set to true.
|
configurableEnvironment.getPropertySources());
|
||||||
// See https://github.com/spring-projects/spring-framework/issues/27947
|
|
||||||
if (this.ignoreUnresolvablePlaceholders &&
|
|
||||||
(this.environment instanceof ConfigurableEnvironment configurableEnvironment)) {
|
|
||||||
PropertySourcesPropertyResolver resolver =
|
|
||||||
new PropertySourcesPropertyResolver(configurableEnvironment.getPropertySources());
|
|
||||||
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
|
|
||||||
propertyResolver = resolver;
|
|
||||||
}
|
}
|
||||||
PropertyResolver propertyResolverToUse = propertyResolver;
|
else {
|
||||||
this.propertySources.addLast(
|
// Fallback code path that should never apply in a regular scenario, since the
|
||||||
new PropertySource<>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
|
// Environment in the ApplicationContext should always be a ConfigurableEnvironment.
|
||||||
@Override
|
environmentPropertySource =
|
||||||
public @Nullable String getProperty(String key) {
|
new PropertySource<>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
|
||||||
return propertyResolverToUse.getProperty(key);
|
@Override
|
||||||
}
|
public @Nullable Object getProperty(String key) {
|
||||||
}
|
return super.source.getProperty(key);
|
||||||
);
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.propertySources.addLast(environmentPropertySource);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
PropertySource<?> localPropertySource =
|
PropertySource<?> localPropertySource =
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 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.
|
||||||
@@ -19,11 +19,13 @@ package org.springframework.context.support;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.testfixture.beans.TestBean;
|
import org.springframework.beans.testfixture.beans.TestBean;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
@@ -432,6 +434,150 @@ class PropertySourcesPlaceholderConfigurerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that use the escape character (or disable it) with nested placeholder
|
||||||
|
* resolution.
|
||||||
|
*/
|
||||||
|
@Nested
|
||||||
|
class EscapedNestedPlaceholdersTests {
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void singleEscapeWithDefaultEscapeCharacter() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "admin")
|
||||||
|
.withProperty("my.property", "\\DOMAIN\\${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
// \DOMAIN\${user.home} resolves to \DOMAIN${user.home} instead of \DOMAIN\admin
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN${user.home}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void singleEscapeWithCustomEscapeCharacter() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "admin\\~${nested}")
|
||||||
|
.withProperty("my.property", "DOMAIN\\${user.home}\\~${enigma}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
// Set custom escape character.
|
||||||
|
ppc.setEscapeCharacter('~');
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN\\admin\\${nested}\\${enigma}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void singleEscapeWithEscapeCharacterDisabled() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "admin\\")
|
||||||
|
.withProperty("my.property", "\\DOMAIN\\${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
// Disable escape character.
|
||||||
|
ppc.setEscapeCharacter(null);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
// \DOMAIN\${user.home} resolves to \DOMAIN\admin
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("\\DOMAIN\\admin\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void tripleEscapeWithDefaultEscapeCharacter() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "admin\\\\\\")
|
||||||
|
.withProperty("my.property", "DOMAIN\\\\\\${user.home}#${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN\\\\${user.home}#admin\\\\\\");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void tripleEscapeWithCustomEscapeCharacter() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "admin\\~${enigma}")
|
||||||
|
.withProperty("my.property", "DOMAIN~~~${user.home}#${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
// Set custom escape character.
|
||||||
|
ppc.setEscapeCharacter('~');
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("DOMAIN~~${user.home}#admin\\${enigma}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void singleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "${enigma}")
|
||||||
|
.withProperty("my.property", "\\${DOMAIN}${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
ppc.setIgnoreUnresolvablePlaceholders(true);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("${DOMAIN}${enigma}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void singleEscapeWithCustomEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "${enigma}")
|
||||||
|
.withProperty("my.property", "~${DOMAIN}\\${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
// Set custom escape character.
|
||||||
|
ppc.setEscapeCharacter('~');
|
||||||
|
ppc.setIgnoreUnresolvablePlaceholders(true);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("${DOMAIN}\\${enigma}");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-34861
|
||||||
|
void tripleEscapeWithDefaultEscapeCharacterAndIgnoreUnresolvablePlaceholders() {
|
||||||
|
MockEnvironment env = new MockEnvironment()
|
||||||
|
.withProperty("user.home", "${enigma}")
|
||||||
|
.withProperty("my.property", "X:\\\\\\${DOMAIN}${user.home}");
|
||||||
|
|
||||||
|
DefaultListableBeanFactory bf = createBeanFactory();
|
||||||
|
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||||
|
ppc.setEnvironment(env);
|
||||||
|
ppc.setIgnoreUnresolvablePlaceholders(true);
|
||||||
|
ppc.postProcessBeanFactory(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("X:\\\\${DOMAIN}${enigma}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultListableBeanFactory createBeanFactory() {
|
||||||
|
BeanDefinition beanDefinition = genericBeanDefinition(TestBean.class)
|
||||||
|
.addPropertyValue("name", "${my.property}")
|
||||||
|
.getBeanDefinition();
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.registerBeanDefinition("testBean",beanDefinition);
|
||||||
|
return bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class OptionalTestBean {
|
private static class OptionalTestBean {
|
||||||
|
|
||||||
private Optional<String> name;
|
private Optional<String> name;
|
||||||
|
|||||||
Reference in New Issue
Block a user