Nie zapełnia się wartości Spring Boot application.properties

97

Mam bardzo prostą aplikację Spring Boot, którą próbuję uruchomić z jakąś zewnętrzną konfiguracją. Próbowałem postępować zgodnie z informacjami zawartymi w dokumentacji bagażnika sprężynowego, ale napotykam blokadę drogową.

Kiedy uruchamiam aplikację poniżej, konfiguracja zewnętrzna w pliku application.properties nie jest umieszczana w zmiennej w komponencie bean. Jestem pewien, że robię coś głupiego, dzięki za wszelkie sugestie.

MyBean.java (znajduje się w / 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 (znajduje się w / 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 (znajduje się w / src / main / resources /)

some.prop=aabbcc

Dane wyjściowe dziennika podczas wykonywania aplikacji 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
grahamrb
źródło
4
A jak należy @Valuego wymienić przed zbudowaniem fasoli? Twój sposób „wykrywania” nieprawidłowego ustawienia wartości. W tym momencie zawsze ma wartość zerową, ponieważ @Valuezostanie przetworzona PO budowie obiektu.
M. Deinum,

Odpowiedzi:

166

Sposób wykonywania wstrzyknięcia właściwości nie będzie działał, ponieważ wstrzyknięcie jest wykonywane po wywołaniu konstruktora.

Musisz wykonać jedną z następujących czynności:

Lepsze rozwiązanie

@Component
public class MyBean {

    private final String prop;

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

Rozwiązanie, które będzie działać, ale jest mniej testowalne i nieco mniej czytelne

@Component
public class MyBean {

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

    public MyBean() {

    }

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

Należy również pamiętać, że nie jest to specyficzne dla Spring Boot, ale dotyczy każdej aplikacji Spring

geoand
źródło
Musiałem dodać adnotację @Autowired do konstruktora, aby to działało
Sébastien Tromp
1
Dzięki za wskazówkę. Powinno to być w dokumentacji wiosennej, gdzie jest mowa o adnotacji @Value - ale ci faceci nie wydają się być zainteresowani opiniami na temat ich dokumentacji :(
Alex Worden
1
Uratował mi trochę frsutration. Dzięki!
Robert Moskal
1
@geoand Co jeśli masz ponad 10 wartości, czy musiałbyś wpisać wszystkie 10 tak, jak to zrobiłeś? Czy jest bardziej przejrzysty sposób
Jesse,
1
@Jackie Rzeczywiście jest czystszy sposób! Sprawdź @ConfigurationPropertiesi @EnableConfigurationPropertiesadnotacje
geo i
5

Użytkownik „geoand” ma rację, wskazując przyczyny tego problemu i podając rozwiązanie. Ale lepszym podejściem jest hermetyzacja konfiguracji w oddzielnej klasie, powiedzmy klasy java SystemContiguration, a następnie wstrzyknięcie tej klasy do wszystkich usług, z których chcesz korzystać z tych pól.

Twój obecny sposób (@grahamrb) odczytywania wartości konfiguracyjnych bezpośrednio do usług jest podatny na błędy i spowodowałby refaktoryzację bólu głowy, jeśli nazwa ustawienia konfiguracji zostanie zmieniona.

Amit Kr
źródło
Jak nie byłoby mniej bólów głowy, gdybyś miał osobną klasę dla tej nieruchomości? Nadal będziesz mieć ciąg znaków, który należy zapamiętać podczas refaktoryzacji
dot_Sp0T
4
Jest tylko jedno miejsce, które musisz „zapamiętać”, a nie numer N. Wartości skalarne, które istnieją w SystemContiguration, zapewniają mocne wpisywanie. Ponadto, jeśli istnieje logika biznesowa, która ma „rozwidlenia” ~~~ na podstawie wartości pochodzących z konfiguracji ~~~ ..... lepiej jest wprowadzić do klasy / businessLogic coś, co wymaga tych wartości. Aka, dużo łatwiej jest mockować SystemContiguration niż próbować zmusić @Value do działania w każdym miejscu.
granadaCoder
3

Właściwie dla mnie poniżej działa dobrze.

@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 + "================== ");
   }

}

Teraz, gdziekolwiek chcę, po prostu przywołaj

MyBean.prop

zwróci wartość.

Raj Mohamad
źródło
2

Ta odpowiedź może, ale nie musi, mieć zastosowanie w twoim przypadku ... Kiedyś miałem podobny objaw i wielokrotnie sprawdzałem mój kod wiele razy i wszystko wyglądało dobrze, ale @Valueustawienie nadal nie działało. A potem po zrobieniu File > Invalidate Cache / Restartz moim IntelliJ(moim IDE) problem zniknął ...

Jest to bardzo łatwe do wypróbowania, więc może warto spróbować

leeyuiwah
źródło
1

Korzystając z klasy Environment możemy pobrać aplikację. Wartości właściwości

@Autowired,

private Environment env;

i dostęp za pomocą

String password =env.getProperty(your property key);
ramakotireddy nagireddy
źródło
0

wykonaj następujące kroki. 1: - stwórz swoją klasę konfiguracji, jak widać poniżej

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: - jeśli masz klasę konfiguracyjną, wstrzyknij zmienną z konfiguracji, której potrzebujesz.

@Component
public class YourService {

    @Autowired
    private String getsomePro;

    // now you have a value in getsomePro variable automatically.
}
Fazle Subhan
źródło
0

Jeśli pracujesz w dużym projekcie wielomodułowym z kilkoma różnymi application.propertiesplikami, spróbuj dodać swoją wartość do pliku właściwości projektu nadrzędnego .

Jeśli nie masz pewności, który jest projektem nadrzędnym, sprawdź pom.xmlplik projektu pod kątem rozszerzenia<parent> tag.

To rozwiązało problem.

Janac Meena
źródło
0

Możesz użyć EnvironmentClass, aby uzyskać dane:

@Autowired
private Environment env;
String prop= env.getProperty('some.prop');
Abd Abughazaleh
źródło