【问题标题】:Access properties file programmatically with Spring?使用 Spring 以编程方式访问属性文件?
【发布时间】:2010-12-18 18:35:54
【问题描述】:

我们使用下面的代码从属性文件中注入带有属性的 Spring bean。

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:/my.properties"/>
</bean>

<bean id="blah" class="abc">
    <property name="path" value="${the.path}"/>
</bean>

有没有一种方法可以通过编程方式访问属性?我正在尝试做一些没有依赖注入的代码。所以我想要一些这样的代码:

PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");

【问题讨论】:

标签: spring properties


【解决方案1】:

PropertiesLoaderUtils怎么样?

Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);

【讨论】:

  • 这里有个问题,这和我的有什么不同,还有两张票,第二张...
  • 打败了我,我没有投票权 :) 我不会使用 PropertyPlaceholderConfigurer,尽管它对于这项任务来说太过分了。
  • 我试图尽可能地接近他所拥有的,因为没有提供足够的细节,我被多次否决。无论如何,你的答案值得投票,因为它是正确的,我想我只是嫉妒我没有得到 2 票,哈哈。
  • 如果文件放在外部目录中,我们应该在路径中给出什么,比如说配置文件夹?
  • @prnjn 我会假设 'new FileSystemResource(fullPath)'
【解决方案2】:

如果您只想从代码中访问占位符值,则可以使用 @Value 注释:

@Value("${settings.some.property}")
String someValue;

要从 SPEL 访问占位符,请使用以下语法:

#('${settings.some.property}')

要将配置暴露给已关闭 SPEL 的视图,可以使用以下技巧:

package com.my.app;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {  
    ConfigurableBeanFactory beanFactory; 

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
    }

    protected String resolveProperty(String name) {
        String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");

        return rv;
    }

    @Override
    public String get(Object key) {
        return resolveProperty(key.toString());
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            resolveProperty(key.toString());
            return true;
        }
        catch(Exception e) {
            return false;
        }
    }

    @Override public boolean isEmpty() { return false; }
    @Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
    @Override public Collection<String> values() { throw new UnsupportedOperationException(); }
    @Override public int size() { throw new UnsupportedOperationException(); }
    @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}

然后使用暴露器将属性暴露给视图:

<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    <property name="attributesMap">
        <map>
            <entry key="config">
                <bean class="com.my.app.PropertyPlaceholderExposer" />
            </entry>
        </map>
    </property>
</bean>

然后在视图中,像这样使用暴露的属性:

${config['settings.some.property']}

此解决方案的优点是您可以依赖标准占位符 context:property-placeholder 标签注入的实现。

现在作为最后一点,如果您确实需要捕获所有占位符属性及其值,您必须通过 StringValueResolver 对它们进行管道传输,以确保占位符在属性值内按预期工作。下面的代码会做到这一点。

package com.my.app;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;

public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {

    Map<String, String> props = new HashMap<String, String>();

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {

        this.props.clear();
        for (Entry<Object, Object> e: props.entrySet())
            this.props.put(e.getKey().toString(), e.getValue().toString());

        super.processProperties(beanFactory, props);
    }

    @Override
    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            StringValueResolver valueResolver) {

        super.doProcessProperties(beanFactoryToProcess, valueResolver);

        for(Entry<String, String> e: props.entrySet())
            e.setValue(valueResolver.resolveStringValue(e.getValue()));
    }

    // Implement map interface to access stored properties
    @Override public Set<String> keySet() { return props.keySet(); }
    @Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
    @Override public Collection<String> values() { return props.values(); }
    @Override public int size() { return props.size(); }
    @Override public boolean isEmpty() { return props.isEmpty(); }
    @Override public boolean containsValue(Object value) { return props.containsValue(value); }
    @Override public boolean containsKey(Object key) { return props.containsKey(key); }
    @Override public String get(Object key) { return props.get(key); }
    @Override public void clear() { throw new UnsupportedOperationException(); }
    @Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
    @Override public String remove(Object key) { throw new UnsupportedOperationException(); }
    @Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}

【讨论】:

  • 感谢这个非常完整的答案!有没有办法用 final 字段做到这一点?
  • @WardC 您无法注入最终字段。但是,您可以注入构造函数参数并在构造函数内设置最终字段值。见stackoverflow.com/questions/2306078/…stackoverflow.com/questions/4203302/…
  • 我尝试了 SpEL 方式,但它抛出了 'Invalid collection name: #('${producer.mongodb.collection}')'。我配置为:@Document(collection = "#('${producer.mongodb.collection}')")
