Reputation: 3651
CONTEXT:
I process reports with @Scheduled
annotation and when invoke Component
from Service
property not getting initialized with @Value
annotation even it physically exists in .properties
and printed out in @PostConstruct
.
DESCRIPTION:
ReportProcessor
interface and InventoryReportProcessor
implementation:
@FunctionalInterface
interface ReportProcessor {
public void process(OutputStream outputStream);
}
@Component
public class InventoryReportProcessor implement ReportProcessor {
@Value("${reportGenerator.path}")
private String destinationFileToSave;
/*
@PostConstruct
public void init() {
System.out.println(destinationFileToSave);
}
*/
@Override
public Map<String, Long> process(ByteArrayOutputStream outputStream) throws IOException {
System.out.println(destinationFileToSave);
// Some data processing in here
return null;
}
}
I use it from
@Service
public class ReportService {
@Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
@Scheduled(cron = "*/10 * * * * *")
public void processReport() {
InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
My confusion comes from the fact that @Value
in Service
works fine but in @Component
it returns null
unless call in @PostConstruct
. Also, if call @PostConstruct
the value is still remains null
in the rest of the class code.
I found similar Q&A and I did research in Srping docs but so far no single idea why it works this way and what can be a solution?
Upvotes: 3
Views: 8658
Reputation: 814
Field injection is done after objects are constructed since obviously the container cannot set a property of something which doesn't exist.
at the time System.out.println(destinationFileToSave); triggers values are not being injected;
if you want to see it working try something like this
@Autowired
InventoryReportProcessor pross;
pross.process(ByteArrayOutputStream outputStream);
@PostConstruct works as it is being called after the object creation.
Upvotes: 5
Reputation: 6420
Spring will only parse @Value
annotations on beans it knows. The code you use creates an instance of the class outside the scope of Spring and as such Spring will do nothing with it.
One thing you can do is to create the instance explictly or use Autowire:
@Autowired
private ReportProcessor reportProcessor;
tl:dr If you have configured your application context correctly then a @Value
cannot be null as that will stop the correct startup of your application.
Change your Code from
@Value("${reportGenerator.path}")
private String destinationFileToSave;
to
@Value("${reportGenerator.path}")
public void setDestinationFileToSave(String destinationFileToSave) {
SendMessageController.destinationFileToSave = destinationFileToSave;
}
Upvotes: 0
Reputation: 1964
You need to Autowire the component to make your spring application aware of the component.
@Service
public class ReportService {
@Value("${mws.appVersion}")
private String appVersion;
/* Other initialization and public API methods*/
@Autowired
private ReportProcessor reportProcessor;
@Scheduled(cron = "*/10 * * * * *")
public void processReport() {
//InventoryReportProcessor reportProcessor = new InventoryReportProcessor();
Map<String, Long> skus = reportProcessor.process(new ByteArrayOutputStream());
}
}
Upvotes: 4