用 Spring 以编程方式访问属性文件?

我们使用下面的代码将 Springbean 与属性文件中的属性一起注入。

<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");
298192 次浏览

我做到了,而且成功了。

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

应该可以。

PropertiesLoaderUtils呢?

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

CREDIT : < a href = “ http://codingbone.wordpress.com/2010/02/28/how-to-load-properties-files-into-Spring-and-expo-to-the-java-classes/”rel = “ nofollow noReferrer”> 通过编程访问 Spring 中的属性,而不需要重新读取属性文件

我发现了一个很好的实现,可以在 Spring 中通过编程方式访问属性,而无需重新加载 Spring 已经加载的属性。[此外,不需要硬编码源代码中的属性文件位置]

通过这些更改,代码看起来更干净,更易于维护。

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

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>

希望这有助于解决你的问题

如果您只想从代码中访问占位符值,那么有一个 @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']}

此解决方案的优点是可以依赖标准占位符 由上下文注入的实现: 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(); }
}

这是另一个样本。

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

您还可以使用 spring utils,或者通过 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

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

您可以访问 Spring 属性占位符加载的属性,比如 Spring bean:

@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;
}
}

创建一个类,如下所示

    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;
}

这将解析任何嵌套属性。

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();
}

这对我有帮助:

ApplicationContextUtils.getApplicationContext().getEnvironment()

正如您所知道的,Spring 的新版本不使用 PropertyPlaceholderConfigrer,而是使用另一个名为 PropertySourcesPlaceholderConfigrer 的噩梦般的构造。如果您正试图从代码中获取解析属性,并希望 Spring 团队在很久以前就为我们提供了解析属性的方法,那么请对这篇文章投票!因为这就是你的新方法:

子类 PropertySourcesPlaceholderConfigrer:

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");
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`

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

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

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

您可以通过 Environment类获得属性:

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

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

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

你可以通过以下途径获得你的“原始”属性:

env.getProperty("your-property")

它将搜索 Spring 已注册的所有属性源。

你可透过以下途径获取环境资讯:

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

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

阅读更多文档: Spring 环境文件

这是我最好的办法:

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);
}
}

我知道这是一个老主题,但是,在我看来,这个主题对于那些使用函数式方法的用例来说变得非常重要,因为在这些用例中,你需要一个“即时”加载的微服务,因此你避免使用注释。 尚未解决的问题是最终加载我在 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;
}
}