2

I'm trying to use @Value annotation and auto-populate my variable from the properties file, but with no luck. Values are not being set and are null.

taskService.java

@Service
public class TaskService {
    @Value("${a}")
    String aa;

    public final RestTemplate restTemplate;

    public TaskService(RestTemplateBuilder restTemplateBuilder){
        System.out.println("----------xxxxxxxxxxx-------------" +aa);
        this.restTemplate = restTemplateBuilder.build();
    }

    public Task getTask(int taskId) throws TaskDoesNotExistException {
        try {
            return this.restTemplate.getForObject("/tasks/{taskId}", Task.class, taskId);
        } catch (HttpClientErrorException e) {
            if(e.getRawStatusCode() == 404)
                throw new TaskDoesNotExistException("Task not found", e);
        }
        return null;
    }
}

eventhandler.java

@Component
@RepositoryEventHandler(Application.class)
public class ApplicationRepositoryEventHandler {

    @Autowired
    TaskService taskService;

    @HandleBeforeCreate
    public void handleApplicationCreate(Application application) throws TaskDoesNotExistException {
        for (Integer taskId: application.getTasks()){
            taskService.getTask(taskId);
        }
    }
}
geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
user7700138
  • 123
  • 2
  • 4
  • 13
  • 1
    Please [edit] your question to show how you're obtaining the `TaskService` instance that has this problem. – Kenster Aug 30 '17 at 22:18
  • Hi ochi, where to use @Configuration annotation.. I have tried in service class and still returning null value – user7700138 Aug 30 '17 at 22:34
  • 1
    Values are injected after constructor completes. Simply add `@Value("${a}") String aa` to your constructor arguments. – Michael Dz Aug 31 '17 at 08:09

6 Answers6

4

Field injection occurs after the object is constructed.
See https://stackoverflow.com/a/6336013/1544715
Just inject aa via the constructor along with the testTemplateBuilder.
In general you should avoid field injection, or at least try and only use one type of injection per class.

Magnus
  • 7,952
  • 2
  • 26
  • 52
  • This makes a lot of sense, especially for the scenarios where we need to access the property value from within a constructor. Thank you, @magnus – Abhay Nagaraj Feb 13 '23 at 16:48
0

First check if your property file is loaded, Normally beginning of your output log, you should see name of the property file.

And then configure following bean in one of your configuration file, if not already done . This bean resolves '${}' in @Value.

 @Bean
 public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
 return new PropertySourcesPlaceholderConfigurer();
 }

Then use the way you are doing, It should resolve that property.And make sure that, TaskService bean is actually loaded (component scan package should includes this class)

Although i normally use above way, there is another way, you may need to check (just fyi, above should work)

 @Autowired
 private Environment env

Then use property wherever needed

 env.getRequiredProperty(“some.property”)
surya
  • 2,581
  • 3
  • 22
  • 34
  • Hi Surya, I have added the PropertySource bean in config and updated the code like below. Still, returns null value. I am new to this world.. pls let me know, if anything wrong here... – user7700138 Aug 31 '17 at 00:59
  • ``` @Service @Component @ComponentScan public class TaskService { @Value("${a:'5'}") String aa; public RestTemplate restTemplate; public TaskService(RestTemplateBuilder restTemplateBuilder){ this.restTemplate = restTemplateBuilder.build(); System.out.println("----------xxxxxxxxxxx-------------" + aa); } ``` – user7700138 Aug 31 '17 at 01:02
  • In ApplicationRepositoryEventHandler , are you sure taskservice (autowured) attr is not null? Make sure that bean is loaded. – surya Aug 31 '17 at 01:15
0

Quite often the problem is that the field injection is finished after the constructor has finished. That means you can access the value that was injected with @Value only after that. If a method getAA() ist added to TaskService:

public String getAA() {
 return aa;
}

and this method is called, the value of aa is not null, because the constructor was executed before and the field injection of @Value("${a}") set the value.

hameiste
  • 41
  • 5
0
 public TaskService(@Value("${a}") String aa, RestTemplateBuilder restTemplateBuilder){
        System.out.println("----------xxxxxxxxxxx-------------" +aa);
        this.restTemplate = restTemplateBuilder.build();
    }
Ganesh
  • 1
  • Please include at least a short explanation of your answer; don't just post code. https://meta.stackexchange.com/questions/114762/explaining-entirely-%E2%80%8C%E2%80%8Bcode-based-answers – kaya3 Nov 13 '19 at 18:24
0

You code can't work because Spring needs to first create instance of class (call the constructor) and THEN inject values to the fields.

public TaskService(RestTemplateBuilder restTemplateBuilder, ){
        System.out.println("----------xxxxxxxxxxx-------------" +aa); // 1
        this.restTemplate = restTemplateBuilder.build();
    }

1) in this moment there is no way Spring can inject value to the fields

Fix it like this:

@Service
public class TaskService {

    private final String aa;

    private final RestTemplate restTemplate;

    public TaskService(RestTemplateBuilder restTemplateBuilder, @Value("${a}" String aa){
        this.aa = aa;
        System.out.println("----------xxxxxxxxxxx-------------" +this.aa); // 1
        this.restTemplate = restTemplateBuilder.build();
    }
Gondy
  • 4,925
  • 4
  • 40
  • 46
0

It is because you don't define a constructor with aa as argument, neither does the class have a setter, so there is no way to inject it.

Please read https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-setter-injection.

WesternGun
  • 11,303
  • 6
  • 88
  • 157