5

I'm trying to use values from my application.properties file in my service implementation class/bean. But when the bean is initialized through my config class, the property values are all null.

Config class:

@Configuration
public class AppConfig {
    @Bean AppServiceImpl appServiceImpl() {
        return new AppServiceImpl();
    }
}

Service class:

@Component
public class AppServiceImpl implements AppService {
    @Value("${value.one}")
    String value_one;

    @Value("${value.two}")
    String value_two;

    @Value("${value.three}")
    String value_three;

    //values are null here
    public AppServiceImpl() {
        functionOne(value_one, value_two, value_three);
    }
}

application.properties(under src/main/resources):

value.one=1
value.two=2
value.three=3

Doing some debugging i can see that the AppConfig class has found the properties file and if i try to declare @Value("${value.one}") String value_one; there it shows it has been given the value 1 as expected.

But in my AppServiceImpl class, all the values are null. What am I doing wrong here? How should this be done properly in Springboot? Or just Spring even.

Thanks.

mTv
  • 1,074
  • 17
  • 28

2 Answers2

10

If you use the values in the constructor they won't be available right away. Indeed they are injected on attribute. What's happening here is after spring created an instance then it will update the attribute value.

If you want to use those values in the constructor you should use constructor injection. Injection by constructor is a best practice.

public class AppServiceImpl implements AppService {
    String value_one;
    String value_two;
    String value_three;

    //values are null here
    public AppServiceImpl(String value1, String value2, String value3) {
        value_one = value1;
        value_two = value2;
        value_three = value3;
        functionOne(value_one, value_two, value_three);
    }
}

And your configuration class

@Configuration
public class AppConfig {
    @Bean
    AppServiceImpl appServiceImpl(@Value("${value.one}") String value1,  @Value("${value.two}") String value2,  @Value("${value.three}") String value3) {
        return new AppServiceImpl(value1, value2, value3);
    }
}
JEY
  • 6,973
  • 1
  • 36
  • 51
  • Why is this preferable over a @PostConstruct function? – mTv May 25 '18 at 09:03
  • Because you have to wait the context to fully initialize, it hides circular dependencies, it's more difficult to test. – JEY May 25 '18 at 09:12
  • Fair enough. Thanks! – mTv May 25 '18 at 09:18
  • 2
    One last point is this way you are not tied to spring and can reuse class in any project. – JEY May 25 '18 at 09:19
  • you have a slight error in your constructor: 1) you're using the instance variables, which are still null. 2) you don't assign the injected values to the instance variables – Lino May 26 '18 at 10:43
4

Those values are injected after the instance is created. So they are null in the constructor.

To execute a method after the values are injected use @javax.annotation.PostConstruct:

@PostConstruct
public void init(){ // method name doesn't matter
     functionOne(value_one, value_two, value_three);
}
Lino
  • 19,604
  • 6
  • 47
  • 65
  • @JEY yes, your answer with constructor injection is prefered, but in some cases constructor injection can not be used. E.g. in JUnit-Testcases – Lino May 25 '18 at 08:52
  • 1
    Seems like this worked. But i should use JEY's way with constructor injection instead? – mTv May 25 '18 at 08:59