如何将属性值注入使用注解配置的Spring Bean中?


294

我有一堆Spring bean,它们是通过注释从类路径中拾取的,例如

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {
    // Implementation omitted
}

在Spring XML文件中,定义了一个PropertyPlaceholderConfigurer

<bean id="propertyConfigurer" 
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/WEB-INF/app.properties" />
</bean> 

我想将app.properites的属性之一注入到上面显示的bean中。我不能简单地做这样的事情

<bean class="com.example.PersonDaoImpl">
    <property name="maxResults" value="${results.max}"/>
</bean>

因为PersonDaoImpl在Spring XML文件中没有功能(它是通过注释从类路径中拾取的)。我有以下内容:

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {

    @Resource(name = "propertyConfigurer")
    protected void setProperties(PropertyPlaceholderConfigurer ppc) {
    // Now how do I access results.max? 
    }
}

但是我不清楚我如何从中访问我感兴趣的财产ppc


1
我问的是基本上相同的问题,尽管情况略有不同:stackoverflow.com/questions/310271/…。到目前为止,还没有人能够回答。
Spencer Kormos

请注意,从Spring 3.1开始,PropertyPlaceholderConfigurer不再推荐使用该类。更喜欢PropertySourcesPlaceholderConfigurer。无论如何,您都可以使用较短的XML定义<context:property-placeholder />
Michael Piefel 2013年

Answers:


292

您可以在Spring 3中使用EL支持进行此操作。例:

@Value("#{systemProperties.databaseName}")
public void setDatabaseName(String dbName) { ... }

@Value("#{strategyBean.databaseKeyGenerator}")
public void setKeyGenerator(KeyGenerator kg) { ... }

systemProperties是一个隐式对象,并且strategyBean是bean名称。

还有一个示例,当您想从Properties对象中获取属性时,该示例将起作用。它还显示您可以应用于@Value字段:

@Value("#{myProperties['github.oauth.clientId']}")
private String githubOauthClientId;

这是我写的一篇博客文章,以获取更多信息。


8
systemProperties简单System.getProperties()?我想如果我想将自己的属性注入到Spring bean中,我需要定义一个a <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">然后使用类似的方法从其中读取值到另一个bean @Value("#{appProperties.databaseName}")
–Dónal

11
确保从max的答案中注意到,您也可以在表达式$ {db.doStuff}中使用占位符,然后就不需要PropertiesFactoryBean,而只需要一个placeholderConfigurer
gtrak 2011年

9
您可以使用util:properties添加自己的属性;例如,<util:properties id =“ config” location =“ classpath:/spring/environment.properties” />。有关如何获取值,请参见编辑后的答案。(我意识到这可能为时已晚,无法对Don有所帮助,但希望其他人会受益。)

2
仅当我在appname-servlet.xml文件中使用util:properties时,它才对我有用。使用我的applicationContext.xml(不是Spring MVC之一)中定义的propertyConfigurer无效。
阿萨夫·梅西卡

:对于一个小进一步阅读,对一些本该详解,看看这个SOF问题太stackoverflow.com/questions/6425795/...
arcseldon

143

我个人很喜欢docs中Spring 3.0中的这种新方法:

private @Value("${propertyName}") String propertyField;

没有吸气剂或二传手!

通过配置加载属性:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
      p:location="classpath:propertyFile.properties" name="propertiesBean"/>

令我高兴的是,我什至可以控制单击IntelliJ中的EL表达式,这使我进入了属性定义!

还有完全非xml版本

@PropertySource("classpath:propertyFile.properties")
public class AppConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

9
确保并添加名称空间uri xmlns:p =“ springframework.org/schema/p ”以使用p:前缀属性。
李恩·李

3
为什么此方法在测试上下文中有效,但在主要上下文中无效?
luksmir

9
叹了口气,我花了一个小时试图使仅注释方法起作用,并且在阅读了神奇的静态bean PropertySauceYadaYada的这个答案声明之后,才发现缺少什么。春天的爱!
Kranach'3

