Làm cách nào tôi có thể đưa giá trị thuộc tính vào Spring Bean được cấu hình bằng chú thích?


294

Tôi có một bó đậu mùa xuân được nhặt từ đường dẫn thông qua các chú thích, vd

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

Trong tệp Spring XML, có một PropertyPlaceholderConfigker được xác định:

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

Tôi muốn tiêm một trong các thuộc tính từ app.properites vào bean hiển thị ở trên. Tôi không thể đơn giản làm một cái gì đó như

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

Bởi vì PersonDaoImpl không có trong tệp Spring XML (nó được chọn từ đường dẫn lớp thông qua các chú thích). Tôi đã có được như sau:

@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {

    @Resource(name = "propertyConfigurer")
    protected void setProperties(PropertyPlaceholderConfigurer ppc) {
    // Now how do I access results.max? 
    }
}

Nhưng tôi không rõ làm thế nào để tôi truy cập vào tài sản mà tôi quan tâm ppc?


1
Về cơ bản, tôi đã hỏi cùng một câu hỏi, mặc dù trong một kịch bản hơi khác: stackoverflow.com/questions/310271/iêu . Cho đến nay, không ai có thể trả lời nó.
Spencer Kormos

Xin lưu ý rằng kể từ Mùa xuân 3.1, PropertyPlaceholderConfigurerkhông còn là lớp được đề xuất. Thích PropertySourcesPlaceholderConfigurerthay thế. Trong mọi trường hợp, bạn có thể sử dụng định nghĩa XML ngắn hơn <context:property-placeholder />.
Michael Piefel

Câu trả lời:


292

Bạn có thể làm điều này trong Spring 3 bằng cách sử dụng hỗ trợ EL. Thí dụ:

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

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

systemPropertieslà một đối tượng ngầm và strategyBeanlà một tên đậu.

Một ví dụ nữa, hoạt động khi bạn muốn lấy một thuộc tính từ một Propertiesđối tượng. Nó cũng cho thấy rằng bạn có thể áp dụng @Valuecho các trường:

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

Đây là một bài viết trên blog tôi đã viết về điều này để biết thêm một chút.


8
systemPropertiesđơn giản System.getProperties()? Tôi đoán nếu tôi muốn đưa các thuộc tính của riêng mình vào một hạt đậu mùa xuân, tôi cần xác định một <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">giá trị sau đó đọc các giá trị đó từ một hạt khác bằng cách sử dụng một cái gì đó như@Value("#{appProperties.databaseName}")
Dónal

11
Hãy chắc chắn lưu ý từ câu trả lời của max rằng bạn cũng có thể sử dụng trình giữ chỗ trong các biểu thức $ {db.doStuff}, sau đó bạn không cần PropertiesFactoryBean, chỉ cần một trình giữ
chỗConfigker

9
Bạn có thể thêm các thuộc tính của riêng bạn bằng cách sử dụng produc: property; ví dụ: <produc: property id = "config" location = "classpath: / spring / en môi.properies" />. Xem câu trả lời được chỉnh sửa để biết cách lấy giá trị. (Tôi nhận ra rằng điều này có lẽ đã quá muộn để giúp ích cho Don, nhưng những người khác hy vọng sẽ có lợi.)

2
Nó chỉ hoạt động với tôi khi tôi sử dụng các thuộc tính: trong tệp appname-servlet.xml của tôi. Sử dụng propertyConfigker được định nghĩa trong applicationContext.xml của tôi (không phải là Spring MVC) không hoạt động.
Asaf Mesika

Để đọc thêm một chút, chi tiết về một số điều này, hãy xem câu hỏi SOF này: stackoverflow.com/questions/6425795/
Kẻ

143

Cá nhân tôi thích cách mới này trong Spring 3.0 từ các tài liệu :

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

Không có getters hoặc setters!

Với các thuộc tính được tải thông qua cấu hình:

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

Để tăng thêm niềm vui của tôi, tôi thậm chí có thể kiểm soát nhấp vào biểu thức EL trong IntelliJ và nó đưa tôi đến định nghĩa thuộc tính!

Ngoài ra còn có phiên bản hoàn toàn không xml :

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

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

9
đảm bảo và thêm vào không gian tên uri xmlns: p = " springframework.org/schema/p " để sử dụng các thuộc tính tiền tố p :.
shane lee

3
Tại sao phương pháp này hoạt động trong bối cảnh thử nghiệm nhưng không phải trong bối cảnh chính?
luksmir

