Come posso inserire un valore di proprietà in un bean Spring che è stato configurato usando le annotazioni?


294

Ho un sacco di fagiolini che vengono raccolti dal percorso di classe tramite annotazioni, ad es

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

Nel file XML Spring, c'è un PropertyPlaceholderConfigurer definito:

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

Voglio iniettare una delle proprietà da app.properites nel bean mostrato sopra. Non posso semplicemente fare qualcosa del genere

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

Perché PersonDaoImpl non è presente nel file XML Spring (viene prelevato dal percorso di classe tramite annotazioni). Ho quanto segue:

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

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

Ma non mi è chiaro come accedo alla proprietà a cui sono interessato ppc?


1
Ho posto essenzialmente la stessa domanda, sebbene in uno scenario leggermente diverso: stackoverflow.com/questions/310271/… . Finora nessuno è stato in grado di rispondere.
Spencer Kormos,

Si noti che a partire dalla primavera 3.1, PropertyPlaceholderConfigurernon è più la classe consigliata. Preferisci PropertySourcesPlaceholderConfigurerinvece. In ogni caso, è possibile utilizzare la definizione XML più breve <context:property-placeholder />.
Michael Piefel,

Risposte:


292

Puoi farlo in Spring 3 usando il supporto EL. Esempio:

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

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

systemPropertiesè un oggetto implicito ed strategyBeanè un nome bean.

Un altro esempio, che funziona quando vuoi prendere una proprietà da un Propertiesoggetto. Mostra anche che puoi applicare @Valueai campi:

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

Ecco un post sul blog che ho scritto su questo per qualche informazione in più.


8
È systemPropertiessemplicemente System.getProperties()? Immagino che se voglio iniettare le mie proprietà in un bean Spring, devo definire un valore <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">quindi leggerlo da quello in un altro bean usando qualcosa del genere@Value("#{appProperties.databaseName}")
Dónal,

11
Assicurati di notare dalla risposta di max che puoi anche usare i segnaposto nelle espressioni $ {db.doStuff}, quindi non hai bisogno di PropertiesFactoryBean, solo un segnapostoConfigurer
gtrak

9
Puoi aggiungere le tue proprietà usando util: properties; ad es. <util: properties id = "config" location = "classpath: /spring/environment.properties" />. Vedi la risposta modificata per come ottenere il valore. (Mi rendo conto che probabilmente è troppo tardi per essere stato utile a Don, ma si spera che altri ne trarranno beneficio.)

2
Ha funzionato solo per me quando ho usato util: properties nel mio file appname-servlet.xml. L'uso di propertyConfigurer definito nel mio applicationContext.xml (non quello di Spring MVC) non ha funzionato.
Asaf Mesika,

Per un po 'più leggere, che elabora su alcune di queste, il check-out questa domanda SOF troppo: stackoverflow.com/questions/6425795/...
arcseldon

143

Personalmente adoro questo nuovo modo nella primavera 3.0 dai documenti :

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

Nessun getter o setter!

Con le proprietà caricate tramite la configurazione:

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

Per migliorare la mia gioia, posso persino controllare il clic sull'espressione EL in IntelliJ e questo mi porta alla definizione della proprietà!

C'è anche la versione totalmente non xml :

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

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

9
assicurati di aggiungere nello spazio dei nomi uri xmlns: p = " springframework.org/schema/p " per usare gli attributi p: prefixed.
shane lee,

3
Perché questo metodo funziona in un contesto di test ma non nel contesto principale?
luksmir,

9
sospiro, ho passato ore a cercare di far funzionare l'approccio basato solo sulle annotazioni e ho scoperto cosa manca solo dopo aver letto questa dichiarazione di risposta di un fagiolo statico magico PropertySauceYadaYada. Amore primavera!
Kranach

