I'm learing Spring Boot and I have probably very simple question, but it's not clear enough for me. I'm facing some problem with @Value annotation - I would like to know why apprication property cannot be injected to class parameter.
I prepared some very basic project using Spring Initializr and I added one property to my "application.properties" resource. Moreover, I created two additional classes: "YellowCar" (which works fine) and "RedCar" (which does not work - the parameter cannot be properly injected).
"application.properties" file:
car.age=15
The main class of my application:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class HelperApplication implements CommandLineRunner {
@Autowired
private Environment env;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (new YellowCar());
}
}
RedCar is build by passing the Environment variable to constructor:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class RedCar {
private int age;
@Autowired
public RedCar (Environment env) {
this.age = new Integer(env.getRequiredProperty("car.age")).intValue();
}
@Override
public String toString() {
return "Car [age=" + age + "]";
}
}
YellowCar is build without passing the Environment variable to the constructor, but using the @Value annotation:
package com.example.helper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class YellowCar {
@Value("${car.age}")
private int age;
@Override
public String toString() {
return "YellowCar [age=" + age + "]";
}
}
This is the program output:
Car [age=15]
YellowCar [age=0]
As you can see, age of YellowCar was not properly injected (it equals 0).
My goal: I'd like not to pass everywhere Environment object to the constructor of other classes... I'd like to use @Value annotatnio instead. Could somebody explain me: 1) why my code is not working? 2) how this code should be updated in order to get the following output?
Car [age=15]
YellowCar [age=15]
Thanks!
This happens, because you instantiate YellowCar
directly. In order to make thing work, @Autowire
YellowCar
inside HelperApplication
and use this injected instance like this(not tested):
@Autowired
private YellowCar yellowCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (yellowCar);
}
Explanation:
When you creating class using new, Spring knows nothing about this new instance, hence, it cannot inject anything. But when instance is created through Spring infrastructure (I'm trying not use a lot of slang words here), it will have all fields injected: when you mark class with @Component
annotation or create method with @Bean
annotation, you tell Spring, that you want it to be a Bean. On application startup, spring creates instances of this beans - by default only one instance per context - and inject this instance into all other beans, where requested. In this case, this instance is being processed by spring infrastructure (exact class which do it - AutowiredAnnotationBeanPostProcessor
), and instances of other beans will be injected into our bean.
UPD: You can do the same thing with RedCar
:
@Autowired
private YellowCar yellowCar;
@Autowired
private RedCar redCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println (redCar);
System.out.println (yellowCar);
}