Как я могу ввести значение свойства в Spring Bean, который был настроен с использованием аннотаций?

У меня есть куча бинов Spring, которые берутся из пути к классам через аннотации, например.

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

В XML-файле Spring есть 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 не присутствует в XML-файле Spring (он выбирается из пути к классам через аннотации). Я получил следующее:

@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?


person Dónal    schedule 25.11.2008    source источник
comment
Я задал, по сути, тот же вопрос, хотя и в несколько ином сценарии: stackoverflow.com/questions/310271/. Пока никто не может ответить на него.   -  person Spencer Kormos    schedule 25.11.2008
comment
Обратите внимание, что начиная с Spring 3.1 PropertyPlaceholderConfigurer больше не является рекомендуемым классом. Вместо этого предпочитайте PropertySourcesPlaceholderConfigurer. В любом случае вы можете использовать более короткое определение XML <context:property-placeholder />.   -  person Michael Piefel    schedule 30.10.2013
comment
stackoverflow .com / questions / 28756014 /   -  person kervin    schedule 30.07.2016


Ответы (18)


Вы можете сделать это в Spring 3, используя поддержку EL. Пример:

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

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

systemProperties - неявный объект, а strategyBean - имя компонента.

Еще один пример, который работает, когда вы хотите получить свойство из объекта Properties. Он также показывает, что вы можете применить @Value к полям:

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

Вот сообщение в блоге Я написал об этом для получения дополнительной информации.

person Community    schedule 04.12.2008
comment
systemProperties просто System.getProperties()? Я предполагаю, что если я хочу внедрить свои собственные свойства в компонент Spring, мне нужно определить <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">, а затем прочитать значения из него в другой компонент, используя что-то вроде @Value("#{appProperties.databaseName}") - person Dónal; 07.04.2011
comment
Обязательно обратите внимание на ответ max, что вы также можете использовать заполнители в выражениях $ {db.doStuff}, тогда вам не нужен PropertiesFactoryBean, только placeholderConfigurer - person gtrak; 21.07.2011
comment
Вы можете добавить свои собственные свойства, используя util: properties; например, ‹util: properties id = config location = classpath: /spring/environment.properties /›. См. Отредактированный ответ, чтобы узнать, как получить значение. (Я понимаю, что это, вероятно, слишком поздно, чтобы быть полезным для Дона, но, надеюсь, это принесет пользу другим.) - person ; 28.01.2012
comment
У меня это сработало только тогда, когда я использовал util: properties в моем файле appname-servlet.xml. Использование propertyConfigurer, определенного в моем applicationContext.xml (не Spring MVC), не сработало. - person Asaf Mesika; 07.10.2012
comment
Для дальнейшего чтения, в котором подробно рассматриваются некоторые из этих вопросов, ознакомьтесь также с этим вопросом SOF: stackoverflow.com/questions/6425795/ - person arcseldon; 21.08.2013
comment
В любом случае добавить операторы импорта на примере java? #повторяющаяся тема - person granadaCoder; 18.02.2020

Лично мне нравится этот новый способ в Spring 3.0 из документации:

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

Никаких геттеров и сеттеров!

Когда свойства загружаются через конфигурацию:

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

Чтобы еще больше развлечься, я могу даже управлять щелчком по выражению EL в IntelliJ, и это приводит меня к определению свойства!

Также есть полностью версия без XML:

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

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
person barrymac    schedule 06.10.2011
comment
убедитесь и добавьте в пространство имен uri xmlns: p = springframework.org/schema/p в используйте атрибуты с префиксом p:. - person shane lee; 31.10.2012
comment
Почему эти методы работают в тестовом контексте, а не в основном контексте? - person luksmir; 05.09.2013
comment
вздох, я потратил час, пытаясь заставить работать подход, основанный только на аннотациях, и обнаружил, чего не хватает, только после прочтения этого ответа - объявления волшебного статического bean-компонента PropertySauceYadaYada. Весенняя любовь! - person Kranach; 04.03.2015
comment
@barrymac Привет, Барри, ты знаешь, в чем разница между @Value (# {...}) и @Value ($ {...}). Спасибо - person Kim; 23.03.2017
comment
У меня это работает. Только один совет: аннотация @Component обязательна. - person yaki_nuka; 26.04.2017
comment
Как Spring создает экземпляр этого частного поля под капотом, если у него нет установщика? - person Elrond_EGLDer; 09.07.2018
comment
Он работает с application.properties, но я не мог заставить его работать с application.yaml - person Enrico Giurin; 08.03.2019

В Spring 3.0.0M3 появилась новая аннотация @Value. @Value поддерживает не только #{...} выражения, но также ${...} заполнители

person Community    schedule 28.07.2009
comment
+1 Если пример помогает, вот он - @Value (value = # {'$ {server.env}'}) или просто @Value (# {'$ {server.env}'}) - person Somu; 28.10.2011

<context:property-placeholder ... /> - это XML-эквивалент PropertyPlaceholderConfigurer.

Пример: applicationContext.xml

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

Компонентный класс

 private @Value("${propertyName}") String propertyField;
person shane lee    schedule 31.10.2012
comment
Для меня это сработало только в том случае, если автоматическое подключение включено через <context:component-scan base-package="com.company.package" /> Для справки, я использую spring через ApplicationContext, а не в веб-контексте. - person Mustafa; 06.08.2015

Другой альтернативой является добавление bean-компонента appProperties, показанного ниже:

<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-компонент может быть введен зависимостью (как экземпляр java.util.Properties) в любой класс через аннотацию @Resource.

Лично я предпочитаю это решение (другому, которое я предложил), поскольку вы можете точно ограничить, какие свойства отображаются в appProperties, и вам не нужно дважды читать app.properties.

person Dónal    schedule 25.11.2008
comment
У меня тоже работает. Но нет ли другого способа получить доступ к свойствам из PropertyPlaceholderConfigurer через аннотацию @Value (при использовании нескольких PropertyPlaceholderConfigurer в нескольких файлах congif XML)? - person Czar; 27.01.2011

Мне нужно иметь два файла свойств: один для производства и переопределения для разработки (который не будет развернут).

Чтобы иметь и компонент свойств, который может быть автоматически подключен, и 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>

и укажите ссылку на Bean-компонент Properties в PropertyConfigurer.

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties" ref="appProperties" />
</bean>
person Willi aus Rohr    schedule 26.11.2008

Вы также можете аннотировать свой класс:

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

И есть такая переменная:

@Autowired
private Environment env;

Теперь вы можете получить доступ ко всем своим свойствам таким образом:

env.getProperty("database.connection.driver")
person Alexis Gamarra    schedule 09.01.2015

Прежде чем мы получим Spring 3, который позволяет вам вводить константы свойств непосредственно в ваши bean-компоненты с помощью аннотаций, я написал подкласс bean-компонента PropertyPlaceholderConfigurer, который делает то же самое. Итак, вы можете пометить свои установщики свойств, и 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);
                    }
                }
            }
        }
    }

}