@barrymac嘿,巴里,你知道@Value(#{...})和@Value($ {...})有什么区别。谢谢
金(Kim)

1
这对我有用。只有一个提示:注释@Component是必需的。
yaki_nuka

121

有一个新的注释@Value春季3.0.0M3@Value不仅支持#{...}表达式,还支持${...}占位符


20
+1如果有帮助的示例,则为-@Value(value =“#{'$ {server.env}'}”)或只是@Value(“#{'$ {server.env}'}”)
Somu

31

<context:property-placeholder ... /> 是与PropertyPlaceholderConfigurer等效的XML。

示例:applicationContext.xml

<context:property-placeholder location="classpath:test.properties"/>  

组件类

 private @Value("${propertyName}") String propertyField;

1
对我来说,这仅在通过启用自动装配功能时才有效。<context:component-scan base-package="com.company.package" />作为参考,我通过而ApplicationContext不是在Web上下文中使用spring 。
Mustafa

15

另一种选择是添加如下所示的appProperties bean:

<bean id="propertyConfigurer"   
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="/WEB-INF/app.properties" />
</bean> 


<bean id="appProperties" 
          class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="singleton" value="true"/>

        <property name="properties">
                <props>
                        <prop key="results.max">${results.max}</prop>
                </props>
        </property>
</bean>

检索到该bean后,可以将其转换为java.util.Properties,其中将包含一个名为results.max的属性,其值是从读取的app.properties。同样,可以通过@Resource批注将此bean依赖项注入(作为java.util.Properties的实例)。

就我个人而言,我更喜欢这种解决方案(相对于我提出的其他解决方案),因为您可以精确地限制appProperty公开哪些属性,而无需两次读取app.properties。


也为我工作。但是,还有没有其他方法可以通过@Value注释从PropertyPlaceholderConfigurer访问属性(在多个congif XML文件中使用多个PropertyPlaceholderConfigurer时)?
沙皇

9

我需要两个属性文件,一个用于生产,另一个用于开发(将不会部署)。

要同时拥有可以自动装配的Properties Bean和PropertyConfigurer,您可以编写:

<bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="singleton" value="true" />

    <property name="ignoreResourceNotFound" value="true" />
    <property name="locations">
        <list>
            <value>classpath:live.properties</value>
            <value>classpath:development.properties</value>
        </list>
    </property>
</bean>

并在PropertyConfigurer中引用属性Bean

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties" ref="appProperties" />
</bean>

7

在获得Spring 3之前(允许您使用批注将属性常量直接注入到您的bean中),我编写了PropertyPlaceholderConfigurer bean的子类来执行相同的操作。因此,您可以标记属性设置器,Spring会将属性自动连接到bean中,如下所示:

@Property(key="property.key", defaultValue="default")
public void setProperty(String property) {
    this.property = property;
}

注释如下:

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Property {
    String key();
    String defaultValue() default "";
}

PropertyAnnotationAndPlaceholderConfigurer如下:

public class PropertyAnnotationAndPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private static Logger log = Logger.getLogger(PropertyAnnotationAndPlaceholderConfigurer.class);

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

        for (String name : beanFactory.getBeanDefinitionNames()) {
            MutablePropertyValues mpv = beanFactory.getBeanDefinition(name).getPropertyValues();
            Class clazz = beanFactory.getType(name);

            if(log.isDebugEnabled()) log.debug("Configuring properties for bean="+name+"["+clazz+"]");

            if(clazz != null) {
                for (PropertyDescriptor property : BeanUtils.getPropertyDescriptors(clazz)) {
                    Method setter = property.getWriteMethod();
                    Method getter = property.getReadMethod();
                    Property annotation = null;
                    if(setter != null && setter.isAnnotationPresent(Property.class)) {
                        annotation = setter.getAnnotation(Property.class);
                    } else if(setter != null && getter != null && getter.isAnnotationPresent(Property.class)) {
                        annotation = getter.getAnnotation(Property.class);
                    }
                    if(annotation != null) {
                        String value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
                        if(StringUtils.isEmpty(value)) {
                            value = annotation.defaultValue();
                        }
                        if(StringUtils.isEmpty(value)) {
                            throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
                        }
                        if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+property.getName()+"] value=["+annotation.key()+"="+value+"]");
                        mpv.addPropertyValue(property.getName(), value);
                    }
                }

                for(Field field : clazz.getDeclaredFields()) {
                    if(log.isDebugEnabled()) log.debug("examining field=["+clazz.getName()+"."+field.getName()+"]");
                    if(field.isAnnotationPresent(Property.class)) {
                        Property annotation = field.getAnnotation(Property.class);
                        PropertyDescriptor property = BeanUtils.getPropertyDescriptor(clazz, field.getName());

                        if(property.getWriteMethod() == null) {
                            throw new BeanConfigurationException("setter for property=["+clazz.getName()+"."+field.getName()+"] not available.");
                        }

                        Object value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
                        if(value == null) {
                            value = annotation.defaultValue();
                        }
                        if(value == null) {
                            throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
                        }
                        if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+field.getName()+"] value=["+annotation.key()+"="+value+"]");
                        mpv.addPropertyValue(property.getName(), value);
                    }
                }
            }
        }
    }

}

随意修改以品尝


3
请注意,我已经为上述项目创建了一个新项目: code.google.com/p/spring-property-annotations
Ricardo Gladwell 2010年

7

您还可以注释您的班级:

@PropertySource("classpath:/com/myProject/config/properties/database.properties")

并有一个像这样的变量:

@Autowired
private Environment env;

现在,您可以通过以下方式访问所有属性:

env.getProperty("database.connection.driver")

7

春季方式:
private @Value("${propertyName}") String propertyField;

是使用Spring的“ PropertyPlaceholderConfigurer”类注入值的新方法。另一种方法是打电话

java.util.Properties props = System.getProperties().getProperty("propertyName");

注意:对于@Value,不能使用静态 propertyField,它只能是非静态的,否则返回null。要解决此问题,将为静态字段创建一个非静态设置器,并在该设置器上方应用@Value。


7

如前所述@Value,这项工作非常灵活,因为您可以在其中安装spring EL。

以下是一些示例,可能会有所帮助:

