I have a spring boot application which has two beans - AppState
and Users
.
AppState
depends on the bean Users
as it autowires it. The skeleton codes for the two beans are as follows.
@Component
@EnableScheduling
public class Users {
@Getter // lombok
private List<String> users;
@PostConstruct
public void init(){
users = new ArrayList<>();
load(); // I removed this later
}
@Scheduled(fixedRate = 3600000)
public void load(){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}
}
AppState
is
@Component
public class AppState {
@Atowired
private Users users;
public List<String> getUsers(){
return users.getUsers();
}
}
I noticed that the method load()
in Users
was getting triggered twice - probably once during init()
and the other time while scheduling load()
right after Users
bean had been created. So I removed the call to load()
in init()
. This fixed the redundant call issue.
However, now I find that my service starts as soon as AppState
and Users
beans have been created, even though Users
has not been populated with data yet. This is risky for me as the service, during this time, will return 0 users if queried.
I need help with ANY of the following.
Should I move load()
back into init()
to make sure that when the bean is done with PostConstruct
, it does have all users info? If I go this route, how can I prevent redundant run of load()
?
If load()
should stay out of init()
, how can I ensure that AppState
is not ready unless Users
has executed load()
? I tried using the following code in AppState
but it just hangs.
The code is as follows.
@PostConstruct
public void appStateInit(){
while(users.getUsers().size()==0){
try{
Thread.sleep(10000); // sleep 10s
}catch(whatever){
}
}
}
I would suggest having a flag
in the class and setting it to true
once init() has finished. You can skip the execution of load()
if flag
is not set yet, e.g.:
private AtomicBoolean shouldExecute;
@PostConstruct
public void init(){
users = new ArrayList<>();
shouldExecute = true;
}
@Scheduled(fixedRate = 3600000)
public void load(){
if(shouldExecute){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}
}
Also, another solution will be to configure initialDelay
in @Scheduled
annotation (documentation here) which would delay the first execution by configured number of milliseconds, e.g:
@Scheduled(fixedRate = 3600000, initialDelay=180000)
public void load(){
// load list of users from (say) a file and populate ArrayList 'users'
// this method takes at least 3 mins to finish
}