Il valore di Spring Boot application.properties non viene popolato


97

Ho un'app Spring Boot molto semplice che sto cercando di far funzionare con una configurazione esternalizzata. Ho provato a seguire le informazioni sulla documentazione dello stivale a molla, tuttavia sto colpendo un blocco stradale.

Quando eseguo l'app sotto la configurazione esterna nel file application.properties non viene popolata nella variabile all'interno del bean. Sono sicuro che sto facendo qualcosa di stupido, grazie per i suggerimenti.

MyBean.java (situato in / src / main / java / foo / bar /)

package foo.bar;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @Value("${some.prop}")
    private String prop;

    public MyBean() {
        System.out.println("================== " + prop + "================== ");
    }
}

Application.java (situato in / src / main / java / foo /)

package foo;

import foo.bar.MyBean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    @Autowired
    private MyBean myBean;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

application.properties (situato in / src / main / resources /)

some.prop=aabbcc

Log output durante l'esecuzione dell'app Spring Boot:

grb-macbook-pro:properties-test-app grahamrb$ java -jar ./build/libs/properties-test-app-0.1.0.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.5.RELEASE)

2014-09-10 21:28:42.149  INFO 16554 --- [           main] foo.Application                          : Starting Application on grb-macbook-pro.local with PID 16554 (/Users/grahamrb/Dropbox/dev-projects/spring-apps/properties-test-app/build/libs/properties-test-app-0.1.0.jar started by grahamrb in /Users/grahamrb/Dropbox/dev-projects/spring-apps/properties-test-app)
2014-09-10 21:28:42.196  INFO 16554 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@67e38ec8: startup date [Wed Sep 10 21:28:42 EST 2014]; root of context hierarchy
2014-09-10 21:28:42.828  INFO 16554 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-09-10 21:28:43.592  INFO 16554 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-09-10 21:28:43.784  INFO 16554 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2014-09-10 21:28:43.785  INFO 16554 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.54
2014-09-10 21:28:43.889  INFO 16554 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-09-10 21:28:43.889  INFO 16554 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1695 ms
2014-09-10 21:28:44.391  INFO 16554 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-09-10 21:28:44.393  INFO 16554 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
================== null==================
2014-09-10 21:28:44.606  INFO 16554 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.679  INFO 16554 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-09-10 21:28:44.679  INFO 16554 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-09-10 21:28:44.716  INFO 16554 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.716  INFO 16554 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-09-10 21:28:44.902  INFO 16554 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-09-10 21:28:44.963  INFO 16554 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-09-10 21:28:44.965  INFO 16554 --- [           main] foo.Application                          : Started Application in 3.316 seconds (JVM running for 3.822)
^C2014-09-10 21:28:54.223  INFO 16554 --- [       Thread-2] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@67e38ec8: startup date [Wed Sep 10 21:28:42 EST 2014]; root of context hierarchy
2014-09-10 21:28:54.225  INFO 16554 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

4
E come dovrebbe @Valueessere sostituito prima di costruire un fagiolo? Il tuo modo di "rilevare" se il valore è impostato è sbagliato. In quel momento sarà sempre nullo in quanto @Valueverrà elaborato DOPO la costruzione dell'oggetto.
M. Deinum

Risposte:


165

Il modo in cui si esegue l'iniezione della proprietà non funzionerà, perché l'iniezione viene eseguita dopo la chiamata del costruttore.

È necessario eseguire una delle seguenti operazioni:

Soluzione migliore

@Component
public class MyBean {

    private final String prop;

    @Autowired
    public MyBean(@Value("${some.prop}") String prop) {
        this.prop = prop;
        System.out.println("================== " + prop + "================== ");
    }
}

Soluzione che funzionerà ma è meno testabile e leggermente meno leggibile

@Component
public class MyBean {

    @Value("${some.prop}")
    private String prop;

    public MyBean() {

    }

    @PostConstruct
    public void init() {
        System.out.println("================== " + prop + "================== ");
    }
}

Si noti inoltre che non è specifico per Spring Boot ma si applica a qualsiasi applicazione Spring


