-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Support escaping prefix and separator in property placeholders [SPR-4953] #9628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Timo Thomas commented Apache Jakarta Commons-Lang has a similar technology known as "StrSubstitutor" (org.apache.commons.lang.text.StrSubstitutor). There, the default escape character is "$", but it is configurable (independently from the prefix and suffix). I'm using StrSubstitutor on expressions in bean properties, and if there is no escaping, the only (ugly) option left for me is to change one of the prefixes. IMHO not having an escape character nowadays should be considered as a production-critical feature (what if changing the prefix is not an option? The workarounds necessary then are even more ugly) - if not as a bug. |
Eric Haszlakiewicz commented It seems that you can work around this by defining a property that has a value of '$'. e.g. and in the xml config to get a value of e.g. '$foo', you'd write '${dollarsign}foo' It's not pretty, but at least it works. |
Jim Utter commented The workaround does not work when the replacement needs the braces passed through. properties: wiring: or even "${var}foo}" or finally "${var}" still results in an error like: Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'id' defined in class path resource [applicationContext.xml]: Could not resolve placeholder 'foo' For me, I'd like to define concatenated strings like "${var1}-${var2}${var3}" etc.. where the variables would be internally replaced by velocity (requiring the braces when concatenated). |
Joshua Caplan commented The following blog post details a workaround that is available starting from Spring 3.0.x (or whenever SpEL was introduced): http://jazzjuice.blogspot.com/2011/06/escaping-property-placeholders-in.html I still agree that native escaping would be useful, though. |
Aleksandr Dubinsky commented Indeed, However, it is too long (5 characters!). It is poorly documented. It exists by sheer accident. It is really shocking that an escape sequence does not exist. Btw, the alternative of defining a different placeholder sequence is a really, really bad idea. That feature should NOT even be there. Allowing users to arbitrarily re-define the basic syntax of a language leads to confusion. |
Michel Nolard commented
Interesting fact: there is a lot advices on the net to use your trick -- often without referencing your post properly, but this does not remove the need for this issue to be solved anyway ! |
Jan Zidek commented In YAML configuration files,
then it is not ignored, but when the parameter is set by |
Bulk closing outdated, unresolved issues. Please, reopen if still relevant. |
I really would welcome a "fix" for this. In my app I externalized config into When I have I worked around this issue by putting @Value("${my.app.var}")
private String var;
public String getVar() {
return fixed(var);
}
private String fixed(String in) {
return in.replace('@', '$');
} |
Interestingly enough, core support for ignoring nested properties appears to have been in place since Spring Framework 3.2 via the However, Here's a JUnit Jupiter based test case for experimentation: package example;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
@SpringJUnitConfig
// Using the following SpEL hack allows the test to pass.
// @TestPropertySource(properties = "my.app.var = #{'$'}{var}")
@TestPropertySource(properties = "my.app.var = ${var}")
class IgnoredPlaceholderTests {
@Value("${my.app.var}")
String value;
@Test
void test() {
assertThat(value).isEqualTo("${var}");
}
@Configuration
static class Config {
@Bean
static PropertySourcesPlaceholderConfigurer pspc() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
pspc.setIgnoreUnresolvablePlaceholders(true);
return pspc;
}
}
} @jhoeller, what do you think about either introducing support for escaping placeholders or providing a mechanism for configuring the |
it's amazing the issue lasts for more than ten years. When I use sharding-jdbc which configuration also need '${xxx}', I faced this problem. |
Will it be fixed at all? |
Any progress? Any comment? :) |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
@snicoll : my frustration stems mostly from the feeling that the team waste their valuable time with destructive changes which nobody wants, instead of addressing the actual issues or even bugs, reported and complained about for 10+ years. The recent "trailing slash upgrade" broke literally thousands of enterprise applications. You should not be surprised that someone may be frustrated. |
This commit provides a rewrite of the parser for properties containing potentially placeholders. Assuming a source where `firstName` = `John` and `lastName` = `Smith`, the "${firstName}-${lastName}" property is evaluated as "John-Smith". Compared with the existing implementation in PropertyPlaceholderHelper, the new implementation offers the following extra features: 1. Placeholder can be escaped using a configurable escape character. When a placeholder is escaped it is rendered as is. This does apply to any nested placeholder that wouldn't be escaped. For instance, "\${firstName}" is evaluated as "${firstName}". 2. The default separator can also be escaped the same way. When the separator is escaped, the left and right parts are not considered as the key and the default value respectively. Rather the two parts combined, including the separator (but not the escape separator) are used for resolution. For instance, ${java\:comp/env/test} is looking for a "java:comp/env/test" property. 3. Placeholders are resolved lazily. Previously, all nested placeholders were resolved before considering if a separator was present. This implementation only attempts the resolution of the default value if the key does not provide a value. 4. Failure to resolve a placeholder are more rich, with a dedicated PlaceholderResolutionException that contains the resolution chain. See spring-projectsgh-9628 See spring-projectsgh-26268
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
This commit provides a rewrite of the parser for properties containing potentially placeholders. Assuming a source where `firstName` = `John` and `lastName` = `Smith`, the "${firstName}-${lastName}" property is evaluated as "John-Smith". Compared with the existing implementation in PropertyPlaceholderHelper, the new implementation offers the following extra features: 1. Placeholder can be escaped using a configurable escape character. When a placeholder is escaped it is rendered as is. This does apply to any nested placeholder that wouldn't be escaped. For instance, "\${firstName}" is evaluated as "${firstName}". 2. The default separator can also be escaped the same way. When the separator is escaped, the left and right parts are not considered as the key and the default value respectively. Rather the two parts combined, including the separator (but not the escape separator) are used for resolution. For instance, ${java\:comp/env/test} is looking for a "java:comp/env/test" property. 3. Placeholders are resolved lazily. Previously, all nested placeholders were resolved before considering if a separator was present. This implementation only attempts the resolution of the default value if the key does not provide a value. 4. Failure to resolve a placeholder are more rich, with a dedicated PlaceholderResolutionException that contains the resolution chain. See gh-9628 See gh-26268
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 gh-9628
To smooth upgrade from 6.1.x, this commit makes sure that code that used to catch an IAE to ignore a faulty placeholder resolution still works. See gh-9628
Spring Framework 6.2 introduced support for an escape character for property placeholders (by default '\'). However, as of Spring Framework 6.2.6, there was no way to either escape the escape character or disable escape character support. For example, given a `username` property configured with the value of `Jane.Smith` and a `DOMAIN\${username}` configuration string, property placeholder replacement used to result in `DOMAIN\Jane.Smith` prior to 6.2 but now results in `DOMAIN${username}`. Similarly, an attempt to escape the escape character via `DOMAIN\\${username}` results in `DOMAIN\${username}`. In theory, one should be able to disable use of an escape character altogether, and that is currently possible by invoking setEscapeCharacter(null) on AbstractPropertyResolver and PlaceholderConfigurerSupport (the superclass of PropertySourcesPlaceholderConfigurer). However, in reality, there are two hurdles. - As of 6.2.6, an invocation of setEscapeCharacter(null) on a PropertySourcesPlaceholderConfigurer applied to its internal top-level PropertySourcesPropertyResolver but not to any nested PropertySourcesPropertyResolver, which means that the `null` escape character could not be effectively applied. - Users may not have an easy way to explicitly set the escape character to `null` for a PropertyResolver or PropertySourcesPlaceholderConfigurer. For example, Spring Boot auto-configures a PropertySourcesPlaceholderConfigurer with the default escape character enabled. This first issue above has recently been addressed by spring-projectsgh-34861. This commit therefore addresses the second issue as follows. - To allow developers to easily revert to the pre-6.2 behavior without changes to code or configuration strings, this commit introduces a `spring.placeholder.escapeCharacter.default` property for use with SpringProperties which globally sets the default escape character that is automatically configured in AbstractPropertyResolver and PlaceholderConfigurerSupport. - Setting the property to an empty string sets the default escape character to `null`, effectively disabling the default support for escape characters. spring.placeholder.escapeCharacter.default = - Setting the property to any other character sets the default escape character to that specific character. spring.placeholder.escapeCharacter.default = ~ - Setting the property to a string containing more than one character results in an exception. - Developers are still able to configure an explicit escape character in AbstractPropertyResolver and PlaceholderConfigurerSupport if they choose to do so. - Third-party components that wish to rely on the same feature can invoke AbstractPropertyResolver.getDefaultEscapeCharacter() to obtain the globally configured default escape character. See spring-projectsgh-9628 See spring-projectsgh-34315 See spring-projectsgh-34861 Closes spring-projectsgh-34865
Spring Framework 6.2 introduced support for an escape character for property placeholders (by default '\'). However, as of Spring Framework 6.2.6, there was no way to either escape the escape character or disable escape character support. For example, given a `username` property configured with the value of `Jane.Smith` and a `DOMAIN\${username}` configuration string, property placeholder replacement used to result in `DOMAIN\Jane.Smith` prior to 6.2 but now results in `DOMAIN${username}`. Similarly, an attempt to escape the escape character via `DOMAIN\\${username}` results in `DOMAIN\${username}`. In theory, one should be able to disable use of an escape character altogether, and that is currently possible by invoking setEscapeCharacter(null) on AbstractPropertyResolver and PlaceholderConfigurerSupport (the superclass of PropertySourcesPlaceholderConfigurer). However, in reality, there are two hurdles. - As of 6.2.6, an invocation of setEscapeCharacter(null) on a PropertySourcesPlaceholderConfigurer applied to its internal top-level PropertySourcesPropertyResolver but not to any nested PropertySourcesPropertyResolver, which means that the `null` escape character could not be effectively applied. - Users may not have an easy way to explicitly set the escape character to `null` for a PropertyResolver or PropertySourcesPlaceholderConfigurer. For example, Spring Boot auto-configures a PropertySourcesPlaceholderConfigurer with the default escape character enabled. This first issue above has recently been addressed by gh-34861. This commit therefore addresses the second issue as follows. - To allow developers to easily revert to the pre-6.2 behavior without changes to code or configuration strings, this commit introduces a `spring.placeholder.escapeCharacter.default` property for use with SpringProperties which globally sets the default escape character that is automatically configured in AbstractPropertyResolver and PlaceholderConfigurerSupport. - Setting the property to an empty string sets the default escape character to `null`, effectively disabling the default support for escape characters. spring.placeholder.escapeCharacter.default = - Setting the property to any other character sets the default escape character to that specific character. spring.placeholder.escapeCharacter.default = ~ - Setting the property to a string containing more than one character results in an exception. - Developers are still able to configure an explicit escape character in AbstractPropertyResolver and PlaceholderConfigurerSupport if they choose to do so. - Third-party components that wish to rely on the same feature can invoke AbstractPropertyResolver.getDefaultEscapeCharacter() to obtain the globally configured default escape character. See gh-9628 See gh-34315 See gh-34861 Closes gh-34865
Uh oh!
There was an error while loading. Please reload this page.
Chris Lee opened SPR-4953 and commented
There is presently no way to inject a placeholder expression (
${...}
) that won't be picked up byPropertyPlaceholderConfigurer
. Ideally we should be able to inject a string that contains${...}
for later use in its target bean without involvement fromPropertyPlaceholderConfigurer
.Perhaps a simple escape mechanism using the conventional backslash, such as
"The current value is \\${...}"
would suffice.Affects: 2.5.4, 2.5.5
11 votes, 5 watchers
The text was updated successfully, but these errors were encountered: