Як я можу ввести значення властивості у Spring Bean, який був налаштований за допомогою приміток?


294

У мене є купа весняної квасолі, яку збирають із класу за допомогою анотацій, наприклад

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

У весняному XML-файлі визначено PropertyPlaceholderConfigurer :

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

Я хочу ввести одне з властивостей програми app.properites у вказаний вище боб. Я просто не можу зробити щось подібне

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

Оскільки PersonDaoImpl не міститься у файлі Spring XML (він вибирається з classpath за допомогою анотацій). У мене є таке:

@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/… . Поки ніхто не зміг відповісти на це.
Спенсер Кормос

Зверніть увагу, що станом на весну 3.1 PropertyPlaceholderConfigurerце вже не рекомендований клас. Віддавайте перевагу PropertySourcesPlaceholderConfigurerзамість цього. У будь-якому випадку, ви можете використовувати коротше визначення XML <context:property-placeholder />.
Майкл Пієфель

Відповіді:


292

Це можна зробити навесні 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;

Ось повідомлення в блозі, про яке я писав трохи більше інформації.


8
Є чи systemPropertiesпросто System.getProperties()? Я думаю, якщо я хочу ввести свої властивості у весняний квасоля, мені потрібно визначити <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">потім прочитані значення з цього в інший квасоля, використовуючи щось на кшталт@Value("#{appProperties.databaseName}")
Dónal

11
Не забудьте відзначити з відповіді max, що ви також можете використовувати заповнювачі у виразах $ {db.doStuff}, тоді вам не потрібен PropertiesFactoryBean, просто заповнювачConfigurer
gtrak

9
Ви можете додати власні властивості за допомогою util: properties; наприклад, <util: properties id = "config" location = "classpath: /spring/environment.properties" />. Див. Відредаговану відповідь, як отримати значення. (Я розумію, що це, мабуть, занадто пізно, щоб допомогти Дону, але інші, сподіваємось, принесуть користь.)

2
Це працювало для мене лише тоді, коли я використовував util: властивості в моєму файлі appname-servlet.xml. Використання propertyConfigurer, визначеного в моєму застосуванніContext.xml (не Spring MVC), не працювало.
Асаф Месіка

Для невеликого додаткового читання, що конкретизує деякі з цього, перевірити цей СФБ питання теж: stackoverflow.com/questions/6425795 / ...
arcseldon

143

Особисто я люблю цей новий спосіб навесні 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();
    }

9
переконайтесь і додайте в область імен uri xmlns: p = " springframework.org/schema/p ", щоб використовувати атрибути p:.
Шейн Лі

3
Чому цей метод працює в тестовому контексті, але не в основному?
luksmir

9
зітхаючи, я витратив годину, намагаючись зробити підходи лише до анотацій, і виявив, чого не вистачає, лише прочитавши цю відповідь - декларацію магічного статичного боба PropertySauceYadaYada. Весняне кохання!
Кранач

@barrymac ей, Баррі, ти знаєш, у чому різниця між @Value (# {...}) та @Value ($ {...}). Дякую
Кім

1
Це працює для мене. Лише одна порада: анотація @Component потрібна.
yaki_nuka

121

Існує нові анотації @Valueв Spring 3.0.0M3 . @Valueпідтримка не тільки #{...}виразів, але і ${...}заповнювачів


20
+1 Якщо приклад допомагає, ось він - @Value (value = "# {'$ {server.env}'}") або просто @Value ("# {'$ {server.env}'}")
Сому

31

<context:property-placeholder ... /> є еквівалентом XML, що відповідає PropertyPlaceholderConfigurer.

Приклад: applicationContext.xml

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

Клас компонентів

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

1
Для мене це спрацьовувало лише в тому випадку, якщо автопроводку ввімкнено через <context:component-scan base-package="com.company.package" />Довідково, я використовую spring через ApplicationContext, а не у веб-контексті.
Мустафа

15

Ще одна альтернатива - додати базу програмиProperties, показану нижче:

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

Коли буде отримано, цей боб може бути переданий до java.util.Propertiesякого буде містити властивість, з ім'ям results.maxякого зчитується значення app.properties. Знову ж таки, цей квасоля може бути введений залежністю (як примірник java.util.Properties) у будь-який клас за допомогою анотації @Resource.

Особисто я віддаю перевагу цьому рішенню (іншому, що я запропонував), оскільки ви можете обмежити, які саме властивості піддаються застосуванню appProperties, і не потрібно читати app.properties двічі.


Працює і для мене. Але чи немає іншого способу отримати доступ до властивостей від PropertyPlaceholderConfigurer через анотацію @Value (коли використовується кілька PropertyPlaceholderConfigurer у кількох конфігураційних XML-файлах.)?
Цар

9

Мені потрібно мати два файли властивостей, один для виробництва та переопрацювання для розробки (які не будуть розгорнуті).

Щоб мати обидва, властивість 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>

і посилайтесь на Properties Bean у PropertyConfigurer

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

7

Перш ніж ми отримаємо весну 3 - яка дозволяє вводити константи властивості безпосередньо у ваші боби за допомогою анотацій - я написав підклас квасолі PropertyPlaceholderConfigurer, який робить те саме. Отже, ви можете позначати власників нерухомості, і Spring передасть ваші властивості у ваш квасоля так:

@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
Рікардо Гладвелл

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" Spring. Інший спосіб - дзвонити

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

Примітка. Для @Value ви не можете використовувати статичну властивістьField, вона повинна бути нестатичною, інакше вона повернеться в нулеву. Щоб виправити це, для статичного поля створюється нестатичний сеттер і над цим сеттером застосовується @Value.


7

Як вже було сказано @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;

5

Можливе рішення - оголосити другий боб, який читає з того самого файлу властивостей:

<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 attruibute, показаного вище.


4

Якщо ви застрягли, використовуючи Spring 2.5, ви можете визначити квасоля для кожної з ваших властивостей і ввести їх за допомогою кваліфікаторів. Подобається це:

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

і

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

Це не дуже читабельно, але це робить роботу.


2

Автоматичне підключення цінностей до весняних бобів:

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

нові речі навесні 3.0 || Значення квасолі для автоматичної проводки || значення електромонтажу властивостей навесні


2

Для мене це була відповідь @ Лакі, а конкретніше - рядок

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

зі сторінки Налагодження капітана

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


1

Я думаю, що найзручнішим способом введення властивостей у квасоля є метод сеттера.

Приклад:

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

Визначення квасолі xml:

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

Для кожного названого propertyметоду setProperty(value)буде викликано.

Цей спосіб особливо корисний, якщо вам потрібно більше одного квасолі на основі однієї реалізації.

Наприклад, якщо ми визначимо ще один боб у 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) {
            ...
        }
    }
}

0

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

У нашому додатку ми використовуємо:

  • Налаштування для налаштування системи PreProd і Prod
  • Налаштування та змінні середовища JNDI (JNDI замінює налаштування) для "mvn jetty: run"
  • Властивості системи для UnitTests (анотація @BeforeClass)

Порядок за замовчуванням, який ключове значення-Джерело перевіряється спочатку, описаний у:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Він може бути налаштований за допомогою параметрів4j.xml (точний log4j.xml) у вашому класний шлях.

Повідомте мені вашу думку: settings4j-user@lists.sourceforge.net

з привітними побажаннями,
Харальд


-1

Скористайтеся класом Spring "PropertyPlaceholderConfigurer" Spring

Простий приклад показує файл властивостей, який динамічно читається як властивість 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 / рекламне оголошення

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.