@barrymac hey barry, sai qual è la differenza tra @Value (# {...}) e @Value ($ {...}). Grazie
Kim,

1
Questo funziona per me. Un solo suggerimento: è richiesta l'annotazione @Component.
yaki_nuka,

121

C'è una nuova annotazione @Valuein Spring 3.0.0M3 . @Valuesupporta non solo #{...}espressioni ma anche ${...}segnaposto


20
+1 Se un esempio aiuta, eccolo qui: @Value (value = "# {'$ {server.env}'}") o semplicemente @Value ("# {'$ {server.env}'}")
Somu,

31

<context:property-placeholder ... /> è l'equivalente XML di PropertyPlaceholderConfigurer.

Esempio: applicationContext.xml

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

Classe del componente

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

1
Per me, ha funzionato solo se l'autowiring è abilitato tramite <context:component-scan base-package="com.company.package" />Per riferimento, sto usando Spring tramite il ApplicationContext, non in un contesto Web.
Mustafa,

15

Un'altra alternativa è aggiungere il bean appProperties mostrato di seguito:

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

Quando viene recuperato, questo bean può essere lanciato su un file java.util.Propertiesche conterrà una proprietà denominata il results.maxcui valore viene lettoapp.properties . Ancora una volta, questo bean può essere iniettato in dipendenza (come un'istanza di java.util.Properties) in qualsiasi classe tramite l'annotazione @Resource.

Personalmente, preferisco questa soluzione (all'altra che ho proposto), poiché puoi limitare esattamente quali proprietà sono esposte da appProperties e non è necessario leggere due volte app.properties.


Funziona anche per me. Ma non c'è altro modo per accedere alle proprietà da un PropertyPlaceholderConfigurer tramite l'annotazione @Value (quando si utilizzano più PropertyPlaceholderConfigurer in diversi file XML congif.)?
Zar

9

Devo avere due file delle proprietà, uno per la produzione e uno per lo sviluppo (che non verrà distribuito).

Per avere entrambi, un bean Proprietà che può essere autowired e un PropertyConfigurer, puoi scrivere:

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

e fare riferimento al bean Proprietà in PropertyConfigurer

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

7

Prima di ottenere Spring 3, che consente di iniettare costanti di proprietà direttamente nei bean utilizzando le annotazioni, ho scritto una sottoclasse del bean PropertyPlaceholderConfigurer che fa la stessa cosa. Quindi, puoi contrassegnare i setter delle tue proprietà e Spring autorizzerà le tue proprietà sui tuoi bean in questo modo:

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

L'annotazione è la seguente:

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

PropertyAnnotationAndPlaceholderConfigurer è il seguente:

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

}

Sentiti libero di modificare a piacere


3
Si noti che ho creato un nuovo progetto per quanto sopra: code.google.com/p/spring-property-annotations
Ricardo Gladwell

7

Puoi anche annotare la tua classe:

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

E avere una variabile come questa:

@Autowired
private Environment env;

Ora puoi accedere a tutte le tue proprietà in questo modo:

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

7

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

è un nuovo modo per iniettare il valore usando la classe "PropertyPlaceholderConfigurer" di Spring. Un altro modo è chiamare

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

Nota: per @Value, non è possibile utilizzare fieldField statico , deve essere solo non statico, altrimenti restituisce null. Per risolverlo, viene creato un setter non statico per il campo statico e @Value viene applicato sopra quel setter.


7

Come accennato @Valuefa il lavoro ed è abbastanza flessibile in quanto si può avere EL primavera in esso.

Ecco alcuni esempi che potrebbero essere utili:

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

Un altro per ottenere un setda alist

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

Puoi anche impostare valori per tipi primitivi.

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

Puoi chiamare metodi statici:

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

Puoi avere la logica

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

5

Una possibile soluzione è dichiarare un secondo bean che legge dallo stesso file delle proprietà:

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

Il bean chiamato 'appProperties' è di tipo java.util.Properties e può essere iniettato in dipendenza usando l'attruibute @Resource mostrato sopra.


4

Se rimani bloccato usando Spring 2.5 potresti definire un bean per ciascuna delle tue proprietà e iniettarle usando i qualificatori. Come questo:

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

e

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

Non è super leggibile ma fa il lavoro.


2

Autowiring valori di proprietà in bean di primavera:

Molte persone sanno che puoi usare @Autowired per dire a Spring di iniettare un oggetto in un altro quando carica il contesto dell'applicazione. Un nugget di informazioni meno noto è che è anche possibile utilizzare l'annotazione @Value per iniettare valori da un file di proprietà negli attributi di un bean. vedi questo post per maggiori informazioni ...

nuove cose in primavera 3.0 || valori bean autowiring || valori di proprietà autowiring in primavera


2

Per me, è stata la risposta di @ Lucky, e in particolare la linea

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

dalla pagina Captain Debug

questo ha risolto il mio problema. Ho un'app basata su ApplicationContext che parte dalla riga di comando e, a giudicare da una serie di commenti su SO, Spring li collega in modo diverso alle app basate su MVC.


1

Penso che il modo più conveniente per iniettare proprietà nel bean sia il metodo setter.

Esempio:

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

Definizione XML del bean:

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

Per ogni propertymetodo denominatosetProperty(value) verrà richiamato.

In questo modo è particolarmente utile se è necessario più di un bean in base a un'implementazione.

Ad esempio, se definiamo un altro bean in xml:

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

Quindi codificare in questo modo:

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

Stamperà

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

Quindi, nel tuo caso dovrebbe apparire così:

@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

Se hai bisogno di maggiore flessibilità per le configurazioni, prova Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

Nella nostra applicazione utilizziamo:

  • Preferenze per configurare PreProd e Prod-System
  • Preferenze e variabili di ambiente JNDI (JNDI sovrascrive le preferenze) per "mvn jetty: run"
  • Proprietà di sistema per UnitTest (annotazione @BeforeClass)

L'ordine predefinito per il quale viene verificato per primo il valore-chiave-origine è descritto in:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Può essere personalizzato con settings4j.xml (preciso a log4j.xml) nel tuo classpath.

Fammi sapere la tua opinione: settings4j-user@lists.sourceforge.net

con cordiali saluti,
Harald


-1

Utilizzare la classe "PropertyPlaceholderConfigurer" di Spring

Un semplice esempio che mostra il file delle proprietà letto dinamicamente come proprietà del 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> 

File delle proprietà

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

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

dev.app.jdbc.username = radice

dev.app.jdbc.password = radice

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.