O valor do Spring Boot application.properties não está preenchendo

97

Eu tenho um aplicativo Spring Boot muito simples que estou tentando fazer funcionar com alguma configuração externalizada. Tentei seguir as informações na documentação da inicialização da mola, mas estou encontrando um bloqueio na estrada.

Quando executo o aplicativo abaixo, a configuração externa no arquivo application.properties não é preenchida na variável dentro do bean. Tenho certeza que estou fazendo algo estúpido, obrigado por qualquer sugestão.

MyBean.java (localizado em / 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 (localizado em / 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 (localizado em / src / main / resources /)

some.prop=aabbcc

Registre a saída ao executar o aplicativo 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
fonte
4
E como deve @Valueser substituído antes de um bean ser construído? Sua maneira de "detectar" se o valor está configurado está errada. Nesse momento, ele sempre será nulo, pois @Valueserá processado APÓS a construção do objeto.
M. Deinum

Respostas:

166

A forma como você está realizando a injeção da propriedade não funcionará, pois a injeção é feita após a chamada do construtor.

Você precisa fazer o seguinte:

Melhor solução

@Component
public class MyBean {

    private final String prop;

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

Solução que funcionará, mas é menos testável e um pouco menos legível

@Component
public class MyBean {

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

    public MyBean() {

    }

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

Observe também que não é específico do Spring Boot, mas se aplica a qualquer aplicativo Spring

geoand
fonte
Tive que adicionar uma anotação @Autowired ao construtor para fazer funcionar
Sébastien Tromp
1
Obrigado pela dica. Isso deve estar na documentação do Spring, onde fala sobre a anotação @Value - mas esses caras não parecem estar interessados ​​em feedback sobre sua documentação :(
Alex Worden
1
Me salvou um pouco de nutrição. Obrigado!
Robert Moskal
1
@geoand E se você tivesse mais de 10 valores, você teria que digitar todos os 10 da maneira que você fez? Ou existe uma maneira mais limpa
Jesse
1
@Jackie De fato existe uma maneira mais limpa! Confira @ConfigurationPropertiese @EnableConfigurationPropertiesanotações
geoand
5

O usuário "geoand" está certo em apontar os motivos aqui e dar uma solução. Mas uma abordagem melhor é encapsular sua configuração em uma classe separada, digamos, classe java SystemContiguration e, em seguida, injetar essa classe em todos os serviços que você deseja usar nesses campos.

Sua maneira atual (@grahamrb) de ler valores de configuração diretamente nos serviços está sujeita a erros e causaria dores de cabeça de refatoração se o nome da definição de configuração fosse alterado.

Amit Kr
fonte
Como não haveria menos dores de cabeça se você tivesse uma classe separada para essa propriedade? Você ainda terá uma string que precisa ser lembrada ao refatorar
dot_Sp0T
4
Há apenas um lugar que você precisa "lembrar", não o número N. Os valores escalares que existem em SystemContiguration fornecem uma tipificação forte. Além disso, se houver lógica de negócios que tenha "bifurcações" ~~~ com base em valores provenientes da configuração ~~~ ..... é melhor injetar algo na classe / businessLogic que precisa dos valores. Sim, é muito mais fácil simular SystemContiguration do que tentar fazer com que @Value funcione em todo lugar.
granadaCoder
3

Na verdade, para mim abaixo funciona bem.

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

}

Agora, onde eu quiser, basta invocar

MyBean.prop

ele retornará o valor.

Raj Mohamad
fonte
2

Esta resposta pode ou não ser aplicável ao seu caso ... Uma vez eu tive um sintoma semelhante e verifiquei meu código várias vezes e tudo parecia bom, mas a @Valueconfiguração ainda não estava funcionando. E depois de fazer File > Invalidate Cache / Restartcom meu IntelliJ(meu IDE), o problema foi embora ...

Isso é muito fácil de tentar, então pode valer a pena tentar

leeyuiwah
fonte
1

Usando a classe Environment, podemos obter a aplicação. Valores de propriedades

@Autowired,

private Environment env;

e acessar usando

String password =env.getProperty(your property key);
ramakotireddy nagireddy
fonte
0

Siga esses passos. 1: - crie sua classe de configuração como abaixo, você pode ver

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 você tem uma classe de configuração, então injete a variável de uma configuração onde você precisa.

@Component
public class YourService {

    @Autowired
    private String getsomePro;

    // now you have a value in getsomePro variable automatically.
}
Fazle Subhan
fonte
0

Se você estiver trabalhando em um grande projeto com vários módulos, com vários application.propertiesarquivos diferentes , tente adicionar seu valor ao arquivo de propriedades do projeto pai .

Se você não tiver certeza de qual é o seu projeto pai, verifique se pom.xmlhá uma <parent>tag no arquivo do seu projeto .

Isso resolveu o problema para mim.

Janac Meena
fonte
0

Você pode usar EnvironmentClass para obter dados:

@Autowired
private Environment env;
String prop= env.getProperty('some.prop');
Abd Abughazaleh
fonte