125

I have a very simple Spring Boot app that I'm trying to get working with some externalised configuration. I've tried to follow the information on the spring boot documentation however I'm hitting a road block.

When I run the app below the external configuration in the application.properties file does not get populated into the variable within the bean. I'm sure I'm doing something stupid, thanks for any suggestions.

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

some.prop=aabbcc

Log output when executing the Spring Boot app:

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
  • 2,159
  • 2
  • 15
  • 22
  • 5
    And how should `@Value` be replaced before a bean is constructed? Your way of "detecting" if the value is set is wrong. At that moment it always be null as `@Value` will be processed AFTER object construction. – M. Deinum Sep 10 '14 at 11:49

11 Answers11

214

The way you are performing the injection of the property will not work, because the injection is done after the constructor is called.

You need to do one of the following:

Better solution

@Component
public class MyBean {

    private final String prop;

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

Solution that will work but is less testable and slightly less readable

@Component
public class MyBean {

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

    public MyBean() {

    }

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

Also note that is not Spring Boot specific but applies to any Spring application

geoand
  • 60,071
  • 24
  • 172
  • 190
  • I had to add an @Autowired annotation to the constructor to make it work – Sébastien Tromp Aug 25 '15 at 17:31
  • @SébastienTromp You're talking about the first solution, correct? – geoand Aug 26 '15 at 07:18
  • @geoand Both actually – Sébastien Tromp Aug 27 '15 at 12:28
  • You had to add an `@Autowired` annotation other than the one present? – geoand Aug 27 '15 at 12:50
  • The first solution works as is (and that's the one I'm using now), but for the second I had to add an `@Autowired` to the field in addition to the `@Value` – Sébastien Tromp Sep 04 '15 at 12:46
  • @SébastienTromp Thanks for the clarification! I haven't ever needed `@Autowired` along with `@Value` – geoand Sep 04 '15 at 13:04
  • 2
    Thanks for the tip. This should be in the Spring documentation where it talks about the @Value annotation - but those guys don't seem to be interested in feedback on their documentation :( – Alex Worden Sep 12 '15 at 22:17
  • @AlexWorden I understand your frustration, but in defence of the Spring team, I have never had any problems with questions/pull-requests etc. with them. On the contrary! Cheers :) – geoand Sep 14 '15 at 07:06
  • 1
    Saved me some frsutration. Thanks! – Robert Moskal May 25 '16 at 18:30
  • 1
    @geoand What if you have over 10 values, would you have to type all 10 just the way you did it? Or is there a cleaner way – JayC Dec 06 '16 at 19:59
  • 2
    @Jackie Indeed there is a cleaner way! Check out `@ConfigurationProperties` and `@EnableConfigurationProperties` annotations – geoand Dec 07 '16 at 08:53
  • @geoand I followed your first method, It was able to print to the console, but when I run localhost I receive a null value? I created a post on it. – JayC Dec 07 '16 at 14:03
  • @Jackie The latest you posted on SO doesn't seem to match this. Am I missing something? – geoand Dec 07 '16 at 14:42
  • @geoand the name is Why am I receiving a Null Exception when using Value Annotations? – JayC Dec 07 '16 at 14:47
  • @geoand very good solution. But how could I get this value prop outside the class. I created a singleton class AppilicationProperties.java, and in this class will load all properties value, what I need. Then in other place, I will do that: AppilicationProperties.getInstance().getProp(); But I got always null. Pls help, thx! – Ying Style Apr 26 '19 at 07:50
  • For Solution 1: With this constructor [[[ public MyBean(@Value("${some.prop}") String prop) ]]], how could I new a instance of this class? – Ying Style Apr 26 '19 at 08:02
  • @YingStyle I propose that you open a new issue to make it easier for people to help – geoand Apr 28 '19 at 09:02
  • The *@Value* annotation from Spring **needs single quotes** in order to work(Tested on Grails 4): `@Value('${some.prop}')` – Tadeu Sampaio Mar 06 '21 at 13:01
  • @geoand Can the "better solution" work if I need to call a constructor to create class? Because if I run the `Constructor()`, it will give me error since I do some calculations in the constructor itself. – Dhana D. Jan 17 '22 at 02:40
8

The user "geoand" is right in pointing out the reasons here and giving a solution. But a better approach is to encapsulate your configuration into a separate class, say SystemContiguration java class and then inject this class into what ever services you want to use those fields.

Your current way(@grahamrb) of reading config values directly into services is error prone and would cause refactoring headaches if config setting name is changed.

Amit Kumar
  • 813
  • 11
  • 13
  • 1
    How wouldn't there be any less headaches if you had a separate class for that property? You'll still have a string that needs to be remembered when refactoring – dot_Sp0T Jul 16 '18 at 21:27
  • 7
    There is only one place you need to "remember", not N number. The scalar values that exist on SystemContiguration give you strong typing. Also, if there is business logic that has "forks" ~~~based on values coming from the configuration~~~.....it is better to inject something into the class/businessLogic that needs the values. Aka, it is much easier to mock SystemContiguration then trying to get @Value working all over the place. – granadaCoder Aug 27 '18 at 22:19
5

This answer may or may not be applicable to your case ... Once I had a similar symptom and I double checked my code many times and all looked good but the @Value setting was still not taking effect. And then after doing File > Invalidate Cache / Restart with my IntelliJ (my IDE), the problem went away ...

This is very easy to try so may be worth a shot

leeyuiwah
  • 6,562
  • 8
  • 41
  • 71
4

Moved no argument constructor code to PostConstruct has done the trick for me. As it'll keep default bean loading workflow intact.

@Component
public class MyBean {

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

