Wie kann ich einen Eigenschaftswert in eine Spring Bean einfügen, die mithilfe von Anmerkungen konfiguriert wurde?

294

Ich habe ein paar Frühlingsbohnen, die über Anmerkungen vom Klassenpfad aufgenommen werden, z

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

In der Spring XML-Datei ist ein PropertyPlaceholderConfigurer definiert:

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

Ich möchte eine der Eigenschaften von app.properites in die oben gezeigte Bean einfügen. Ich kann so etwas nicht einfach machen

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

Da PersonDaoImpl nicht in der Spring-XML-Datei enthalten ist (es wird über Anmerkungen aus dem Klassenpfad abgerufen). Ich bin bis zu folgendem gekommen:

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

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

Aber mir ist nicht klar, wie ich auf die Immobilie zugreife, an der ich interessiert bin ppc?

Dónal
quelle
1
Ich habe im Wesentlichen dieselbe Frage gestellt, allerdings in einem etwas anderen Szenario: stackoverflow.com/questions/310271/… . Bisher konnte niemand darauf antworten.
Spencer Kormos
Bitte beachten Sie, dass ab Frühjahr 3.1 PropertyPlaceholderConfigurernicht mehr die empfohlene Klasse ist. Lieber PropertySourcesPlaceholderConfigurerstattdessen. In jedem Fall können Sie die kürzere XML-Definition verwenden <context:property-placeholder />.
Michael Piefel

Antworten:

292

Sie können dies in Spring 3 mithilfe der EL-Unterstützung tun. Beispiel:

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

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

systemPropertiesist ein implizites Objekt und strategyBeanein Bean-Name.

Ein weiteres Beispiel, das funktioniert, wenn Sie eine Eigenschaft von einem PropertiesObjekt abrufen möchten . Es zeigt auch, dass Sie @Valueauf Felder anwenden können:

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

Hier ist ein Blog-Beitrag, den ich darüber geschrieben habe, um ein bisschen mehr Informationen zu erhalten.

