【问题标题】:Using application-wide properties in SpEL (programmatically)在 SpEL 中使用应用程序范围的属性(以编程方式)
【发布时间】:2016-01-05 14:59:02
【问题描述】:

我需要构建一个小模块,将SpEL 编码的表达式转换为字符串。这是为了丢弃 Jexl 并访问应用程序上下文。

例如,如果我正确配置的属性文件包含

application.name=AppTest
company.name=ACME Inc.

我想根据属性翻译下面的路径,或者类似的字符串

/path/to/#{application.name}/#{company.name}

# 或 $ 对我来说无关紧要

通常在 Spring 中,您可以将属性注入到 bean 中,我已经成功了。但现在我希望用户输入一个模板字符串,该字符串可以使用应用程序上下文中的所有属性进行翻译。目前我不需要访问 bean 属性,但将来可能会有。以上是定义文件夹路径时最常见场景的简化变体。运行时参数(例如一天中的时间)增加了一点复杂性,但我提出的问题是按部就班地工作。

所以我在 JUnit 的帮助下尝试通过玩 Spring 表达式来理解它们。我编写了以下代码,但无法使其工作

代码

final HashMap<String, Object> propertySource = new HashMap<String, Object>();
private final String FOLDER_PATTERN = "#tmp/appTest/#{company}_#{appname}/q1"; //#tmp is only token being replaced "hardcoded", not passed to Spring

@Before
public void setUp() throws Exception
{

    propertySource.put("appname", APPNAME);
    propertySource.put("company", COMPANY);
    applicationContext.getEnvironment()
                      .getPropertySources()
                      .addLast(new MapPropertySource("test", propertySource));

}

@Test
public void playWithExpression()
{
    ExpressionParser expParser = new SpelExpressionParser();
    StandardEvaluationContext stdEvaluationContext = new StandardEvaluationContext();
    stdEvaluationContext.setBeanResolver(new BeanFactoryResolver(applicationContext.getBeanFactory()));
    // stdEvaluationContext.setVariables(propertySource);

    final TemplateParserContext templateParserContext = new TemplateParserContext();

    String folderPattern = FOLDER_PATTERN.replace("#tmp", SYSTEM_TEMP_DIR);

    String realPath = expParser.parseExpression(folderPattern, templateParserContext)
                               .getValue(stdEvaluationContext, String.class);

    String calculatedPath = folderPattern.replace("#{company}", COMPANY)
                                         .replace("#{appname}", APPNAME);

    assertEquals(calculatedPath, realPath);

}

说明

部分复制粘贴其他示例,我:

  • 注入自定义属性源以仅为当前 JUnit 测试添加属性
  • 使用 BeanFactory 作为 bean 解析器来实例化 EvaluationContext
  • 使用使用#{..} 表示法的默认模板解析器上下文解析表达式
  • 使用上述评估上下文检索该表达式的值。 这失败了

表达式实例化成功但getValue抛出异常

org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'company' cannot be found on null
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:220)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:81)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:131)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:299)
    at org.springframework.expression.common.CompositeStringExpression.getValue(CompositeStringExpression.java:89)
    at org.springframework.expression.common.CompositeStringExpression.getValue(CompositeStringExpression.java:136)
    at it.phoenix.web.data.managers.test.FoldersManagerTemplateTests.playWithExpression(FoldersManagerTemplateTests.java:80)

从错误来看,Spring 似乎在尝试将company 令牌解析为根对象的属性,而本例中缺少该属性。

如果我使用 #{@...} 表示法,那么 Spring 会尝试将令牌解释为 bean 标识符。不是我的情况。

如何在表达式中解析 A​​ppContext 的 PropertySources 中的属性?

【问题讨论】:

    标签: java spring spring-el


    【解决方案1】:

    可能有更优雅的解决方案,但是

    private final String FOLDER_PATTERN = "#tmp/appTest/#{environment.getProperty('company')}"
            + "_#{environment.getProperty('appname')}/q1";  // #tmp is only token being replaced
                                                            // "hardcoded", not passed to Spring
    

        String realPath = expParser.parseExpression(folderPattern, templateParserContext)
                .getValue(stdEvaluationContext, applicationContext, String.class);
    

    有效(即在评估中使用应用程序上下文作为根对象)。

    编辑

    稍微好一点...

    private final String FOLDER_PATTERN = "#tmp/appTest/#{getProperty('company')}"
            + "_#{getProperty('appname')}/q1";  // #tmp is only token being replaced
                                                // "hardcoded", not passed to Spring
    

        String realPath = expParser.parseExpression(folderPattern, templateParserContext)
                .getValue(stdEvaluationContext, applicationContext.getEnvironment(), String.class);
    

    【讨论】:

      猜你喜欢
      • 2011-06-07
      • 2021-09-13
      • 2020-11-09
      • 2020-12-03
      • 2023-04-08
      • 2011-12-18
      • 2022-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多