【解决方案3】:

信用Programmatic access to properties in Spring without re-reading the properties file

我找到了一个很好的实现,它可以在 spring 中以编程方式访问属性,而无需重新加载 spring 已经加载的相同属性。 [另外,不需要硬编码源中的属性文件位置]

通过这些更改,代码看起来更简洁且更易于维护。

这个概念很简单。只需扩展spring默认属性占位符(PropertyPlaceholderConfigurer),并捕获它加载到局部变量中的属性

public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {

    private static Map<String, String> propertiesMap;
    // Default as in PropertyPlaceholderConfigurer
    private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;

    @Override
    public void setSystemPropertiesMode(int systemPropertiesMode) {
        super.setSystemPropertiesMode(systemPropertiesMode);
        springSystemPropertiesMode = systemPropertiesMode;
    }

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
        super.processProperties(beanFactory, props);

        propertiesMap = new HashMap<String, String>();
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
            propertiesMap.put(keyStr, valueStr);
        }
    }

    public static String getProperty(String name) {
        return propertiesMap.get(name).toString();
    }

}

使用示例

SpringPropertiesUtil.getProperty("myProperty")

Spring 配置更改

<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    <property name="locations">
    <list>
        <value>classpath:myproperties.properties</value>
    </list>
    </property>
</bean>

希望这有助于解决您遇到的问题

【讨论】:

  • 这不是一个完整的实现,将无法正常工作。 PropertyPlaceholderConfigurer 使用 PropertyPlaceholderHelper 替换所有占位符属性,包括嵌套占位符。在 Kalinga 的实现中,如果您有类似 myFile=${myFolder}/myFile.txt 之类的内容,则使用键“myFile”从地图中获得的文字属性值将是 ${myFolder}/myFile.txt。
  • 这是正确的解决方案。解决布赖恩的顾虑。 ${myFolder} 应该是系统属性,而不是在属性文件中。这可以通过在eclipse中设置tomcat系统属性或运行属性来解决。您甚至可以拥有一个构建属性。这个解决方案假设一点并且应该解决这个问题,但同时这个答案更符合标准做法,在一个地方而不是单独加载 spring 和 java 属性。另一种选择是在文件中加载带有 myFile 的通用属性文件,然后使用它来获取其余部分。
  • 我尝试将此解决方法应用于 Spring 3.1+ 中的“新” PropertySourcesPlaceholderConfigurer 但我发现方法 processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) 现在已弃用,因此现在无法访问“道具”论点。查看 PropertySourcesPlaceholderConfigurer 的来源找不到公开属性的干净方法。有什么想法吗?谢谢!
【解决方案4】:

我已经做到了,并且成功了。

Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);

应该可以的。

