Chris Lee opened SPR-4953 and commented

There is presently no way to inject a placeholder expression (${...}) that won't be picked up by PropertyPlaceholderConfigurer. Ideally we should be able to inject a string that contains ${...} for later use in its target bean without involvement from PropertyPlaceholderConfigurer.

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

Comment From: spring-projects-issues

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.

Comment From: spring-projects-issues

Eric Haszlakiewicz commented

It seems that you can work around this by defining a property that has a value of '$'. e.g. dollarsign=$

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.

Comment From: spring-projects-issues

Jim Utter commented

The workaround does not work when the replacement needs the braces passed through. For example:

properties: var=$

wiring: "${var}{foo}"

or even var=${

"${var}foo}"

or finally var=${foo}

"${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' at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:268)

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).

Comment From: spring-projects-issues

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.

Comment From: spring-projects-issues

Aleksandr Dubinsky commented

Indeed, #{'$'}{...} seems to be a valid escape sequence for ${...}.

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.

Comment From: spring-projects-issues

Michel Nolard commented

@Aleksandr Dubinsky: I agree to a 100% with you.

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 !

Comment From: spring-projects-issues

Jan Zidek commented

In YAML configuration files, # is the line comment sign, the rest of the line is ignored. If I escape it with backslah, so I use

\#{'$'}

then it is not ignored, but when the parameter is set by @ConfigurationProperties, the Java string simply contains the sequence as was written in YAML.

Comment From: spring-projects-issues

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.

Comment From: mar1ged

I really would welcome a "fix" for this.

In my app I externalized config into application.yml, this configuration contains ${var} entries which I later on fill in using PropertyPlaceholderHelper in my own code.

When I have ${var} in my application.yml the app does not load because the value of var will be filled in later and depends on user input.

I worked around this issue by putting @{var} into the YAML file and replacing @ with $ at runtime. But this is not very elegant because I have to do something like this:

    @Value("${my.app.var}")
    private String var;

    public String getVar() {
        return fixed(var);
    }

    private String fixed(String in) {
        return in.replace('@', '$');
    }

Comment From: sbrannen

Interestingly enough, core support for ignoring nested properties appears to have been in place since Spring Framework 3.2 via the ConfigurablePropertyResolver.setIgnoreUnresolvableNestedPlaceholders(boolean) flag.

However, PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(ConfigurableListableBeanFactory) instantiates a PropertySourcesPropertyResolver (which is a subtype of ConfigurablePropertyResolver) without providing the option to set any flags in the property resolver.

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 PropertySourcesPropertyResolver used by PropertySourcesPlaceholderConfigurer?

Comment From: libinglong

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.

Comment From: unrec

Will it be fixed at all?

Comment From: honza-zidek

@jhoeller, @rstoyanchev, @sbrannen, @sdeleuze, @bclozel, @poutsma, @snicoll, @izeye - any idea if this may be fixed? :) It has been opened for over 13 years without much attention, so I am trying to attract your eyes here...

Comment From: honza-zidek

Any progress? Any comment? :)

Comment From: sbrannen

The team currently plans to look into escaping support in the 6.0.x timeline.

Comment From: sbrannen

The team currently plans to look into escaping support in the 6.0.x timeline.

In the interim, you may find it useful to ignore unresolvable placeholders when using PropertySourcesPlaceholderConfigurer, and I just pushed a fix for ignoring unresolvable placeholders in @Value annotations in conjunction with #27947.

That fix will be available in 5.3.16.

Comment From: sebastianhaberey

+1 for escaping!

Comment From: honza-zidek

Hey team, any progress here?

Comment From: snicoll

@honza-zidek did you take the time to look at the PR link right above your comment? If not, you can see we're working on it: https://github.com/spring-projects/spring-framework/pull/30671

Comment From: honza-zidek

@snicoll Thanks, I haven't noticed. I am happy that the team reacted to the issues after 15 years - better late than never :)

Comment From: snicoll

I realize that you're probably frustrated, but sarcasm such as this isn't helpful.

Comment From: honza-zidek

I realize that you're probably frustrated, but sarcasm such as this isn't helpful.

@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.