Skip to content

Commit d6b9cd9

Browse files
committed
Use new implementation in PropertyPlaceholderHelper
This commit removes the previous implementation in favor of the new PlaceholderParser. The only noticeable side effect is that the exception is no longer an IllegalArgumentException, but rather the dedicated PlaceholderResolutionException. See spring-projectsgh-9628
1 parent ec3a2e4 commit d6b9cd9

File tree

23 files changed

+154
-188
lines changed

23 files changed

+154
-188
lines changed

framework-docs/modules/ROOT/pages/core/beans/annotation-config/value-annotations.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
101101

102102
Using the above configuration ensures Spring initialization failure if any `${}`
103103
placeholder could not be resolved. It is also possible to use methods like
104-
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
105-
placeholders.
104+
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
105+
`setEscapeCharacter` to customize placeholders.
106106

107107
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
108108
will get properties from `application.properties` and `application.yml` files.

spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -100,6 +100,8 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
100100
/** Default value separator: {@value}. */
101101
public static final String DEFAULT_VALUE_SEPARATOR = ":";
102102

103+
/** Default escape character: {@value}. */
104+
public static final Character DEFAULT_ESCAPE_CHARACTER = '\\';
103105

104106
/** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */
105107
protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
@@ -111,6 +113,10 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi
111113
@Nullable
112114
protected String valueSeparator = DEFAULT_VALUE_SEPARATOR;
113115

116+
/** Defaults to {@value #DEFAULT_ESCAPE_CHARACTER}. */
117+
@Nullable
118+
protected Character escapeCharacter = DEFAULT_ESCAPE_CHARACTER;
119+
114120
protected boolean trimValues = false;
115121

116122
@Nullable
@@ -151,6 +157,17 @@ public void setValueSeparator(@Nullable String valueSeparator) {
151157
this.valueSeparator = valueSeparator;
152158
}
153159

