Jak wstrzyknąć wartość właściwości do Spring Bean, który został skonfigurowany za pomocą adnotacji?

294

Mam kilka wiosennych ziaren, które są zbierane ze ścieżki klas za pomocą adnotacji, np

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

W wiosennym pliku XML zdefiniowano właściwość PropertyPlaceholderConfigurer :

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

Chcę wstrzyknąć jedną z właściwości app.properites do pokazanego powyżej komponentu bean. Nie mogę po prostu zrobić czegoś takiego

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

Ponieważ PersonockeyImpl nie występuje w pliku Spring XML (jest pobierany ze ścieżki klas za pomocą adnotacji). Mam do tego, co następuje:

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

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

Ale nie jest dla mnie jasne, jak uzyskać dostęp do nieruchomości, która mnie interesuje ppc?

Dónal
źródło
1
Poprosiłem zasadniczo to samo pytanie, choć w nieco inny scenariusz: stackoverflow.com/questions/310271/... . Jak dotąd nikt nie był w stanie na to odpowiedzieć.
Spencer Kormos
Należy pamiętać, że od wiosny 3.1 PropertyPlaceholderConfigurernie jest już zalecaną klasą. Wolę PropertySourcesPlaceholderConfigurerzamiast tego. W każdym razie możesz użyć krótszej definicji XML <context:property-placeholder />.
Michael Piefel

Odpowiedzi:

292

Możesz to zrobić wiosną 3, korzystając ze wsparcia EL. Przykład:

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

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

systemProperties jest niejawnym przedmiotem i strategyBean jest nazwą fasoli.

Jeszcze jeden przykład, który działa, gdy chcesz pobrać właściwość z Propertiesobiektu. Pokazuje także, że możesz zastosować @Valuedo pól:

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

Oto post na blogu , o którym napisałem, aby uzyskać więcej informacji.

Do widzenia StackExchange
źródło
8
Jest systemPropertiespo prostu System.getProperties()? Myślę, że jeśli chcę wstrzyknąć własne właściwości do fasoli wiosennej, muszę zdefiniować <bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">wartości odczytane z tej fasoli do innej fasoli, używając czegoś takiego@Value("#{appProperties.databaseName}")
Dónal
11
Pamiętaj, aby zauważyć z odpowiedzi max, że możesz również używać symboli zastępczych w wyrażeniach $ {db.doStuff}, a następnie nie potrzebujesz PropertiesFactoryBean, tylko placeholderConfigurer
gtrak
9
Możesz dodawać własne właściwości za pomocą util: properties; np. <util: properties id = "config" location = "ścieżka klasy: /spring/environment.properties" />. Zobacz edytowaną odpowiedź, w jaki sposób uzyskać wartość. (Zdaję sobie sprawę, że prawdopodobnie jest już za późno, by pomóc Donowi, ale mam nadzieję, że inni skorzystają).
2
Działa to tylko dla mnie, gdy użyłem util: properties w moim pliku appname-servlet.xml. Używanie propertyConfigurer zdefiniowanego w mojej aplikacjiContext.xml (nie Spring MVC) nie działało.
Asaf Mesika
Aby zapoznać się z niektórymi z nich, przeczytaj również pytanie SOF: stackoverflow.com/questions/6425795/…
arcseldon
143

Osobiście uwielbiam ten nowy sposób w Spring 3.0 z dokumentacji :

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

Żadnych pobierających ani ustawiających!

Z właściwościami ładowanymi przez config:

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

Aby pogłębić moją radość, mogę nawet kontrolować kliknięcie wyrażenia EL w IntelliJ i przenosi mnie do definicji właściwości!

