0

I've been trying to implement @PostConstruct and @PreDestroy methods in an Account class of mine. Neither of them worked in the following situation, but for the sake of brevity, I'll only talk about @PostConstruct.

I'm using Spring Batch's readers to load these accounts from a fixed-length file. So far, so good, except when my reader creates these accounts, it apparently does not call the @PostConstruct method (debug breakpoints are never activated, and log messages are not printed out).
The reader is only custom in the sense that it's a custom class extending FlatFileItemReader<Account> and setting values in the constructor.

Adding the exact same initialization method (that never got called in the Account class) to the reader itself works just fine.
I.e. if the @PostConstruct method should be called upon reader initialization, it works. Just not when the reader itself initializes accounts annotated with @PostConstruct.

If I add a breakpoint or log message in the constructor of Account directly, it also works without an issue.


Is this desired behaviour by Spring Batch? Or could this be caused by any configuration of mine?

Another question's answer mentioned that annotations such as @PostConstruct "only apply to container-managed beans", not if "you are simply calling new BlogEntryDao() yourself".

Is this what's happening here - Spring Batch calling new Account(...) directly, without registering them in the container? After all, I never have these accounts available as beans or anything.

PixelMaster
  • 895
  • 10
  • 28
  • See @Ivans answer. If an `Account` represents a line of the file, then what additional logic needs to be done in a post construct or pre destroy `Account` method? Does that logic really belong within `Account`? What is the logic? – Andrew S Sep 07 '18 at 17:35
  • In my concrete use case, I merely wanted to try out the `PostConstruct` and `PreDestroy` annotations, and randomly picked this class. I'm actually not sure if there's a real-world use case where these methods would be preferable to another step in the job. Nevertheless, I'm curious as to why they're not called. – PixelMaster Sep 07 '18 at 18:07

2 Answers2

1

Is your Account class annotated with @Component, @Bean or @Service? If you create objects of account class like Account c = new Account() the nSpring doesn't know about creation of such objects. Because of that Spring doesn't call method annotated with @postConstruct

Ivan
  • 8,508
  • 2
  • 19
  • 30
  • No, my `Account` class is not annotated with `@Component` & Co. Nevertheless, *I myself* never call `new Account()`, creating the accounts is handled by the `ItemReader` implementations provided by Spring Batch by default. My guess is that the issue lies somewhere within this implementation, I'm just not sure if it's not supposed to work by design or not. – PixelMaster Sep 07 '18 at 18:01
  • @PixelMaster PostConstruct method is called only for objects created inside Spring context by Spring IoC container – Ivan Sep 07 '18 at 18:40
1

when my reader creates these accounts, it apparently does not call the @PostConstruct method

@PostConstruct and @PreDestroy methods are called by the Spring container after creating and before destroying the instance of your bean. If your object is not managed by Spring, those methods will not be called.

I'm using Spring Batch's readers to load these accounts from a fixed-length file

In this case, you should have already configured a FieldSetMapper to map fields to an Account instance. If you use the BeanWrapperFieldSetMapper, you can set the PrototypeBeanName which is the name of a bean (for example of type Account) of scope prototype (so that an instance is created for each line). This way, Account instances will be managed by Spring, used by Spring Batch reader, and your method annotated with PostConstruct will be called. Here is an example:

@Bean
@Scope("prototype")
public Account account() {
    return new Account();
}

@Bean
public BeanWrapperFieldSetMapper<Account> beanMapper() {
    BeanWrapperFieldSetMapper<Account> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
    fieldSetMapper.setPrototypeBeanName("account");
    return fieldSetMapper;
}

@Bean
public FlatFileItemReader<Account> accountReader() {
    return new FlatFileItemReaderBuilder<Account>()
            .name("accountsItemReader")
            .resource(new ClassPathResource("accounts.txt"))
            .fixedLength()
            .columns(new Range[] {new Range(1, 1), new Range(2, 4)})
            .names(new String[]{"id", "name"})
            .fieldSetMapper(beanMapper())
            .build();
}

More details about this in the Javadoc.

Hope this helps.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50