160+
/**
161+
* Specify the escape character to use to ignore placeholder prefix
162+
* or value separator, or {@code null} if no escaping should take
163+
* place.
164+
* <p>Default is {@value #DEFAULT_ESCAPE_CHARACTER}.
165+
* @since 6.2
166+
*/
167+
public void setEscapeCharacter(@Nullable Character escsEscapeCharacter) {
168+
this.escapeCharacter = escsEscapeCharacter;
169+
}
170+
154171
/**
155172
* Specify whether to trim resolved values before applying them,
156173
* removing superfluous whitespace from the beginning and end.

spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -234,7 +234,8 @@ private class PlaceholderResolvingStringValueResolver implements StringValueReso
234234

235235
public PlaceholderResolvingStringValueResolver(Properties props) {
236236
this.helper = new PropertyPlaceholderHelper(
237-
placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
237+
placeholderPrefix, placeholderSuffix, valueSeparator,
238+
ignoreUnresolvablePlaceholders, escapeCharacter);
238239
this.resolver = new PropertyPlaceholderConfigurerResolver(props);
239240
}
240241

spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -193,6 +193,7 @@ protected void processProperties(ConfigurableListableBeanFactory beanFactoryToPr
193193
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
194194
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
195195
propertyResolver.setValueSeparator(this.valueSeparator);
196+
propertyResolver.setEscapeCharacter(this.escapeCharacter);
196197

197198
StringValueResolver valueResolver = strVal -> {
198199
String resolved = (this.ignoreUnresolvablePlaceholders ?

spring-context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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,7 @@
4040
import org.springframework.core.io.support.EncodedResource;
4141
import org.springframework.core.io.support.PropertiesLoaderUtils;
4242
import org.springframework.core.io.support.PropertySourceFactory;
43+
import org.springframework.util.PlaceholderResolutionException;
4344

4445
import static org.assertj.core.api.Assertions.assertThat;
4546
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -132,7 +133,7 @@ void withCustomFactoryAsMeta() {
132133
void withUnresolvablePlaceholder() {
133134
assertThatExceptionOfType(BeanDefinitionStoreException.class)
134135
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
135-
.withCauseInstanceOf(IllegalArgumentException.class);
136+
.withCauseInstanceOf(PlaceholderResolutionException.class);
136137
}
137138

138139
@Test

spring-context/src/test/java/org/springframework/context/config/ContextNamespaceHandlerTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -28,6 +28,7 @@
2828
import org.springframework.context.support.GenericXmlApplicationContext;
2929
import org.springframework.core.io.ClassPathResource;
3030
import org.springframework.mock.env.MockEnvironment;
31+
import org.springframework.util.PlaceholderResolutionException;
3132

3233
import static org.assertj.core.api.Assertions.assertThat;
3334
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -136,7 +137,7 @@ void propertyPlaceholderLocationWithSystemPropertyMissing() {
136137
assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() ->
137138
new ClassPathXmlApplicationContext("contextNamespaceHandlerTests-location-placeholder.xml", getClass()))
138139
.havingRootCause()
139-
.isInstanceOf(IllegalArgumentException.class)
140+
.isInstanceOf(PlaceholderResolutionException.class)
140141
.withMessage("Could not resolve placeholder 'foo' in value \"${foo}\"");
141142
}
142143

spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.core.io.Resource;
3838
import org.springframework.core.testfixture.env.MockPropertySource;
3939
import org.springframework.mock.env.MockEnvironment;
40+
import org.springframework.util.PlaceholderResolutionException;
4041

4142
import static org.assertj.core.api.Assertions.assertThat;
4243
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -170,7 +171,7 @@ void ignoreUnresolvablePlaceholders_falseIsDefault() {
170171
assertThatExceptionOfType(BeanDefinitionStoreException.class)
171172
.isThrownBy(() -> ppc.postProcessBeanFactory(bf))
172173
.havingCause()
173-
.isExactlyInstanceOf(IllegalArgumentException.class)
174+
.isExactlyInstanceOf(PlaceholderResolutionException.class)
174175
.withMessage("Could not resolve placeholder 'my.name' in value \"${my.name}\"");
175176
}
176177

@@ -201,8 +202,8 @@ public void ignoreUnresolvablePlaceholdersInAtValueAnnotation__falseIsDefault()
201202
assertThatExceptionOfType(BeanCreationException.class)
202203
.isThrownBy(context::refresh)
203204
.havingCause()
204-
.isExactlyInstanceOf(IllegalArgumentException.class)
205-
.withMessage("Could not resolve placeholder 'enigma' in value \"${enigma}\"");
205+
.isExactlyInstanceOf(PlaceholderResolutionException.class)
206+
.withMessage("Could not resolve placeholder 'enigma' in value \"${enigma}\" <-- \"${my.key}\"");
206207
}
207208

208209
@Test

spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -521,6 +521,11 @@ public void setValueSeparator(@Nullable String valueSeparator) {
521521
this.propertyResolver.setValueSeparator(valueSeparator);
522522
}
523523

524+
@Override
525+
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
526+
this.propertyResolver.setEscapeCharacter(escapeCharacter);
527+
}
528+
524529
@Override
525530
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
526531
this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders);

spring-core/src/main/java/org/springframework/core/env/AbstractPropertyResolver.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -61,6 +61,9 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
6161
@Nullable
6262
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
6363

64+
@Nullable
65+
private Character escapeCharacter = SystemPropertyUtils.ESCAPE_CHARACTER;
66+
6467
private final Set<String> requiredProperties = new LinkedHashSet<>();
6568

6669

@@ -121,6 +124,19 @@ public void setValueSeparator(@Nullable String valueSeparator) {
121124
this.valueSeparator = valueSeparator;
122125
}
123126

127+
/**
128+
* Specify the escape character to use to ignore placeholder prefix
129+
* or value separator, or {@code null} if no escaping should take
130+
* place.
131+
* <p>The default is "\".
132+
* @since 6.2
133+
* @see org.springframework.util.SystemPropertyUtils#ESCAPE_CHARACTER
134+
*/
135+
@Override
136+
public void setEscapeCharacter(@Nullable Character escapeCharacter) {
137+
this.escapeCharacter = escapeCharacter;
138+
}
139+
124140
/**
125141
* Set whether to throw an exception when encountering an unresolvable placeholder
126142
* nested within the value of a given property. A {@code false} value indicates strict
@@ -232,7 +248,7 @@ protected String resolveNestedPlaceholders(String value) {
232248

233249
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
234250
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
235-
this.valueSeparator, ignoreUnresolvablePlaceholders);
251+
this.valueSeparator, ignoreUnresolvablePlaceholders, this.escapeCharacter);
236252
}
237253

238254
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {

spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -74,6 +74,14 @@ public interface ConfigurablePropertyResolver extends PropertyResolver {
7474
*/
7575
void setValueSeparator(@Nullable String valueSeparator);
7676

77+
/**
78+
* Specify the escape character to use to ignore placeholder prefix
79+
* or value separator, or {@code null} if no escaping should take
80+
* place.
81+
* @since 6.2
82+
*/
83+
void setEscapeCharacter(@Nullable Character escapeCharacter);
84+
7785
/**
7886
* Set whether to throw an exception when encountering an unresolvable placeholder
7987
* nested within the value of a given property. A {@code false} value indicates strict

spring-core/src/main/java/org/springframework/core/io/support/PropertySourceProcessor.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 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.
@@ -36,6 +36,7 @@
3636
import org.springframework.core.io.ResourceLoader;
3737
import org.springframework.lang.Nullable;
3838
import org.springframework.util.Assert;
39+
import org.springframework.util.PlaceholderResolutionException;
3940
import org.springframework.util.ReflectionUtils;
4041

4142
/**
@@ -93,8 +94,8 @@ public void processPropertySource(PropertySourceDescriptor descriptor) throws IO
9394
}
9495
}
9596
catch (RuntimeException | IOException ex) {
96-
// Placeholders not resolvable (IllegalArgumentException) or resource not found when trying to open it
97-
if (ignoreResourceNotFound && (ex instanceof IllegalArgumentException || isIgnorableException(ex) ||
97+
// Placeholders not resolvable or resource not found when trying to open it
98+
if (ignoreResourceNotFound && (ex instanceof PlaceholderResolutionException || isIgnorableException(ex) ||
9899
isIgnorableException(ex.getCause()))) {
99100
if (logger.isInfoEnabled()) {
100101
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());

0 commit comments

Comments
 (0)