Istnieje również całkowicie inna wersja XML :

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

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
barrymac
źródło
9
upewnij się i dodaj w przestrzeni nazw uri xmlns: p = " springframework.org/schema/p ", aby użyć atrybutów p: prefiks.
shane lee
3
Dlaczego te metody działają w kontekście testowym, ale nie w kontekście głównym?
luksmir
9
westchnienie, spędziłem godzinę próbując sprawić, by podejście oparte wyłącznie na adnotacjach działało, i odkryłem, czego brakuje dopiero po przeczytaniu tej odpowiedzi-deklaracji magicznej statycznej fasoli PropertySauceYadaYada. Wiosenna miłość!
Kranach
@ barrymac hej Barry, czy wiesz, jaka jest różnica między @Value (# {...}) a @Value ($ {...}). Dziękuję
Kim
1
To działa dla mnie. Tylko jedna wskazówka: adnotacja @ Składnik jest wymagana.
yaki_nuka
121

Dodano nową adnotację @Valuedo Spring 3.0.0M3 . @Valueobsługują nie tylko #{...}wyrażenia, ale także ${...}symbole zastępcze

AdrieanKhisbe
źródło
20
+1 Jeśli przykład pomaga, oto on - @Value (wartość = "# {'$ {server.env}'}") lub po prostu @Value ("# {'$ {server.env}'}")
Somu,
31

<context:property-placeholder ... /> jest odpowiednikiem XML właściwości PropertyPlaceholderConfigurer.

Przykład: applicationContext.xml

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

Klasa komponentów

 private @Value("${propertyName}") String propertyField;
Shane Lee
źródło
1
Dla mnie działało to tylko wtedy, gdy włączono automatyczne okablowanie za pomocą. <context:component-scan base-package="com.company.package" />W celach informacyjnych używam sprężyny za pośrednictwem ApplicationContext, a nie w kontekście sieciowym.
Mustafa,
15

Inną alternatywą jest dodanie fasoli appProperties pokazanej poniżej:

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

Po pobraniu, ta fasola może być rzutowana na java.util.Propertieswłaściwość, która będzie zawierać nazwę results.maxo nazwie, z której odczytana jest wartośćapp.properties . Znów ta fasola może być wstrzykiwana w zależności (jako instancja java.util.Properties) do dowolnej klasy za pomocą adnotacji @Resource.

Osobiście wolę to rozwiązanie (od drugiego, które zaproponowałem), ponieważ możesz ograniczyć dokładnie, które właściwości są udostępniane przez appProperties, i nie musisz dwa razy czytać app.properties.

Dónal
źródło
Dla mnie też działa. Ale czy nie ma innego sposobu na uzyskanie dostępu do właściwości z PropertyPlaceholderConfigurer za pomocą adnotacji @Value (w przypadku używania wielu PropertyPlaceholderConfigurer w kilku plikach XML congif)?
Czar
9

Muszę mieć dwa pliki właściwości, jeden dla produkcji i zastąpienie dla rozwoju (które nie zostanie wdrożone).

Aby mieć oba elementy, Bean właściwości, które można autowiredować oraz PropertyConfigurer, możesz napisać:

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

i odwołaj się do właściwości Bean w PropertyConfigurer

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties" ref="appProperties" />
</bean>
Willi aus Rohr
źródło
7

Zanim otrzymamy Spring 3 - który pozwala ci wstrzykiwać stałe właściwości bezpośrednio do ziaren za pomocą adnotacji - napisałem podklasę fasoli PropertyPlaceholderConfigurer, która robi to samo. Możesz więc oznaczyć osoby ustawiające właściwości, a Spring automatycznie zapisze twoje właściwości w fasoli w następujący sposób:

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

Adnotacja wygląda następująco:

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

Właściwość PropertyAnnotationAndPlaceholderConfigurer wygląda następująco:

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

}

Zmodyfikuj do smaku

Ricardo Gladwell
źródło
3
Pamiętaj, że stworzyłem nowy projekt dla powyższych: code.google.com/p/spring-property-annotations
Ricardo Gladwell
7

Możesz także dodać adnotację do swojej klasy:

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

I mieć zmienną taką jak ta:

@Autowired
private Environment env;

Teraz możesz uzyskać dostęp do wszystkich swoich nieruchomości w ten sposób:

env.getProperty("database.connection.driver")
Alexis Gamarra
źródło
7

Wiosna sposób:
private @Value("${propertyName}") String propertyField;

to nowy sposób wprowadzania wartości za pomocą klasy Spring „PropertyPlaceholderConfigurer”. Innym sposobem jest zadzwonić

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

