如何在Spring中以编程方式解析属性占位符

问题描述 投票:38回答:6

我目前正在基于Spring 3.1.0.M1的Web应用程序上工作,基于注释,并且在应用程序的一个特定位置解析属性占位符时遇到问题。

这里是故事。

1)在我的Web应用程序上下文中(由DispatcherServlet加载),我有

mvc-config.xml:

<!-- Handles HTTP GET requests for /resources/version/**  -->
<resources mapping="/${app.resources.path}/**" location="/static/" cache-period="31556926"/> 

...

<!-- Web properties -->
<context:property-placeholder location="
    classpath:app.properties
    "/>

2)在app.properties中,有2个属性,其中包括:

app.properties:

# Properties provided (filtered) by Maven itself
app.version: 0.1-SNAPSHOT
...

# Static resources mapping
app.resources.path: resources/${app.version}

3)我的JSP 2.1模板中有一个JSP定制标记。该标签负责根据环境设置,应用程序版本,spring主题选择等来进行完整的资源路径构建。自定义标签类扩展了spring:url实现类,因此它可以被视为常用的url标签,但具有有关正确路径的一些附加知识。

我的问题是我无法在JSP定制标记实现中正确解析$ {app.resources.path}。 JSP自定义标签是由servlet容器而不是Spring管理的,因此不参与DI。所以我不能只使用通常的@Value(“ $ {app.resources.path}”)并由Spring自动解决它。

我所拥有的都是Web应用程序上下文实例,所以我必须以编程方式解析我的属性。

到目前为止,我尝试过:

ResourceTag.java:

// returns null
PropertyResolver resolver = getRequestContext().getWebApplicationContext().getBean(PropertyResolver.class);
resolver.getProperty("app.resources.path");


// returns null, its the same web context instance (as expected)
PropertyResolver resolver2 = WebApplicationContextUtils.getRequiredWebApplicationContext(pageContext.getServletContext()).getBean(PropertyResolver.class);
resolver2.getProperty("app.resources.path");


// throws NPE, resolver3 is null as StringValueResolver is not bound
StringValueResolver resolver3 = getRequestContext().getWebApplicationContext().getBean(StringValueResolver.class);
resolver3.resolveStringValue("app.resources.path");


// null, since context: property-placeholder does not register itself as PropertySource
Environment env = getRequestContext().getWebApplicationContext().getEnvironment();
env.getProperty("app.resources.path");

所以现在我有点坚持了。我知道解析占位符的能力就在上下文中,我只是不知道正确的方法。如有任何帮助或想法,我们将不胜感激。

spring jsp dependency-injection jsp-tags custom-tag
6个回答
15
投票

我认为您不必像关注上下文占位符的内部工作,而是可以像这样简单地定义一个新的util:properties:

<util:properties id="appProperties" location="classpath:app.properties" />

并且在您的代码中,像这样使用它:

Properties props = appContext.getBean("appProperties", Properties.class);

或在任何可以做DI的地方都这样:

@Value("#{appProperties['app.resources.path']}")

39
投票

自Spring 3.0.3起,存在EmbeddedValueResolverAware,其作用与另一篇使用appContext.getBeanFactory().resolveEmbeddedValue("${prop}")调用的文章所提到的相同。

解决问题:

  1. 使您的类实现EmbeddedValueResolverAware接口,您将为您注入解析器

  2. 然后您将可以在其中检索代码片段中演示的属性:

    String propertyValue = resolver.resolveStringValue("${your.property.name}");
    

然后,您的bean不需要依赖ApplicationContext来检索所需的属性。


24
投票

从3.0版开始,Spring在beanFactory中保留了一个String解析器列表。您可以像这样使用它:

String value = appContext.getBeanFactory().resolveEmbeddedValue("${prop}");

javadoc将此方法声明为解决嵌入值,例如注释属性,因此也许我们正在绕过其用法,但它可以工作。


5
投票

一种选择是在PropertySource上添加MapPropertySource(此处为ConfigurableEnvironment以举例说明内存中的配置),并要求它为您解析属性。

public class Foo {

    @Autowired
    private ConfigurableEnvironment env;

    @PostConstruct
    public void setup() {
        env.getPropertySources()
           .addFirst(new MapPropertySource("my-propertysource", 
               ImmutableMap.<String, Object>of("your.property.name", "the value")));
        env.resolvePlaceholders("your.property.name");
    }
}

[可选地用Foo注释@Configuration类,以享受程序配置的强大功能,而偏向XML


1
投票

还有另一种可能的解决方案:通过AspectJ使标记类@Configurable并启用编译时或加载时编织。然后,我可以在自定义标签中使用通常的Spring @Value批注。但是,实际上,我不想仅仅因为几个类而建立编织基础结构。仍在寻找通过ApplicationContext解析占位符的方法。


0
投票

只需添加一个完整的答案,我就添加这个。

您可以使用这样的自定义类来做到这一点。

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;
import org.springframework.lang.Nullable;

public class MyValueResolver implements EmbeddedValueResolverAware {
    @Nullable
    private StringValueResolver embeddedValueResolver;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.embeddedValueResolver = resolver;
    }

    public String readMyProperty(String propertyString){
        return embeddedValueResolver.resolveStringValue(propertyString);
    }
}

propertyString应作为"${my.property}"传递。

鉴于.properties文件中的它表示为

my.property=myPropertyValue
© www.soinside.com 2019 - 2024. All rights reserved.