Не стесняйтесь изменять по вкусу

person Ricardo Gladwell    schedule 23.02.2009
comment
Обратите внимание, что я создал новый проект для вышеуказанного: code.google.com / p / spring-property-annotations - person Ricardo Gladwell; 25.04.2010

Весенний путь:
private @Value("${propertyName}") String propertyField;

- это новый способ ввода значения с помощью класса Spring PropertyPlaceholderConfigurer. Другой способ - позвонить

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

Примечание. Для @Value нельзя использовать static propertyField, он должен быть только нестатическим, иначе возвращается null. Чтобы исправить это, для статического поля создается нестатический сеттер, а над этим сеттером применяется @Value.

person hi.nitish    schedule 25.10.2016

Как уже упоминалось, @Value выполняет свою работу, и он довольно гибкий, так как в нем может быть пружина 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;

Другой, чтобы получить set от list

//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;
person Alireza Fattahi    schedule 21.05.2016

Возможное решение - объявить второй 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 имеет тип java.util.Properties и может быть введен зависимостью с использованием атрибута @Resource, показанного выше.

person Dónal    schedule 25.11.2008

Если вы застряли в 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) {
...

Его не очень читабельно, но он выполняет свою работу.

person Nik    schedule 17.12.2009

Автоматическое связывание значений свойств в Spring Beans:

Большинство людей знают, что вы можете использовать @Autowired, чтобы сообщить Spring, чтобы он вставлял один объект в другой, когда он загружает контекст вашего приложения. Менее известный самородок информации заключается в том, что вы также можете использовать аннотацию @Value для вставки значений из файла свойств в атрибуты bean-компонента. см. этот пост для получения дополнительной информации ...

новые возможности Spring 3.0 || автоматическое подключение значений bean-компонентов || автоматическое подключение значений свойств весной

person Lucky    schedule 02.01.2013

Для меня это был ответ @ Lucky, в частности, строчка

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

со страницы Captain Debug

это устранило мою проблему. У меня есть приложение на основе ApplicationContext, работающее из командной строки, и, судя по ряду комментариев к SO, Spring подключает их по-другому к приложениям на основе MVC.

person ben3000    schedule 18.06.2013

Я думаю, что самый удобный способ добавить свойства в bean-компонент - это метод установки.

Пример:

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-компонента на основе одной реализации.

Например, если мы определим еще один bean-компонент в xml:

<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) {
            ...
        }
    }
}
person Sergei Pikalev    schedule 20.10.2016

Если вам нужна дополнительная гибкость для конфигураций, попробуйте Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

В нашем приложении мы используем:

  • Предпочтения для настройки PreProd- и Prod-System
  • Предпочтения и переменные среды JNDI (JNDI перезаписывает предпочтения) для "mvn jetty: run"
  • Системные свойства для модульных тестов (аннотация @BeforeClass)

Порядок по умолчанию, при котором в первую очередь проверяется источник значения ключа, описан в:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Его можно настроить с помощью settings4j.xml (с точностью до log4j.xml) в пути к классам.

Дайте мне знать свое мнение: [email protected]

С уважением,
Харальд

person Harald Brabenetz    schedule 13.03.2013

Используйте класс 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: // локальный: 3306 / addvertising

dev.app.jdbc.username = корень

dev.app.jdbc.password = корень

person ravi ranjan    schedule 19.04.2014

Самый простой способ в Spring 5 - использовать @ConfigurationProperties, вот пример https://mkyong.com/spring-boot/spring-boot-configurationproperties-example/.

person Vikrant Chaudhary    schedule 29.06.2020