Evaluating ${my.property} as a SpEL expression wit

2019-05-25 06:31发布

问题:

Long story short:

Is there a way to interpret the string resulting from ${my.property} as a SpEL expression within a @Value annotation without using converters, e.g. something like @Value("#{${my.property}})?


I have an abstract factory (simplified) that lets me build some common objects that are part of the configuration of my system.

@Component
public class Factory {
  public Product makeVal(int x) { return new Product(5); }
}

In order to be more flexible, I'd like to let users write SpEL expressions in the app.properties file, so that the factory can directly be accessed:

my.property = @Factory.makeVal(12)

Now, in the class needing this property, to achieve my goal I wrote the following code.

@Value("#{${my.property}}")
private Product obj;

I thought that ${my.property} would be be macro-expanded and then evaluated by #{} as the corresponding SpEL expression, @Factory.makeVal(12) in the example above. Unfortunately, this wasn't the case, and loading the Spring context resulted in an error saying that it could not convert a string (the property's value ${my.property}) to the destination type Product.

Now, I solved this by writing a class implementing Converter<String, Product>, but it's very convoluted as I need there to programmatically evaluate the string as a SpEL expression by instantiating the ExpressionParser and so on.

But is there a simpler solution? Is there a single SpEL expression to be put in @Value annotations that lets me simply evaluate ${my.property} as a SpEL expression by itself, please?

回答1:

Maybe it just a matter of replacing @Factory with factory in the property value. This test passes for me:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpelTest.Config.class })
public class SpelTest
{
    @Value("#{${my.property}}")
    Product _product;

    @Test
    public void evaluating_spel_from_property_value() throws Exception
    {
        Assert.assertEquals(1234, _product.value);
    }

    @Component
    public static class Factory
    {
        public Product makeVal(int x) { return new Product(x); }
    }

    public static class Product
    {
        public final int value;

        public Product(final int value) { this.value = value; }
    }

    @Configuration
    @ComponentScan(basePackageClasses = SpelTest.class)
    public static class Config
    {
        @Bean
        public Factory factory() { return new Factory(); }

        @Bean
        public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
            final PropertySourcesPlaceholderConfigurer psc = new PropertySourcesPlaceholderConfigurer();
            final MutablePropertySources sources = new MutablePropertySources();
            sources.addFirst(new MockPropertySource()
                .withProperty("my.property", 
                          "factory.makeVal(1234)"));
            psc.setPropertySources(sources);
            return psc;
        }
    }
}