El valor de la aplicación Spring Boot no se completa

97

Tengo una aplicación Spring Boot muy simple que estoy tratando de hacer funcionar con alguna configuración externa. Intenté seguir la información en la documentación de arranque de primavera, sin embargo, me encuentro con un obstáculo.

Cuando ejecuto la aplicación debajo de la configuración externa en el archivo application.properties no se completa en la variable dentro del bean. Estoy seguro de que estoy haciendo una estupidez, gracias por las sugerencias.

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

some.prop=aabbcc

Salida de registro al ejecutar la aplicación 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
fuente
4
¿Y cómo se debe @Valuereemplazar antes de construir un bean? Su forma de "detectar" si el valor está establecido es incorrecta. En ese momento siempre será nulo ya @Valueque se procesará DESPUÉS de la construcción del objeto.
M. Deinum

Respuestas:

165

La forma en que está realizando la inyección de la propiedad no funcionará, porque la inyección se realiza después de que se llama al constructor.

Debe realizar una de las siguientes acciones:

Mejor solucion

@Component
public class MyBean {

    private final String prop;

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

Solución que funcionará pero es menos comprobable y un poco menos legible

@Component
public class MyBean {

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

    public MyBean() {

    }

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

También tenga en cuenta que no es específico de Spring Boot, pero se aplica a cualquier aplicación de Spring

geo y
fuente
Tuve que agregar una anotación @Autowired al constructor para que funcione
Sébastien Tromp
1
Gracias por el consejo. Esto debería estar en la documentación de Spring donde habla sobre la anotación @Value, pero esos tipos no parecen estar interesados ​​en comentarios sobre su documentación :(
Alex Worden
1
Me salvó un poco de frsutration. ¡Gracias!
Robert Moskal
1
@geoand ¿Qué pasa si tiene más de 10 valores, tendría que escribir los 10 tal como lo hizo? ¿O hay una manera más limpia?
Jesse
1
@Jackie ¡De hecho, hay una forma más limpia! Echa un vistazo @ConfigurationPropertiesy @EnableConfigurationPropertiesanotaciones
geo y
5

El usuario "geoand" tiene razón al señalar las razones aquí y dar una solución. Pero un mejor enfoque es encapsular su configuración en una clase separada, digamos la clase java SystemContiguration y luego inyectar esta clase en los servicios que desee usar esos campos.

Su forma actual (@grahamrb) de leer los valores de configuración directamente en los servicios es propensa a errores y causaría dolores de cabeza por refactorización si se cambia el nombre de la configuración.

Amit Kr
fuente
¿Cómo no habría menos dolores de cabeza si tuviera una clase separada para esa propiedad? Todavía tendrá una cadena que debe recordarse al refactorizar
dot_Sp0T
4
Solo hay un lugar que debe "recordar", no el número N. Los valores escalares que existen en SystemContiguration le brindan una escritura segura. Además, si hay una lógica de negocios que tiene "bifurcaciones" ~~~ basadas en valores provenientes de la configuración ~~~ ..... es mejor inyectar algo en la clase / businessLogic que necesite los valores. Aka, es mucho más fácil burlarse de SystemContiguration que intentar que @Value funcione por todas partes.
granadaCoder
3

En realidad, para mí a continuación funciona bien.

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

}

Ahora donde quiera, solo invoca

MyBean.prop

devolverá valor.

Raj Mohamad
fuente
2

Esta respuesta puede o no ser aplicable a su caso ... Una vez tuve un síntoma similar y verifiqué mi código muchas veces y todo se veía bien, pero la @Valueconfiguración aún no tenía efecto. Y luego, después de hacer File > Invalidate Cache / Restartcon mi IntelliJ(mi IDE), el problema desapareció ...

Esto es muy fácil de probar, por lo que puede valer la pena intentarlo

leeyuiwah
fuente
1

Usando la clase Environment podemos obtener la aplicación. Valores de propiedades

@Autowired,

private Environment env;

y acceder usando

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

sigue estos pasos. 1: - crea tu clase de configuración como a continuación puedes 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: - cuando tenga una clase de configuración, inyecte la variable desde una configuración donde lo necesite.

@Component
public class YourService {

    @Autowired
    private String getsomePro;

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

Si está trabajando en un gran proyecto de varios módulos, con varios application.propertiesarchivos diferentes , intente agregar su valor al archivo de propiedades del proyecto principal .

Si no está seguro de cuál es su proyecto principal, consulte el pom.xmlarchivo de su proyecto para ver si tiene una <parent>etiqueta.

Esto me resolvió el problema.

Janac Meena
fuente
0

Puede usar EnvironmentClass para obtener datos:

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