Auf Wiedersehen StackExchange
quelle
8
Ist systemPropertieseinfach System.getProperties()? Ich denke, wenn ich meine eigenen Eigenschaften in eine Spring Bean injizieren möchte, muss ich einen <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">dann gelesenen Wert daraus in eine andere Bean mit etwas wie@Value("#{appProperties.databaseName}")
Dónal
11
Stellen Sie aus der Antwort von max sicher, dass Sie auch Platzhalter in den Ausdrücken $ {db.doStuff} verwenden können. Dann benötigen Sie keine PropertiesFactoryBean,
sondern
9
Sie können Ihre eigenen Eigenschaften hinzufügen, indem Sie util verwenden: properties; Beispiel: <util: properties id = "config" location = "Klassenpfad: /spring/environment.properties" />. In der bearbeiteten Antwort erfahren Sie, wie Sie den Wert erhalten. (Mir ist klar, dass dies wahrscheinlich zu spät ist, um Don
2
Es hat nur funktioniert, wenn ich util: properties in meiner Datei appname-servlet.xml verwendet habe. Die Verwendung des in meiner applicationContext.xml definierten propertyConfigurer (nicht der Spring MVC) funktionierte nicht.
Asaf Mesika
Für eine weitere Lektüre, die einige davon ausführlich behandelt, lesen Sie auch diese SOF-Frage: stackoverflow.com/questions/6425795/…
arcseldon
143

Persönlich liebe ich diesen neuen Weg in Spring 3.0 aus den Dokumenten :

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

Keine Getter oder Setter!

Mit den Eigenschaften, die über die Konfiguration geladen werden:

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

Um meine Freude zu fördern, kann ich sogar den Klick auf den EL-Ausdruck in IntelliJ steuern und komme zur Eigenschaftsdefinition!

Es gibt auch die völlig nicht XML-Version :

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

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
Barrymac
quelle
9
Stellen Sie sicher, und fügen Sie im Namespace uri xmlns: p = " springframework.org/schema/p " hinzu, um die Attribute mit dem Präfix p: zu verwenden.
Shane Lee
3
Warum funktioniert diese Methode in einem Testkontext, aber nicht im Hauptkontext?
Luksmir
9
Seufzend habe ich stundenlang versucht, einen Annotations-Only-Ansatz zum Laufen zu bringen, und erst nach dem Lesen dieser Antworterklärung einer magischen statischen Bean PropertySauceYadaYada herausgefunden, was fehlt. Frühlingsliebe!
Kranach
@barrymac hey barry, weißt du was der Unterschied zwischen @Value (# {...}) und @Value ($ {...}) ist? Vielen Dank
Kim
1
Das funktioniert bei mir. Nur ein Tipp: Annotation @Component ist erforderlich.
Yaki_Nuka
121

@ValueIn Spring 3.0.0M3 gibt es eine neue Anmerkung . @Valueunterstützen nicht nur #{...}Ausdrücke, sondern auch ${...}Platzhalter

AdrieanKhisbe
quelle
20
+1 Wenn ein Beispiel hilft, ist es hier - @Value (value = "# {'$ {server.env}'}") oder einfach @Value ("# {'$ {server.env}'}")
Somu
31

<context:property-placeholder ... /> ist das XML-Äquivalent zum PropertyPlaceholderConfigurer.

Beispiel: applicationContext.xml

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

Komponentenklasse

 private @Value("${propertyName}") String propertyField;
Shane Lee
quelle
1
Für mich hat dies nur funktioniert, wenn das automatische Verdrahten über aktiviert ist. <context:component-scan base-package="com.company.package" />Als Referenz verwende ich spring über das ApplicationContext, nicht in einem Webkontext .
Mustafa
15

Eine andere Alternative ist das Hinzufügen der unten gezeigten 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>

Beim Abrufen kann diese Bean in eine umgewandelt java.util.Propertieswerden, die eine Eigenschaft mit dem Namen enthält, aus results.maxderen Wert gelesen wird app.properties. Auch diese Bean kann über die Annotation @Resource einer Abhängigkeit (als Instanz von java.util.Properties) in eine beliebige Klasse injiziert werden.

Persönlich bevorzuge ich diese Lösung (gegenüber der anderen, die ich vorgeschlagen habe), da Sie genau einschränken können, welche Eigenschaften von appProperties verfügbar gemacht werden, und app.properties nicht zweimal lesen müssen.

Dónal
quelle
Funktioniert auch bei mir. Aber gibt es keine andere Möglichkeit, über die @ Value-Annotation auf die Eigenschaften eines PropertyPlaceholderConfigurers zuzugreifen (wenn mehrere PropertyPlaceholderConfigurer in mehreren Congif-XML-Dateien verwendet werden)?
Zar
9

Ich benötige zwei Eigenschaftendateien, eine für die Produktion und eine Überschreibung für die Entwicklung (die nicht bereitgestellt wird).

Um beides zu haben, eine Eigenschaften-Bean, die automatisch verdrahtet werden kann, und einen PropertyConfigurer, können Sie schreiben:

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

und verweisen Sie auf die Properties Bean im PropertyConfigurer

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

Bevor wir Spring 3 erhalten, mit dem Sie Eigenschaftskonstanten mithilfe von Anmerkungen direkt in Ihre Beans einfügen können, habe ich eine Unterklasse der PropertyPlaceholderConfigurer-Bean geschrieben, die dasselbe tut. So können Sie Ihre Immobiliensetter markieren und Spring verdrahtet Ihre Immobilien automatisch wie folgt in Ihre Bohnen:

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

Die Anmerkung lautet wie folgt:

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

Der PropertyAnnotationAndPlaceholderConfigurer lautet wie folgt:

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

}

Fühlen Sie sich frei, nach Geschmack zu ändern

Ricardo Gladwell
quelle
3
Bitte beachten Sie, dass ich ein neues Projekt für die oben genannten erstellt habe: code.google.com/p/spring-property-annotations
Ricardo Gladwell
7

Sie können Ihre Klasse auch mit Anmerkungen versehen:

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

Und haben Sie eine Variable wie diese:

@Autowired
private Environment env;

Jetzt können Sie auf folgende Weise auf alle Ihre Immobilien zugreifen:

env.getProperty("database.connection.driver")
Alexis Gamarra
quelle
7

Frühlingsweg:
private @Value("${propertyName}") String propertyField;

ist eine neue Methode, um den Wert mithilfe der Spring-Klasse "PropertyPlaceholderConfigurer" einzufügen. Ein anderer Weg ist anzurufen

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

Hinweis: Für @Value können Sie das statische propertyField nicht verwenden . Es sollte nur nicht statisch sein, andernfalls wird null zurückgegeben. Um dies zu beheben, wird ein nicht statischer Setter für das statische Feld erstellt und @Value wird über diesem Setter angewendet.

hi.nitish
quelle
7

Wie erwähnt @Value erledigt es die Arbeit und es ist ziemlich flexibel, da Sie Feder EL darin haben können.

Hier einige Beispiele, die hilfreich sein könnten:

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

Ein anderer, um einen setvon einem zu bekommenlist

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

Sie können auch Werte für primitive Typen festlegen.

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

Sie können statische Methoden aufrufen:

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

Sie können Logik haben

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

Eine mögliche Lösung besteht darin, eine zweite Bean zu deklarieren, die aus derselben Eigenschaftendatei liest:

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

Die Bean mit dem Namen 'appProperties' ist vom Typ java.util.Properties und kann mithilfe des oben gezeigten @ Resource-Attributs abhängig injiziert werden.

Dónal
quelle
4

Wenn Sie mit Spring 2.5 nicht weiterkommen, können Sie für jede Ihrer Eigenschaften eine Bean definieren und diese mithilfe von Qualifikationsmerkmalen injizieren. So was:

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

und

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

Es ist nicht super lesbar, aber es erledigt den Job.

Nik
quelle
2

Autowiring-Eigenschaftswerte in Spring Beans:

Die meisten Leute wissen, dass Sie @Autowired verwenden können, um Spring anzuweisen, ein Objekt in ein anderes zu injizieren, wenn es Ihren Anwendungskontext lädt. Ein weniger bekanntes Nugget an Informationen ist, dass Sie auch die Annotation @Value verwenden können, um Werte aus einer Eigenschaftendatei in die Attribute einer Bean einzufügen. Weitere Informationen finden Sie in diesem Beitrag ...

neue Sachen im Frühjahr 3.0 || Autowiring Bean-Werte || Autowiring-Immobilienwerte im Frühjahr

Glücklich
quelle
2

Für mich war es die Antwort von @ Lucky und speziell die Zeile

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

von der Captain Debug Seite

das hat mein Problem behoben. Ich habe eine ApplicationContext-basierte App, die über die Befehlszeile ausgeführt wird. Nach einer Reihe von Kommentaren zu SO verdrahtet Spring diese anders als MVC-basierte Apps.

ben3000
quelle
1

Ich denke, es ist am bequemsten, Eigenschaften in die Bean-Setter-Methode zu injizieren.

Beispiel:

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

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

Für jede benannte propertyMethodesetProperty(value) wird aufgerufen.

Diese Methode ist besonders hilfreich, wenn Sie mehr als eine Bean basierend auf einer Implementierung benötigen.

Wenn wir zum Beispiel eine weitere Bean in xml definieren:

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

Dann Code wie folgt:

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

Wird gedruckt

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

In Ihrem Fall sollte es also so aussehen:

@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) {
            ...
        }
    }
}
Sergei Pikalev
quelle
0

Wenn Sie mehr Flexibilität für die Konfigurationen benötigen, versuchen Sie es mit dem Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

In unserer Anwendung verwenden wir:

  • Einstellungen zum Konfigurieren des PreProd- und Prod-Systems
  • Einstellungen und JNDI-Umgebungsvariablen (JNDI überschreibt die Einstellungen) für "mvn jetty: run"
  • Systemeigenschaften für UnitTests (@BeforeClass-Annotation)

Die Standardreihenfolge, in der die Schlüsselwertquelle zuerst überprüft wird, ist beschrieben in:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Adresse Sie kann mit einer settings4j.xml (genau auf log4j.xml) in Ihrem angepasst werden Klassenpfad.

Teilen Sie mir Ihre Meinung mit: [email protected]

mit freundlichen Grüßen,
Harald

brabenetz
quelle
-1

Verwenden Sie die Spring-Klasse "PropertyPlaceholderConfigurer"

Ein einfaches Beispiel, in dem die Eigenschaftendatei dynamisch als Bean-Eigenschaft gelesen wird

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

Eigenschaftendatei

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

Ravi Ranjan
quelle