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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/317687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:05:36  来源:igfitidea点击:

How can I inject a property value into a Spring Bean which was configured using annotations?

javaspringdependency-injection

提问by Dónal

I have a bunch of Spring beans which are picked up from the classpath via annotations, e.g.

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

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

In the Spring XML file, there's a PropertyPlaceholderConfigurerdefined:

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

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

I want to inject one of the properties from app.properites into the bean shown above. I can't simply do something like

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

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

Because PersonDaoImpl does not feature in the Spring XML file (it is picked up from the classpath via annotations). I've got as far as the following:

因为 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? 
    }
}

But it's not clear to me how I access the property I'm interested in from ppc?

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

采纳答案by Willi aus Rohr

You can do this in Spring 3 using EL support. Example:

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

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

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

systemPropertiesis an implicit object and strategyBeanis a bean name.

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

One more example, which works when you want to grab a property from a Propertiesobject. It also shows that you can apply @Valueto fields:

再举一个例子,当你想从一个Properties对象中获取一个属性时它会起作用。它还表明您可以申请@Value字段:

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

Here is a blog postI wrote about this for a little more info.

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

回答by Dónal

A possible solutions is to declare a second bean which reads from the same properties file:

一个可能的解决方案是声明从同一属性文件中读取的第二个 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"/>

The bean named 'appProperties' is of type java.util.Properties and can be dependency injected using the @Resource attruibute shown above.

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

回答by Dónal

Another alternative is to add the appProperties bean shown below:

另一种选择是添加如下所示的 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>

When retrieved, this bean can be cast to a java.util.Propertieswhich will contain a property named results.maxwhose value is read from app.properties. Again, this bean can be dependency injected (as an instance of java.util.Properties) into any class via the @Resource annotation.

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

Personally, I prefer this solution (to the other I proposed), as you can limit exactly which properties are exposed by appProperties, and don't need to read app.properties twice.

就我个人而言,我更喜欢这个解决方案(而不是我提出的另一个),因为您可以精确限制 appProperties 公开哪些属性,并且不需要读取 app.properties 两次。

回答by Willi aus Rohr

I need to have two properties files, one for production and an override for development (that will not be deployed).

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

To have both, a Properties Bean that can be autowired and a PropertyConfigurer, you can write:

要同时拥有可自动装配的 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>

and reference the Properties Bean in the PropertyConfigurer

并在 PropertyConfigurer 中引用 Properties Bean

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

回答by Ricardo Gladwell

Before we get Spring 3 - which allows you to inject property constants directly into your beans using annotations - I wrote a sub-class of the PropertyPlaceholderConfigurer bean that does the same thing. So, you can mark up your property setters and Spring will autowire your properties into your beans like so:

在我们获得 Spring 3 之前——它允许你使用注解将属性常量直接注入到你的 bean 中——我写了一个 PropertyPlaceholderConfigurer bean 的子类来做同样的事情。因此,您可以标记您的属性设置器,Spring 会将您的属性自动装配到您的 bean 中,如下所示:

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

The Annotation is as follows:

注解如下:

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

The PropertyAnnotationAndPlaceholderConfigurer is as follows:

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

}

Feel free to modify to taste

随意修改口味

回答by Ricardo Gladwell

There is a new annotation @Valuein Spring 3.0.0M3. @Valuesupport not only #{...}expressions but ${...}placeholders as well

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

回答by Nik

If you are stuck using Spring 2.5 you could define a bean for each of your properties and inject them using qualifiers. Like this:

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

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

and

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

Its not super readable but it gets the job done.

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

回答by barrymac

Personally I love this new way in Spring 3.0 from the docs:

我个人喜欢文档中 Spring 3.0 中的这种新方式:

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

No getters or setters!

没有 getter 或 setter!

With the properties being loaded via the config:

通过配置加载属性:

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

To further my glee I can even control click on the EL expression in IntelliJ and it brings me to the property definition!

为了让我更高兴,我什至可以控制单击 IntelliJ 中的 EL 表达式,它将我带到属性定义!

There's also the totally non xml version:

还有完全非 xml 版本

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

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

回答by shane lee

<context:property-placeholder ... />is the XML equivalent to the PropertyPlaceholderConfigurer.

<context:property-placeholder ... />是等同于 PropertyPlaceholderConfigurer 的 XML。

Example: applicationContext.xml

示例:applicationContext.xml

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

Component class

组件类

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

回答by Lucky

Autowiring Property Values into Spring Beans:

将属性值自动装配到 Spring Beans 中:

Most people know that you can use @Autowired to tell Spring to inject one object into another when it loads your application context. A lesser known nugget of information is that you can also use the @Value annotation to inject values from a property file into a bean's attributes. see this post for more info...

大多数人都知道您可以使用 @Autowired 告诉 Spring 在加载应用程序上下文时将一个对象注入另一个对象。一个鲜为人知的信息是,您还可以使用 @Value 注释将属性文件中的值注入到 bean 的属性中。请参阅此帖子以获取更多信息...

new stuff in Spring 3.0||autowiring bean values||autowiring property values in spring

Spring 3.0 中的新东西|| 自动装配 bean 值|| 在 spring 中自动装配属性值