//Build and array from comma separated parameters 
//Like currency.codes.list=10,11,12,13
@Value("#{'${currency.codes.list}'.split(',')}") 
private List<String> currencyTypes;

另一个setlist

//If you have a list of some objects like (List<BranchVO>) 
//and the BranchVO has areaCode,cityCode,...
//You can easily make a set or areaCodes as below
@Value("#{BranchList.![areaCode]}") 
private Set<String> areas;

您还可以设置基本类型的值。

@Value("${amount.limit}")
private int amountLimit;

您可以调用静态方法:

@Value("#{T(foo.bar).isSecurityEnabled()}")
private boolean securityEnabled;

你可以有逻辑

@Value("#{T(foo.bar).isSecurityEnabled() ? '${security.logo.path}' : '${default.logo.path}'}")
private String logoPath;

5

可能的解决方案是声明另一个从相同属性文件读取的bean:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/WEB-INF/app.properties" />
</bean> 

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

名为“ appProperties”的bean的类型为java.util.Properties,可以使用上面显示的@Resource attruibute进行依赖项注入。


4

如果您坚持使用Spring 2.5,则可以为每个属性定义一个bean,然后使用限定符注入它们。像这样:

  <bean id="someFile" class="java.io.File">
    <constructor-arg value="${someFile}"/>
  </bean>

@Service
public class Thing
      public Thing(@Qualifier("someFile") File someFile) {
...

它不是超级可读,但可以完成工作。



2

对我而言,这是@Lucky的答案,特别是该行

AutowiredFakaSource fakeDataSource = ctx.getBean(AutowiredFakaSource.class);

“机长调试”页面

解决了我的问题。我有一个基于ApplicationContext的应用程序,该应用程序从命令行运行,并根据SO上的许多注释进行判断,Spring将这些内容以不同的方式连接到基于MVC的应用程序。


1

我认为将属性注入bean的最方便的方法是setter方法。

例:

package org.some.beans;

public class MyBean {
    Long id;
    String name;

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Bean xml定义:

<bean id="Bean1" class="org.some.beans.MyBean">
    <property name="id" value="1"/>
    <property name="name" value="MyBean"/>
</bean>

对于每个命名property方法setProperty(value)都将被调用。

如果您需要基于一个实现的多个bean,则这种方法特别有用。

例如,如果我们在xml中再定义一个bean:

<bean id="Bean2" class="org.some.beans.MyBean">
    <property name="id" value="2"/>
    <property name="name" value="EnotherBean"/>
</bean>

然后编写如下代码:

MyBean b1 = appContext.getBean("Bean1");
System.out.println("Bean id = " + b1.getId() + " name = " + b1.getName());
MyBean b2 = appContext.getBean("Bean2");
System.out.println("Bean id = " + b2.getId() + " name = " + b2.getName());

将打印

Bean id = 1 name = MyBean
Bean id = 2 name = AnotherBean

因此,在您的情况下,它应如下所示:

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {

    Long maxResults;

    public void setMaxResults(Long maxResults) {
        this.maxResults = maxResults;
    }

    // Now use maxResults value in your code, it will be injected on Bean creation
    public void someMethod(Long results) {
        if (results < maxResults) {
            ...
        }
    }
}

0

如果您需要更高的配置灵活性,请尝试Settings4jPlaceholderConfigurer:http : //settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

在我们的应用程序中,我们使用:

  • 配置PreProd和Prod系统的首选项
  • “ mvn jetty:run”的首选项和JNDI环境变量(JNDI覆盖首选项)
  • UnitTests的系统属性(@BeforeClass批注)

首先检查键-值-源的默认顺序,如下所述:
http :
//settings4j.sourceforge.net/currentrelease/configDefault.html可以在您的设置中使用settings4j.xml(准确到log4j.xml)进行自定义。类路径。

让我知道您的意见:settings4j-user@lists.sourceforge.net

以友好的问候,
哈拉尔德


-1

使用Spring的“ PropertyPlaceholderConfigurer”类

一个简单的示例,显示了作为Bean的属性动态读取的属性文件

<bean id="placeholderConfig"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>/WEB-INF/classes/config_properties/dev/database.properties</value>
        </list>
    </property> 
</bean>

<bean id="devDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="${dev.app.jdbc.driver}"/>
    <property name="jdbcUrl" value="${dev.app.jdbc.url}"/>
    <property name="user" value="${dev.app.jdbc.username}"/>
    <property name="password" value="${dev.app.jdbc.password}"/>
    <property name="acquireIncrement" value="3"/>
    <property name="minPoolSize" value="5"/>
    <property name="maxPoolSize" value="10"/>
    <property name="maxStatementsPerConnection" value="11000"/>
    <property name="numHelperThreads" value="8"/>
    <property name="idleConnectionTestPeriod" value="300"/>
    <property name="preferredTestQuery" value="SELECT 0"/>
</bean> 

属性文件

dev.app.jdbc.driver = com.mysql.jdbc.Driver

dev.app.jdbc.url = jdbc:mysql:// localhost:3306 / addvertisement

dev.app.jdbc.username = root

dev.app.jdbc.password =根

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.