【问题标题】:How can I inject a property value into a Spring Bean which was configured using annotations?如何将属性值注入使用注释配置的 Spring Bean?
【发布时间】: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 访问我感兴趣的属性?

【问题讨论】:

  • 我问过基本相同的问题,尽管情况略有不同:stackoverflow.com/questions/310271/…。到目前为止,还没有人能够回答。
  • 请注意,从 Spring 3.1 开始,PropertyPlaceholderConfigurer 不再是推荐的类。更喜欢PropertySourcesPlaceholderConfigurer。在任何情况下,您都可以使用较短的 XML 定义 &lt;context:property-placeholder /&gt;

标签: java spring dependency-injection


【解决方案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 中,我需要定义一个 &lt;bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"&gt;,然后使用 @Value("#{appProperties.databaseName}") 之类的东西将值从它读取到另一个 bean
      • 请务必从 max 的回答中注意到,您也可以在表达式 ${db.doStuff} 中使用占位符,那么您不需要 PropertiesFactoryBean,只需一个 placeholderConfigurer
      • 您可以使用 util:properties 添加自己的属性;例如,。有关如何获取值的信息,请参阅编辑后的答案。 (我意识到这对 Don 有所帮助可能为时已晚,但希望其他人会受益。)
      • 只有当我在 appname-servlet.xml 文件中使用 util:properties 时它才对我有用。使用我的 applicationContext.xml(不是 Spring MVC 的)中定义的 propertyConfigurer 不起作用。
      • 要进一步阅读,详细说明其中的一些内容,请查看这个 SOF 问题:stackoverflow.com/questions/6425795/…
      【解决方案5】:

      在我们获得 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);
                          }
                      }
                  }
              }
          }
      
      }
      

      随意修改以适应口味

      【讨论】:

      【解决方案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】:

        &lt;context:property-placeholder ... /&gt; 是等同于 PropertyPlaceholderConfigurer 的 XML。

        示例: applicationContext.xml

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

        组件类

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

        【讨论】:

        • 对我来说,这仅在通过 &lt;context:component-scan base-package="com.company.package" /&gt; 启用自动装配时才有效。作为参考,我通过 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/

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-08-06
                          • 1970-01-01
                          • 2015-07-03
                          • 1970-01-01
                          • 2011-09-09
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多