在 SpEL 中使用应用程序范围的属性(以编程方式)
Using application-wide properties in SpEL (programmatically)
我需要构建一个小模块,将 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 标识符。不是我的情况。
如何在表达式中解析 AppContext PropertySource
的属性?
可能有更优雅的解决方案,但是
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);
我需要构建一个小模块,将 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 标识符。不是我的情况。
如何在表达式中解析 AppContext PropertySource
的属性?
可能有更优雅的解决方案,但是
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);