Ho dovuto aggiungere un'annotazione @Autowired al costruttore per farlo funzionare
Sébastien Tromp

1
Grazie per il consiglio. Questo dovrebbe essere nella documentazione di primavera in cui si parla dell'annotazione @Value, ma quei ragazzi non sembrano essere interessati al feedback sulla loro documentazione :(
Alex Worden

1
Mi ha salvato un po 'di frsutrazione. Grazie!
Robert Moskal

1
@geoand E se avessi più di 10 valori, dovresti digitarli tutti e 10 come hai fatto? O c'è un modo più pulito
Jesse

1
@ Jackie In effetti c'è un modo più pulito! Check out @ConfigurationPropertiese @EnableConfigurationPropertiesannotazioni
geoand

5

L'utente "geoand" ha ragione nel sottolineare qui le ragioni e nel dare una soluzione. Ma un approccio migliore consiste nell'incapsulare la configurazione in una classe separata, ad esempio classe java SystemContiguration e quindi iniettare questa classe nei servizi che si desidera utilizzare in quei campi.

Il tuo modo attuale (@grahamrb) di leggere i valori di configurazione direttamente nei servizi è soggetto a errori e causerebbe problemi di refactoring se il nome dell'impostazione di configurazione viene modificato.


Come non ci sarebbero meno mal di testa se avessi una classe separata per quella proprietà? Avrai ancora una stringa che deve essere ricordata durante il refactoring
dot_Sp0T

4
C'è solo un posto che devi "ricordare", non il numero N. I valori scalari che esistono su SystemContiguration forniscono una digitazione forte. Inoltre, se esiste una logica di business che ha "fork" ~~~ basati sui valori provenienti dalla configurazione ~~~ ..... è meglio iniettare qualcosa nella classe / businessLogic che necessita dei valori. Aka, è molto più facile deridere SystemContiguration quindi provare a far funzionare @Value ovunque.
granadaCoder

3

In realtà, per me di seguito funziona bene.

@Component
public class MyBean {

   public static String prop;

   @Value("${some.prop}")
   public void setProp(String prop) {
      this.prop= prop;
   }

   public MyBean() {

   }

   @PostConstruct
   public void init() {
      System.out.println("================== " + prop + "================== ");
   }

}

Ora, ovunque io voglia, invoca

MyBean.prop

restituirà valore.


2

Questa risposta può o non può essere applicabile al tuo caso ... Una volta che ho avuto un sintomo simile e ho ricontrollato il mio codice molte volte e tutto sembrava a posto, ma l' @Valueimpostazione non aveva ancora effetto. E poi dopo aver fatto File > Invalidate Cache / Restartcon il mio IntelliJ(il mio IDE), il problema è andato via ...

Questo è molto facile da provare, quindi potrebbe valere la pena provare


1

Usando la classe Environment possiamo ottenere l'applicazione. Valori delle proprietà

@Autowired,

private Environment env;

e accedi usando

String password =env.getProperty(your property key);

0

Segui questi passi. 1: - crea la tua classe di configurazione come sotto puoi vedere

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;

@Configuration
public class YourConfiguration{

    // passing the key which you set in application.properties
    @Value("${some.pro}")
    private String somePro;

   // getting the value from that key which you set in application.properties
    @Bean
    public String getsomePro() {
        return somePro;
    }
}

2: - quando hai una classe di configurazione, inserisci la variabile da una configurazione in cui ti serve.

@Component
public class YourService {

    @Autowired
    private String getsomePro;

    // now you have a value in getsomePro variable automatically.
}

0

Se stai lavorando in un grande progetto multimodulo, con diversi application.propertiesfile diversi , prova ad aggiungere il tuo valore al file delle proprietà del progetto principale .

Se non sei sicuro di quale sia il tuo progetto principale, controlla il pom.xmlfile del progetto , per un <parent>tag.

Questo ha risolto il problema per me.


0

Puoi usare EnvironmentClass per ottenere dati:

@Autowired
private Environment env;
String prop= env.getProperty('some.prop');
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.