Uwaga: W przypadku @Value, nie można używać statycznego propertyField, powinno być tylko non-static, w przeciwnym wypadku zwraca NULL. Aby to naprawić, dla pola statycznego tworzony jest niestacjonarny setter, a nad tym selektorem stosowana jest wartość @Value.

cześć
źródło
7

Jak wspomniano @Value, praca jest dość elastyczna, ponieważ można w niej sprężynować EL.

Oto kilka przykładów, które mogą być pomocne:

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

Kolejny do zdobycia setodlist

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

Możesz także ustawić wartości dla typów pierwotnych.

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

Możesz wywoływać metody statyczne:

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

Możesz mieć logikę

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

Możliwym rozwiązaniem jest zadeklarowanie drugiego komponentu bean, który czyta z tego samego pliku właściwości:

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

Fasola o nazwie „appProperties” jest typu java.util.Properties i może być wstrzykiwana w zależności za pomocą powyższego atrybutu @Resource attruibute.

Dónal
źródło
4

Jeśli utkniesz przy użyciu Spring 2.5, możesz zdefiniować fasolę dla każdej ze swoich właściwości i wstrzyknąć ją za pomocą kwalifikatorów. Lubię to:

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

i

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

Nie jest bardzo czytelny, ale wykonuje zadanie.

Nik
źródło
2

Autowiring wartości nieruchomości w fasoli wiosennej:

Większość ludzi wie, że możesz użyć @Autowired, aby powiedzieć Springowi, aby wstrzyknął jeden obiekt w inny, gdy ładuje kontekst aplikacji. Mniej znanym modelem użytkowym informacji jest to, że można również użyć adnotacji @Value, aby wstrzyknąć wartości z pliku właściwości do atrybutów komponentu bean. zobacz ten post, aby uzyskać więcej informacji ...

nowe rzeczy wiosną 3.0 || wartości fasoli autowiringowej || wartości właściwości autowiring na wiosnę

Szczęściarz
źródło
2

Dla mnie była to odpowiedź @ Lucky'ego, a konkretnie linia

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

ze strony debugowania kapitana

to naprawiło mój problem. Mam aplikację opartą na ApplicationContext uruchomioną z wiersza polecenia i sądząc po wielu komentarzach na temat SO, Spring łączy je inaczej niż aplikacje oparte na MVC.

ben3000
źródło
1

Myślę, że najwygodniejszym sposobem na wstrzyknięcie właściwości fasoli jest metoda ustawiająca.

Przykład:

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

Definicja xml fasoli:

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

Dla każdej nazwanej propertymetody setProperty(value)zostanie wywołana.

Ten sposób jest szczególnie pomocny, jeśli potrzebujesz więcej niż jednego komponentu bean w oparciu o jedną implementację.

Na przykład, jeśli zdefiniujemy jeszcze jedną fasolę w xml:

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

Następnie kod w ten sposób:

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

Wydrukuje

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

W twoim przypadku powinno to wyglądać tak:

@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) {
            ...
        }
    }
}
Siergiej Pikalev
źródło
0

Jeśli potrzebujesz większej elastyczności dla konfiguracji, wypróbuj Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html

W naszej aplikacji używamy:

  • Preferencje konfiguracji systemu PreProd i Prod
  • Preferencje i zmienne środowiskowe JNDI (JNDI zastępuje preferencje) dla „mvn jetty: run”
  • Właściwości systemu dla UnitTests (adnotacja @BeforeClass)

Domyślna kolejność, w której klucz-wartość-źródło jest sprawdzana jako pierwsza, została opisana w:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Można ją dostosować za pomocą ustawienia4j.xml (dokładny do log4j.xml) w swoim ścieżka klasy.

Daj mi znać swoją opinię: [email protected]

z poważaniem
Harald

Brabenetz
źródło
-1

Użyj klasy „PropertyPlaceholderConfigurer” Springa

Prosty przykład pokazujący plik właściwości odczytywany dynamicznie jako właściwość komponentu 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> 

Plik właściwości

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
źródło