【问题标题】:How to programmatically resolve property placeholder in Spring如何在 Spring 中以编程方式解析属性占位符
【发布时间】:2011-07-07 13:00:07
【问题描述】:

我目前正在开发一个基于 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


    【解决方案1】:

    自 Spring 3.0.3 以来,EmbeddedValueResolverAware 的工作方式与另一篇使用 appContext.getBeanFactory().resolveEmbeddedValue("${prop}") 调用的帖子中提到的相同。

    解决问题:

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

    2. 然后您可以在哪里检索属性,如代码 sn-p 所示:

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

    那么你的 bean 就不需要依赖 ApplicationContext 来检索你需要的属性了。

    【讨论】:

    • 这是解决问题的一种非常简单的方法。对于任何未来的读者,只需将 EmbeddedValueResolverAware 接口添加到 spring 管理的类中,您希望能够在其中以编程方式解析属性,实现 setter 方法,并使用 @Eds 编写的代码来解析。
    • 事实上,Alex 的评论值得点赞。你不想编辑答案吗?
    • 这是我见过的唯一一种似乎可以接受的编写共享组件/配置的方法,这些组件/配置必须适用于多个 Spring 应用程序。您不希望所述代码依赖于特定应用程序如何从文件或其他方式获取其属性配置。
    • 感谢@Alex,它的工作如我所愿,同样适用于 Spring 5,其中 ApplicationContext.getBeanFactory() 方法不再起作用。请记住按照描述将您的属性包装在“${...}”中,很容易忘记并想知道为什么您只能拿回钥匙;)
    【解决方案2】:

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

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

    javadoc 声明此方法用于解析诸如注释属性之类的嵌入值,所以也许我们正在规避它的使用,但它确实有效。

    【讨论】:

    • 真的非常感谢!现在正在寻找一种干净的方式来查询环境。
    【解决方案3】:

    我认为与其关注上下文占位符的内部工作,不如简单地定义一个新的 util:properties,如下所示:

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

    在你的代码中,像这样使用它:

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

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

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

    【讨论】:

    • 谢谢你,Ritesh。我想我可以使用这个解决方案,以防找不到更优雅的方法。
    • 将此答案标记为已接受,但无论如何找到纯编程解决方案很有趣。
    • @Max Alexejev 您还可以扩展 PropertyPlaceholderConfigurer 并捕获在第二个参数中传递给 processProperties 方法的已解析属性。但是你将不得不使用 bean 配置而不是 context:property-placeholder。
    • 我认为这个答案更“优雅”:stackoverflow.com/a/11647231/120794。也适用于 spring 2.5。
    【解决方案4】:

    一种选择是将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");
        }
    }
    

    可选择使用@Configuration 注释Foo 类,以享受编程配置的强大功能,以支持XML。

    【讨论】:

    • 太棒了!这正是我想要的,谢谢
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      只是为了添加一个完整的答案,我正在添加这个。

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

      import org.springframework.context.EmbeddedValueResolverAware;
      import org.springframework.util.StringValueResolver;
      import org.springframework.lang.Nullable;
      
      public class MyValueResolver implements EmbeddedValueResolverAware {
          //this will be injected using the setter injection.
          //Spring already has an implementation org.springframework.beans.factory.config.EmbeddedValueResolver
          //Or you can implement your own and use @Primary
          @Nullable
          private StringValueResolver resolver;
      
          @Override
          public void setEmbeddedValueResolver(StringValueResolver resolver) {
              this.resolver = resolver;
          }
      
          public String readMyProperty(String propertyString){
              return resolver.resolveStringValue(propertyString);
          }
      }
      

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

      鉴于在.properties 文件中显示为

      my.property=myPropertyValue
      

      【讨论】:

      • 从哪里获得StringValueResolver?请清除。
      • @Arman 已经添加了,导入 org.springframework.util.StringValueResolver;
      • 我想我的问题还不够清楚。我知道 Spring 知道 StringValueResolver,但是为了使用 MyValueResolver,它必须以某种方式构造。那么如何获取StringValueResolver的实例?
      • 非常抱歉造成误解,所以这里会发生这种 StringValueResolver 类型将被注入,所以在 Spring 中它已经有 StringValueResolver 的实现(例如:EmbeddedValueResolver 类)。或者您可以实现自己的(然后必须使用 @Primary 或其他方式来消除冲突)。我将添加此描述作为我的代码 sn-p 的注释。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      • 2014-03-23
      • 2012-07-10
      • 2021-03-08
      • 2017-01-21
      • 2016-03-04
      • 2015-01-16
      相关资源
      最近更新 更多