9
thở dài, tôi đã dành hàng giờ cố gắng để làm cho cách tiếp cận chỉ chú thích hoạt động và phát hiện ra những gì còn thiếu chỉ sau khi đọc câu trả lời này - tuyên bố về một hạt đậu tĩnh ma thuậtSauceYadaYada. Tình yêu mùa xuân!
Kranach

@barrymac hey barry, bạn có biết sự khác biệt giữa @Value (# {...}) và @Value ($ {...}). Cảm ơn bạn
Kim

1
Điều này làm việc cho tôi. Chỉ có một mẹo: chú thích @Component là bắt buộc.
yaki_nuka

121

Có một chú thích mới @Valuetrong Spring 3.0.0M3 . @Valuehỗ trợ không chỉ các #{...}biểu thức mà cả ${...}giữ chỗ


20
+1 Nếu một ví dụ có ích, thì đây là - @Value (value = "# {'$ {server.env}'}") hoặc đơn giản là @Value ("# {'$ {server.env}'}")
Somu

31

<context:property-placeholder ... /> là XML tương đương với PropertyPlaceholderConfigker.

Ví dụ: applicationContext.xml

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

Lớp thành phần

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

1
Đối với tôi, điều này chỉ hoạt động nếu tính năng tự động kích hoạt thông qua <context:component-scan base-package="com.company.package" />Để tham khảo, tôi đang sử dụng mùa xuân thông qua ApplicationContext, không phải trong bối cảnh web.
Mustafa

15

Một cách khác là thêm bean appProperIES hiển thị bên dưới:

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

Khi lấy ra, hạt này có thể được chuyển thành java.util.Properties thuộc tính có chứa results.maxgiá trị được đọc từ đó app.properties. Một lần nữa, bean này có thể được chèn phụ thuộc (như một thể hiện của java.util.ProperIES) vào bất kỳ lớp nào thông qua chú thích @Resource.

Cá nhân, tôi thích giải pháp này (với giải pháp khác mà tôi đã đề xuất), vì bạn có thể giới hạn chính xác các thuộc tính nào được hiển thị bởi appProperies và không cần phải đọc app.properations hai lần.


Làm việc cho tôi cũng vậy. Nhưng không có cách nào khác để tích lũy các thuộc tính từ PropertyPlaceholderConfigker thông qua chú thích @Value (khi sử dụng nhiều PropertyPlaceholderConfigker trong một số tệp XML congif.)?
Sa hoàng

9

Tôi cần phải có hai tệp thuộc tính, một tệp để sản xuất và ghi đè để phát triển (sẽ không được triển khai).

Để có cả hai, Bean Bean có thể được tự động và PropertyConfigker, bạn có thể viết:

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

và tham chiếu Bean Bean trong PropertyConfigker

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

7

Trước khi chúng tôi nhận được Spring 3 - cho phép bạn tiêm trực tiếp các hằng số thuộc tính vào các bean của mình bằng cách sử dụng các chú thích - tôi đã viết một lớp con của bean PropertyPlaceholderConfigker cũng làm điều tương tự. Vì vậy, bạn có thể đánh dấu setters tài sản của mình và Spring sẽ tự động cấp các thuộc tính của bạn vào các bean của bạn như sau:

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

Chú thích như sau:

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

PropertyAnnotationAndPlaceholderConfigker như sau:

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

}

Hãy thoải mái sửa đổi để nếm thử


3
Xin lưu ý rằng tôi đã tạo một dự án mới cho phần trên: code.google.com/p/spring-property-annotations
Ricardo Gladwell

7

Bạn cũng có thể chú thích lớp bạn:

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

Và có một biến như thế này:

@Autowired
private Environment env;

Bây giờ bạn có thể truy cập vào tất cả các tài sản của bạn theo cách này:

env.getProperty("database.connection.driver")

7

Con đường mùa xuân:
private @Value("${propertyName}") String propertyField;

là một cách mới để tiêm giá trị bằng cách sử dụng lớp "PropertyPlaceholderConfigker" của Spring. Một cách khác là gọi

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

Lưu ý: Đối với @Value, bạn không thể sử dụng static propertyField, nó chỉ ở dạng không tĩnh, nếu không, nó sẽ trả về null. Để sửa nó, một setter không tĩnh được tạo cho trường tĩnh và @Value được áp dụng ở trên setter đó.


7

Như đã đề cập @Value , công việc và nó khá linh hoạt khi bạn có thể có EL mùa xuân trong đó.

Dưới đây là một số ví dụ, có thể hữu ích:

//Build and array from comma separated parameters 
//Like currency.codes.list=10,11,12,13
@Value("#{'${currency.codes.list}'.split(',')}") 
private List<String> currencyTypes;

