如何将属性值注入使用注释配置的 Spring Bean?
Posted
技术标签:
【中文标题】如何将属性值注入使用注释配置的 Spring Bean?【英文标题】:How can I inject a property value into a Spring Bean which was configured using annotations? 【发布时间】:2010-09-23 23:30:12 【问题描述】:我有一堆 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
访问我感兴趣的属性?
【问题讨论】:
我问过基本相同的问题,尽管情况略有不同:***.com/questions/310271/…。到目前为止,还没有人能够回答。 请注意,从 Spring 3.1 开始,PropertyPlaceholderConfigurer
不再是推荐的类。更喜欢PropertySourcesPlaceholderConfigurer
。在任何情况下,您都可以使用较短的 XML 定义 <context:property-placeholder />
。
***.com/questions/28756014/…
【参考方案1】:
一种可能的解决方案是声明第二个 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 属性进行依赖注入。
【讨论】:
【参考方案2】:另一种选择是添加如下所示的 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
中读取。同样,这个 bean 可以通过 @Resource 注解依赖注入(作为 java.util.Properties 的一个实例)到任何类中。
就我个人而言,我更喜欢这个解决方案(相对于我提出的其他解决方案),因为您可以精确限制 appProperties 公开哪些属性,并且不需要读取 app.properties 两次。
【讨论】:
也适合我。但是没有其他方法可以通过 @Value 注释从 PropertyPlaceholderConfigurer 访问属性(在多个 congif XML 文件中使用多个 PropertyPlaceholderConfigurer 时。)?【参考方案3】:我需要两个属性文件,一个用于生产,一个用于开发(不会部署)。
要同时拥有一个可以自动装配的 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 中引用 Properties Bean
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="appProperties" />
</bean>
【讨论】:
【参考方案4】:您可以在 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;
这是一个blog post,我写了关于这个的更多信息。
【讨论】:
systemProperties
只是 System.getProperties()
吗?我想如果我想将自己的属性注入到 Spring bean 中,我需要定义一个 <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
,然后使用 @Value("#appProperties.databaseName")
之类的东西将值从它读取到另一个 bean
请务必从 max 的回答中注意到,您也可以在表达式 $db.doStuff 中使用占位符,那么您不需要 PropertiesFactoryBean,只需一个 placeholderConfigurer
您可以使用 util:properties 添加自己的属性;例如,在我们获得 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);
随意修改以适应口味
【讨论】:
请注意,我已经为上面创建了一个新项目:code.google.com/p/spring-property-annotations【参考方案6】:Spring 3.0.0M3 中有一个新的注解 @Value
。 @Value
不仅支持#...
表达式,还支持$...
占位符
【讨论】:
+1 如果一个例子有帮助,这里是 - @Value(value="#'$server.env'") 或简单的@Value("#'$server .env'")【参考方案7】:如果您无法使用 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)
...
它不是超级可读,但它可以完成工作。
【讨论】:
【参考方案8】:我个人喜欢 Spring 3.0 中的这种新方式from the docs:
private @Value("$propertyName") String propertyField;
没有 getter 或 setter!
通过配置加载属性:
<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();
【讨论】:
确保并在命名空间 uri 中添加 xmlns:p="springframework.org/schema/p" 以使用 p: 前缀属性。 为什么这个方法在测试环境中有效,但在主环境中无效? 叹息,我花了一个小时试图使仅注释的方法起作用,并且只有在阅读了这个答案后才发现缺少的东西 - 一个神奇的静态 bean PropertySauceYadaYada 的声明。春天的爱! @barrymac 嘿,巴里,你知道@Value(#...) 和@Value($...) 有什么区别吗?谢谢 这对我有用。只有一个提示:注解@Component 是必需的。【参考方案9】:<context:property-placeholder ... />
是等同于 PropertyPlaceholderConfigurer 的 XML。
示例: applicationContext.xml
<context:property-placeholder location="classpath:test.properties"/>
组件类
private @Value("$propertyName") String propertyField;
【讨论】:
对我来说,这仅在通过<context:component-scan base-package="com.company.package" />
启用自动装配时才有效。作为参考,我通过 ApplicationContext
使用 spring,而不是在 web 上下文中。【参考方案10】:
将属性值自动装配到 Spring Beans 中:
大多数人都知道,您可以使用@Autowired 告诉 Spring 在加载应用程序上下文时将一个对象注入另一个对象。一个鲜为人知的信息块是您还可以使用 @Value 注释将属性文件中的值注入到 bean 的属性中。 有关更多信息,请参阅此帖子...
new stuff in Spring 3.0 || autowiring bean values ||autowiring property values in spring
【讨论】:
【参考方案11】:如果您需要更多的配置灵活性,请尝试 Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html
在我们的应用程序中我们使用:
配置 PreProd 和 Prod 系统的首选项 “mvn jetty:run”的首选项和 JNDI 环境变量(JNDI 覆盖首选项) UnitTest 的系统属性(@BeforeClass 注释)先检查key-value-Source的默认顺序,描述在:http://settings4j.sourceforge.net/currentrelease/configDefault.html 它可以在您的类路径中使用 settings4j.xml(精确到 log4j.xml)进行自定义。
让我知道您的意见:settings4j-user@lists.sourceforge.net
致以友好的问候, 哈拉尔
【讨论】:
【参考方案12】:对我来说,这是@Lucky 的回答,特别是那一行
AutowiredFakaSource fakeDataSource = ctx.getBean(AutowiredFakaSource.class);
来自the Captain Debug page
这解决了我的问题。我有一个从命令行运行的基于 ApplicationContext 的应用程序,根据 SO 上的许多 cmets 判断,Spring 将这些与基于 MVC 的应用程序不同地连接起来。
【讨论】:
【参考方案13】:使用 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=root
【讨论】:
【参考方案14】:你也可以注释你的类:
@PropertySource("classpath:/com/myProject/config/properties/database.properties")
并且有一个像这样的变量:
@Autowired
private Environment env;
现在您可以通过这种方式访问您的所有属性:
env.getProperty("database.connection.driver")
【讨论】:
【参考方案15】:如前所述,@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;
另一个从list
获得set
//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;
【讨论】:
【参考方案16】:我认为将属性注入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)
...
【讨论】:
【参考方案17】:弹簧方式:private @Value("$propertyName")
String propertyField;
是一种使用 Spring 的“PropertyPlaceholderConfigurer”类注入值的新方法。 另一种方法是调用
java.util.Properties props = System.getProperties().getProperty("propertyName");
注意:@Value 不能使用static propertyField,只能是非静态的,否则返回null。为了修复它,为静态字段创建了一个非静态 setter,并在该 setter 上方应用了 @Value。
【讨论】:
【参考方案18】:Spring 5 中最简单的方法是使用 @ConfigurationProperties
这是示例
https://mkyong.com/spring-boot/spring-boot-configurationproperties-example/
【讨论】:
以上是关于如何将属性值注入使用注释配置的 Spring Bean?的主要内容,如果未能解决你的问题,请参考以下文章
如何根据 Spring Boot 中的活动配置文件设置注释属性值?