【讨论】:

    【解决方案5】:

    您也可以使用 spring 工具,或通过 PropertiesFactoryBean 加载属性。

    <util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>
    

    或:

    <bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
      <property name="location" value="classpath:com/foo/myprops.properties"/>
    </bean>
    

    然后你可以在你的应用程序中选择它们:

    @Resource(name = "myProps")
    private Properties myProps;
    

    并在您的配置中另外使用这些属性:

    <context:property-placeholder properties-ref="myProps"/>
    

    这也在文档中:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties

    【讨论】:

      【解决方案6】:

      创建一个如下所示的类

          package com.tmghealth.common.util;
      
          import java.util.Properties;
      
          import org.springframework.beans.BeansException;
      
          import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
      
          import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
      
          import org.springframework.context.annotation.Configuration;
      
          import org.springframework.context.annotation.PropertySource;
      
          import org.springframework.stereotype.Component;
      
      
          @Component
          @Configuration
          @PropertySource(value = { "classpath:/spring/server-urls.properties" })
          public class PropertiesReader extends PropertyPlaceholderConfigurer {
      
              @Override
              protected void processProperties(
                      ConfigurableListableBeanFactory beanFactory, Properties props)
                      throws BeansException {
                  super.processProperties(beanFactory, props);
      
              }
      
          }
      

      然后,无论您想访问某个属性,请使用

          @Autowired
              private Environment environment;
          and getters and setters then access using 
      
          environment.getProperty(envName
                          + ".letter.fdi.letterdetails.restServiceUrl");
      

      --在访问器类中编写getter和setter

          public Environment getEnvironment() {
                  return environment;
              }`enter code here`
      
              public void setEnvironment(Environment environment) {
                  this.environment = environment;
              }
      

      【讨论】:

      • 到目前为止最好的答案,应该只是自动装配环境。
      【解决方案7】:

      您可以通过Environment 类获取您的属性。就文档而言:

      属性在几乎所有应用程序中都发挥着重要作用,并且可能源自多种来源:属性文件、JVM 系统属性、系统环境变量、JNDI、servlet 上下文参数、ad-hoc 属性对象、映射等.与属性相关的环境对象的作用是为用户提供一个方便的服务接口,用于配置属性源并从中解析属性。

      将环境作为env 变量,只需调用:

      env.resolvePlaceholders("${your-property:default-value}")
      

      您可以通过以下方式获取“原始”属性:

      env.getProperty("your-property")
      

      它会搜索所有spring已经注册的属性源。

      您可以通过以下方式获得环境:

      • 通过实现ApplicationContextAware 注入ApplicationContext,然后在上下文中调用getEnvironment()
      • 实现EnvironmentAware

      它是通过实现一个类来获得的,因为属性在应用程序启动的早期阶段就被解析了,因为它们可能是构建 bean 所需要的。

      阅读更多文档:spring Environment documentation

      【讨论】:

      • 这正是我想要的,谢谢
      【解决方案8】:

      如您所知,较新版本的 Spring 不使用 PropertyPlaceholderConfigurer,而现在使用另一个噩梦般的构造,称为 PropertySourcesPlaceholderConfigurer。如果您正在尝试从代码中获取已解析的属性,并希望 Spring 团队很久以前就为我们提供了一种方法来做到这一点,那么请投票给这篇文章! ...因为这是您以新方式执行此操作的方式:

      子类 PropertySourcesPlaceholderConfigurer:

      public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {
      
          private ConfigurableListableBeanFactory factory;
      
          /**
           * Save off the bean factory so we can use it later to resolve properties
           */
          @Override
          protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
                  final ConfigurablePropertyResolver propertyResolver) throws BeansException {
              super.processProperties(beanFactoryToProcess, propertyResolver);
      
              if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
                  logger.debug("Value resolver exists.");
                  factory = beanFactoryToProcess;
              }
              else {
                  logger.error("No existing embedded value resolver.");
              }
          }
      
          public String getProperty(String name) {
              Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
              return propertyValue.toString();
          }
      }
      

      要使用它,请确保在 @Configuration 中使用您的子类并保存对它的引用以供以后使用。

      @Configuration
      @ComponentScan
      public class PropertiesConfig {
      
          public static SpringPropertyExposer commonEnvConfig;
      
          @Bean(name="commonConfig")
          public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
              commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
              PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
              commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
              try {
                  commonConfig.afterPropertiesSet();
              }
              catch (IOException e) {
                  e.printStackTrace();
                  throw e;
              }
              commonEnvConfig.setProperties(commonConfig.getObject());
              return commonEnvConfig;
          }
      }
      

      用法:

      Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
      

      【讨论】:

        【解决方案9】:

        这对我有帮助:

        ApplicationContextUtils.getApplicationContext().getEnvironment()
        

        【讨论】:

        • ApplicationContextUtils在什么包
        【解决方案10】:

        这是另一个示例。

        XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
        PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
        cfg.setLocation(new FileSystemResource("jdbc.properties"));
        cfg.postProcessBeanFactory(factory);
        

        【讨论】:

          【解决方案11】:

          这将解析所有嵌套属性。

          public class Environment extends PropertyPlaceholderConfigurer {
          
          /**
           * Map that hold all the properties.
           */
          private Map<String, String> propertiesMap; 
          
          /**
           * Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
           */
          @Override
          protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
              super.processProperties(beanFactory, props);
          
              propertiesMap = new HashMap<String, String>();
              for (Object key : props.keySet()) {
                  String keyStr = key.toString();
                  String valueStr = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
                  propertiesMap.put(keyStr, valueStr);
              }
          } 
          
          /**
           * This method gets the String value for a given String key for the property files.
           * 
           * @param name - Key for which the value needs to be retrieved.
           * @return Value
           */
          public String getProperty(String name) {
              return propertiesMap.get(name).toString();
          }
          

          【讨论】:

            【解决方案12】:

            这篇文章还解释了如何访问属性:http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html

            您可以通过此类 spring bean 访问由 spring property-placeholder 加载的属性:

            @Named
            public class PropertiesAccessor {
            
                private final AbstractBeanFactory beanFactory;
            
                private final Map<String,String> cache = new ConcurrentHashMap<>();
            
                @Inject
                protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
                    this.beanFactory = beanFactory;
                }
            
                public  String getProperty(String key) {
                    if(cache.containsKey(key)){
                        return cache.get(key);
                    }
            
                    String foundProp = null;
                    try {
                        foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
                        cache.put(key,foundProp);
                    } catch (IllegalArgumentException ex) {
                       // ok - property was not found
                    }
            
                    return foundProp;
                }
            }
            

            【讨论】:

              【解决方案13】:

              这是我让它工作的最佳方式:

              package your.package;
              
              import java.io.IOException;
              import java.util.Properties;
              import java.util.logging.Level;
              import java.util.logging.Logger;
              import org.springframework.core.io.ClassPathResource;
              import org.springframework.core.io.Resource;
              import org.springframework.core.io.support.PropertiesLoaderUtils;
              
              public class ApplicationProperties {
              
                  private Properties properties;
              
                  public ApplicationProperties() {
                      // application.properties located at src/main/resource
                      Resource resource = new ClassPathResource("/application.properties");
                      try {
                          this.properties = PropertiesLoaderUtils.loadProperties(resource);
                      } catch (IOException ex) {
                          Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
                      }
                  }
              
                  public String getProperty(String propertyName) {
                      return this.properties.getProperty(propertyName);
                  }
              }
              

              【讨论】:

              • 实例化类并调用方法obj.getProperty("my.property.name");
              【解决方案14】:
              create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`
              

              在 servlet-context.xml 之后你可以在任何地方直接使用你的文件

              【讨论】:

                【解决方案15】:

                请在您的 spring 配置文件中使用以下代码从应用程序的类路径加载文件

                 <context:property-placeholder
                    ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
                

                【讨论】:

                  【解决方案16】:

                  我知道这是一个旧线程,但是,我认为这个主题对于那些使用函数式方法的人来说非常重要,因为这些用例需要“即时”加载的微服务,因此避免使用注释。 仍未解决的问题是最终加载我在 application.yml 中的环境变量。

                  public class AppPropsLoader {
                  public static Properties load() {
                    var propPholderConfig = new PropertySourcesPlaceHolderConfigurer();
                    var yaml = new YamlPropertiesFactoryBean();
                    ClassPathResource resource = new ClassPathResource("application.yml");
                    Objects.requireNonNull(resource, "File application.yml does not exist");
                    yaml.setResources(resource);
                    Objects.requireNonNull(yaml.getObject(), "Configuration cannot be null");
                    propPholderConfig.postProcessBeanFactory(new DefaultListableBeanFactory());
                    propPholderConfig.setProperties(yaml.getObject());
                    PropertySources appliedPropertySources = 
                    propPholderConfig.getAppliedPropertySources();
                    var resolver = new PropertySourcesPlaceholderResolver(appliedPropertySources);
                    Properties resolvedProps = new Properties();
                    for (Map.Entry<Object, Object> prop: yaml.getObject().entrySet()) {
                      resolvedProps.setProperty((String)prop.getKey(), 
                        getPropertyValue(resolver.resolvePlaceHolders(prop.getValue()));
                    }
                    return resolvedProps;
                  }
                   static String getPropertyValue(Object prop) {
                     var val = String.valueOf(prop);
                     Pattern p = Pattern.compile("^(\\$\\{)([a-zA-Z0-9-._]+)(\\})$");
                     Matcher m = p.matcher(val);
                     if(m.matches()) {
                      return System.getEnv(m.group(2));
                    }
                     return val;
                   }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2011-12-18
                    • 1970-01-01
                    • 2011-02-07
                    • 2010-11-11
                    • 2023-01-10
                    • 2016-05-22
                    • 1970-01-01
                    • 2012-07-10
                    相关资源
                    最近更新 更多