Dmytro Chasovskyi
Dmytro Chasovskyi

Reputation: 3651

Annotation @Value doesn't work properly in Spring Boot?

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

Answers (3)

SandOfTime
SandOfTime

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

Vishwa Ratna
Vishwa Ratna

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

Dhaval Simaria
Dhaval Simaria

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

Related Questions