    @PostConstruct
    public void init() {
        System.out.println("================== " + prop + "================== ");
    }
}
Jaykishan
  • 1,409
  • 1
  • 15
  • 26
3

Actually, For me below works fine.

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

}

Now whereever i want, just invoke

MyBean.prop

it will return value.

R. Oosterholt
  • 7,720
  • 2
  • 53
  • 77
2

Using Environment class we can get application. Properties values

@Autowired,

private Environment env;

and access using

String password =env.getProperty(your property key);
Angel F Syrus
  • 1,984
  • 8
  • 23
  • 43
1

Simplest solution that solved this issue for me:

Add @PropertySource annotation to the Component/Service that needs to populate @Value field:

@Service
@PropertySource("classpath:myproperties.properties")
public class MyService {

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

  // some logic...
}

Make sure to add the properties file to the resource folder of the same module as your Service/Component.

solujic
  • 924
  • 1
  • 18
  • 43
0

follow these steps. 1:- create your configuration class like below you can see

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:- when you have a configuration class then inject in the variable from a configuration where you need.

@Component
public class YourService {

    @Autowired
    private String getsomePro;

    // now you have a value in getsomePro variable automatically.
}
Fazle Subhan
  • 211
  • 3
  • 5
0

If you're working in a large multi-module project, with several different application.properties files, then try adding your value to the parent project's property file.

If you are unsure which is your parent project, check your project's pom.xml file, for a <parent> tag.

This solved the issue for me.

Janac Meena
  • 3,203
  • 35
  • 32
0

You can use Environment Class to get data :

@Autowired
private Environment env;
String prop= env.getProperty('some.prop');
Abd Abughazaleh
  • 4,615
  • 3
  • 44
  • 53
0

You are getting this error because you are initializing the class with new keyword. To solve this, first you need to create the configuration class and under this class you need to create the bean of this class. When you will call it by using bean then it will work..

package ca.testing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.sql.Connection;
import java.sql.DriverManager;

@Component
@PropertySource("db.properties")
public class ConnectionFactory {
    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.url}")
    private String url;
    Connection connection;

    public Connection getConnection(){
        try {
            connection = DriverManager.getConnection(url, user, password);
            System.out.println(connection.hashCode());
        }catch (Exception e){
            System.out.println(e);
        }
        return connection;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(Config.class);
        ConnectionFactory connectionFactory= context.getBean(ConnectionFactory.class);
        connectionFactory.getConnection();
    }
}
Maninder
  • 1,539
  • 1
  • 10
  • 12