评估 ${my.property} 作为 @Value 注释中的 SpEL 表达式

Evaluating ${my.property} as a SpEL expression within a @Value annotation

长话短说:

有没有一种方法可以在不使用转换器的情况下将 ${my.property} 产生的字符串解释为 @Value 注释中的 SpEL 表达式,例如像 @Value("#{${my.property}})?


我有一个抽象工厂(简化版),可以让我构建一些通用对象,这些对象是我系统配置的一部分。

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

为了更灵活,我想让用户在app.properties文件中写SpEL表达式,这样工厂就可以直接访问了:

my.property = @Factory.makeVal(12)

现在,在 class 中需要这个 属性,为了实现我的目标,我编写了以下代码。

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

我以为${my.property}会被宏展开,然后被#{}计算为对应的SpEL表达式,上例中的@Factory.makeVal(12)。不幸的是,情况并非如此,加载 Spring 上下文导致错误,指出它无法将字符串(属性 的值 ${my.property})转换为目标类型Product.

现在,我通过编写 class 实现 Converter<String, Product> 解决了这个问题,但它非常复杂,因为我需要在那里通过实例化 ExpressionParser 以编程方式将字符串评估为 SpEL 表达式等等。

但是有更简单的解决方案吗? @Value 注释中是否有单个 SpEL 表达式,让我可以简单地将 ${my.property} 本身评估为 SpEL 表达式,好吗?

也许只需将 属性 值中的 @Factory 替换为 factory 即可。我通过了这个测试:

@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;
        }
    }
}