Khác để có được một settừ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;

Bạn cũng có thể đặt giá trị cho các kiểu nguyên thủy.

@Value("${amount.limit}")
private int amountLimit;

Bạn có thể gọi các phương thức tĩnh:

@Value("#{T(foo.bar).isSecurityEnabled()}")
private boolean securityEnabled;

Bạn có thể có logic

@Value("#{T(foo.bar).isSecurityEnabled() ? '${security.logo.path}' : '${default.logo.path}'}")
private String logoPath;

5

Một giải pháp khả thi là khai báo một bean thứ hai đọc từ cùng một tệp thuộc tính:

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

Bean có tên 'appProperIES' thuộc loại java.util.Properies và có thể được thêm vào phụ thuộc bằng cách sử dụng tài khoản @Resource được hiển thị ở trên.


4

Nếu bạn bị mắc kẹt khi sử dụng Spring 2.5, bạn có thể xác định một bean cho từng thuộc tính của mình và tiêm chúng bằng cách sử dụng vòng loại. Như thế này:

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

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

Nó không siêu dễ đọc nhưng nó hoàn thành công việc.


2

Tự động định giá trị tài sản vào đậu mùa xuân:

Hầu hết mọi người biết rằng bạn có thể sử dụng @Autowired để nói với Spring để tiêm một đối tượng vào một đối tượng khác khi nó tải bối cảnh ứng dụng của bạn. Một thông tin ít được biết đến hơn là bạn cũng có thể sử dụng chú thích @Value để đưa các giá trị từ tệp thuộc tính vào thuộc tính của bean. xem bài đăng này để biết thêm ...

công cụ mới trong mùa xuân 3.0 || giá trị đậu tự động | | giá trị tài sản tự động vào mùa xuân


2

Đối với tôi, đó là câu trả lời của @ Lucky, và đặc biệt, dòng

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

từ trang Debug Captain

đã khắc phục vấn đề của tôi. Tôi có một ứng dụng dựa trên ApplicationContext chạy từ dòng lệnh và đánh giá bằng một số ý kiến ​​về SO, Spring kết nối những thứ này khác với các ứng dụng dựa trên MVC.


1

Tôi nghĩ rằng đó là cách thuận tiện nhất để tiêm thuộc tính vào bean là phương thức setter.

Thí dụ:

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

Định nghĩa xml đậu:

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

Đối với mỗi propertyphương thức được đặt tên setProperty(value)sẽ được gọi.

Cách này đặc biệt hữu ích nếu bạn cần nhiều hơn một hạt dựa trên một lần thực hiện.

Ví dụ: nếu chúng ta xác định thêm một bean trong xml:

<bean id="Bean2" class="org.some.beans.MyBean">
    <property name="id" value="2"/>
    <property name="name" value="EnotherBean"/>
</bean>

Sau đó, mã như thế này:

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

Sẽ in

Bean id = 1 name = MyBean
Bean id = 2 name = AnotherBean

Vì vậy, trong trường hợp của bạn, nó sẽ trông như thế này:

@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

Nếu bạn cần thêm tính linh hoạt cho các cấu hình, hãy thử Cài đặt4jPlaceholderConfigker: http://sinstall4j.sourceforge.net/cienrelease / configSpringPlaceholder.html

Trong ứng dụng của chúng tôi, chúng tôi sử dụng:

  • Tùy chọn cấu hình PreProd- và Prod-System
  • Tùy chọn và biến môi trường JNDI (JNDI ghi đè lên tùy chọn) cho "mvn jetty: run"
  • Thuộc tính hệ thống cho UnitTests (chú thích @B BeforeClass)

Thứ tự mặc định mà khóa-giá trị-nguồn được kiểm tra trước, được mô tả trong:
http://sinstall4j.sourceforge.net/civerserelease/configDefault.html
Nó có thể được tùy chỉnh với settings4j.xml (chính xác với log4j.xml) trong lớp học

Hãy cho tôi biết ý kiến ​​của bạn: settings4j-user@lists.sourceforge.net

Trân trọng,
Harald


-1

Sử dụng lớp "PropertyPlaceholderConfigker" của Spring

Một ví dụ đơn giản hiển thị tệp thuộc tính đọc động như thuộc tính của 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> 

Tập tin tài sản

dev.app.jdbc.driver = com.mysql.jdbc.Driver

dev.app.jdbc.url = jdbc: mysql: // localhost: 3306 / addSpaceement

dev.app.jdbc.username = root

dev.app.jdbc.password = root

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.