Which one executes first with default configs, scheduled or post construct?
#Component
public class x {
#Autowired
private Y y;
#PostConstruct
public void init() {
y.doInit();
}
#Scheduled(fixedDelay = 10000)
public void check() {
y.doCheck();
}
}
Related
I'm making use of TaskExecutor to run some tasks in background threads. Following is my configuration:-
#Configuration
public class TaskExecutorConfig
{
#Bean
public TaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(4);
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(false);
threadPoolTaskExecutor.setThreadNamePrefix("TaskExecutor");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
There can be many tasks to run in background. I have declared a ThreadComponent implementing Runnable class. In this ThreadComponent, I have declared methods which get executed based on condition. Following is the component and what it somewhat looks like:-
public class ThreadComponent implements Runnable
{
....
....
....
....
public ThreadComponent()
{
//................//
//Initialization code for variables and some autowired components//
//................//
}
#Override
public void run()
{
if (condition)
doThis();
else if (condition)
doThat();
else if (condition)
doThen();
else if (condition)
doWhere();
else if (condition)
doHow();
else if (condition)
doWhen();
else if (condition)
doNothing();
}
public void doThis() {
......
.....,
}
public void doThat() {
......
.....,
}
public void doThen() {
......
.....,
}
public void doWhere() {
......
.....,
}
public void doHow() {
......
.....,
}
public void doWhen() {
......
.....,
}
public void doNothing() {
......
.....,
}
}
I call this component using TaskExecutor for tasks. In ThreadComponent, I have also used #Autowired components which I initialize in the constructor using ApplicationContext. But this is making my processing slow. I need help so how can I optimize my code to make it better.
Update
Please find the class variables and constructor of the component:
private DTO dto1;
private DTO2 dto2;
private Map<String, Object> dtoVal;
private String schemaName;
private int checkCondition;
private AppUser user;
private Details oldApprovalDetails;
private Details newApprovalDetails;
private ThreadDTO threadDTO;
#Autowired
OcrService ocrService;
#Autowired
NotificationService notificationService;
#Autowired
CreateRecordsService createRecordService;
#Autowired
DtoService dtoService;
#Autowired
DtoCalculations dtoCalculations;
#Autowired
MailService mailService;
#Autowired
TransDtoRepository transDtoRepository;
#Autowired
TransDtoApproverDetailsRepository approverDetails;
public ThreadComponent(ThreadDTO threadDTO,
List<Details> approvalDetails, ApplicationContext ctx)
{
this.dto1 = threadDTO.getDto1();
this.dto2 = threadDTO.getDto2();
this.dtoVal = threadDTO.getDto1().getConfig();
this.schemaName = threadDTO.getDto1().getSchemaName();
this.checkCondition = threadDTO.getCheckCondition();
this.user = threadDTO.getUser();
this.oldApprovalDetails = approvalDetails.get(0);
this.newApprovalDetails = approvalDetails.get(1);
this.threadDTO = threadDTO;
this.ocrService = ctx.getBean(OcrService.class);
this.notificationService = ctx.getBean(NotificationService.class);
this.createRecordService = ctx.getBean(CreateRecordsService.class);
this.dtoService = ctx.getBean(DtoService.class);
this.dtoCalculations = ctx.getBean(DtoCalculations.class);
this.mailService = ctx.getBean(MailService.class);
this.transDtoRepository = ctx.getBean(TransDtoRepository.class);
this.approverDetails = ctx.getBean(TransDtoApproverDetailsRepository.class);
}
Constructor contains all the variables declared in class scope.
I implemented a work around and that helped me solve my issue. I implemented spring boot #Async annotation service and added all my backend code that doesn't require to interrupt the user in that service. This service then makes call to the TaskExecutor for multiple threads. In short, I implemented both #Async and TaskExecutor framework for one complex functionality and that made my code faster (now it takes 300 milliseconds to 500 milliseconds to give me response).
Hope this helps anyone who is in need.
I need to use autowired in more than one class with ScheduledExecutorService, what I have tried is shown in this code. logging size of User list in below example always shows 0, even after user added to arraylist. How to properly use Autowired and ScheduledExecutorService in spring boot?
#Component
public class AnotherClass {
List<User> users = new ArrayList();
public void addUser(User user){
users.add(user);
}
public void logUsers(){
logger.info("User size " + users.size()); <================= Always logs 0, when called from executor
}
}
#RestController
public class SecondClass {
#Autowired
private AnotherClass anotherClass;
#GetMapping(value="/user/test")
public void logUsers(){
anotherClass.addUser(new User());
}
}
Application Class
#Component
#SpringBootApplication
public class SpringBootDemoApplication {
private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
#Autowired
private AnotherClass anotherClass;
#PostConstruct
public void init() {
logger();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
public void logger(){
exec.scheduleAtFixedRate(new Runnable(){
#Override
public void run(){
try {
anotherClass.logUsers();
}catch (Exception e){
}
}
}, 2000, 1000, TimeUnit.MILLISECONDS);
}
}
The code works if you use the Spring #Autowired and not the #AutoWired Annotation.
I am attempting to create List of beans of the same type in a class annotated by #Configuration. What I need is to execute #Scheduled function declared in those beans.
#SpringBootApplication
#EnableScheduling
public class DemoApplication
{
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
}
#Configuration
public class Config
{
#Bean
public List<Monitoring> mon()
{
List<Monitoring> list = new ArrayList<>();
for (int x = 0; x < 5; ++x)
{
list.add(new First());
}
return list;
}
}
public class First implements Monitoring
{
private static final Logger logger = LoggerFactory.getLogger(First.class);
#Override
public void doSth()
{
logger.info("first monitoring bean");
}
#Scheduled(fixedRate = 50)
private void init()
{
logger.info("scheduled task");
}
}
What am I expecting from these code snippets is for my 5 Beans of Monitoring to print "scheduled task" every 50ms, but I never see this output.
You are only creating a list as a bean with unmanaged Monitoring instances. That won't work.
You need to properly create the Monitoring beans (so that Spring can do its magic):
#Configuration
public class Config {
#Bean
public Monitoring first() {
return new First();
}
#Bean
public Monitoring second() {
return new First();
}
}
I have a monitoring app wherein I am running a fixedRate task. This is pulling in a config parameter configured with Consul. I want to pull in updated configuration, so I added #RefreshScope. But as soon as I update the config value on Consul, the fixedRate task stops running.
#Service
#RefreshScope
public class MonitorService {
#Autowired
private AppConfig appConfig;
#PostConstruct
public void postConstRun() {
System.out.println(appConfig.getMonitorConfig());
}
#Scheduled(fixedRate = 1000)
public void scheduledMonitorScan() {
System.out.println("MonitorConfig:" + appConfig.getMonitorConfig());
}
}
AppConfig class just has a single String parameter:
#Configuration
#Getter
#Setter
public class AppConfig {
#Value("${monitor-config:default value}")
private String monitorConfig;
}
As soon as I update the value in consul, the scheduled task just stops running (display in sheduledMonitorScan method) stop showing up.
I'm successfully get & override the values from consul config server using RefreshScopeRefreshedEvent
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
#RefreshScope
public class AlertSchedulerCron implements ApplicationListener<RefreshScopeRefreshedEvent> {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
#Value("${pollingtime}")
private String pollingtime;
/*
* #Value("${interval}") private String interval;
*/
#Scheduled(cron = "${pollingtime}")
//#Scheduled(fixedRateString = "${interval}" )
public void task() {
System.out.println(pollingtime);
System.out.println("Scheduler (cron expression) task with duration : " + sdf.format(new Date()));
}
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
// TODO Auto-generated method stub
}
}
Here's how we've solved this issue.
/**
* Listener of Spring's lifecycle to revive Scheduler beans, when spring's
* scope is refreshed.
* <p>
* Spring is able to restart beans, when we change their properties. Such a
* beans marked with RefreshScope annotation. To make it work, spring creates
* <b>lazy</b> proxies and push them instead of real object. The issue with
* scope refresh is that right after refresh in order for such a lazy proxy
* to be actually instantiated again someone has to call for any method of it.
* <p>
* It creates a tricky case with Schedulers, because there is no bean, which
* directly call anything on any Scheduler. Scheduler lifecycle is to start
* few threads upon instantiation and schedule tasks. No other bean needs
* anything from them.
* <p>
* To overcome this, we had to create artificial method on Schedulers and call
* them, when there is a scope refresh event. This actually instantiates.
*/
#RequiredArgsConstructor
public class RefreshScopeListener implements ApplicationListener<RefreshScopeRefreshedEvent> {
private final List<RefreshScheduler> refreshSchedulers;
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
refreshSchedulers.forEach(RefreshScheduler::materializeAfterRefresh);
}
}
So, we've defined an interface, which does nothing in particular, but allows us to call for a refreshed job.
public interface RefreshScheduler {
/**
* Used after refresh context for scheduler bean initialization
*/
default void materializeAfterRefresh() {
}
}
And here is actual job, whose parameter from.properties can be refreshed.
public class AJob implements RefreshScheduler {
#Scheduled(cron = "${from.properties}")
public void aTask() {
// do something useful
}
}
UPDATED:
Of course AJob bean must be marked with #RefreshScope in #Configuration
#Configuration
#EnableScheduling
public class SchedulingConfiguration {
#Bean
#RefreshScope
public AJob aJob() {
return new AJob();
}
}
I have done workaround for this kind of scenario by implementing SchedulingConfigurer interface.
Here I am dynamically updating "scheduler.interval" property from external property file and scheduler is working fine even after actuator refresh as I am not using #RefreshScope anymore.
Hope this might help you in your case also.
public class MySchedulerImpl implements SchedulingConfigurer {
#Autowired
private Environment env;
#Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(this.taskExecutor());
taskRegistrar.addTriggerTask(() -> {
//put your code here that to be scheduled
}, triggerContext -> {
final Calendar nextExecutionTime = new GregorianCalendar();
final Date lastActualExecutionTime = triggerContext.lastActualExecutionTime();
if (lastActualExecutionTime == null) {
nextExecutionTime.setTime(new Date());
} else {
nextExecutionTime.setTime(lastActualExecutionTime);
nextExecutionTime.add(Calendar.MILLISECOND, env.getProperty("scheduler.interval", Integer.class));
}
return nextExecutionTime.getTime();
});
}
}
My solution consists of listening to EnvironmentChangeEvent
#Configuration
public class SchedulingSpringConfig implements ApplicationListener<EnvironmentChangeEvent>, SchedulingConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(SchedulingSpringConfig.class);
private final DemoProperties demoProperties;
public SchedulingSpringConfig(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
LOGGER.info("Configuring scheduled task with cron expression: {}", demoProperties.getCronExpression());
taskRegistrar.addTriggerTask(triggerTask());
taskRegistrar.setTaskScheduler(taskScheduler());
}
#Bean
public TriggerTask triggerTask() {
return new TriggerTask(this::work, cronTrigger());
}
private void work() {
LOGGER.info("Doing work!");
}
#Bean
#RefreshScope
public CronTrigger cronTrigger() {
return new CronTrigger(demoProperties.getCronExpression());
}
#Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
#Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
if (event.getKeys().contains("demo.config.cronExpression")) {
ScheduledTasksRefresher scheduledTasksRefresher = new ScheduledTasksRefresher(triggerTask());
scheduledTasksRefresher.afterPropertiesSet();
}
}
}
Then I use the ContextLifecycleScheduledTaskRegistrar to recreate the task.
public class ScheduledTasksRefresher extends ContextLifecycleScheduledTaskRegistrar {
private final TriggerTask triggerTask;
ScheduledTasksRefresher(TriggerTask triggerTask) {
this.triggerTask = triggerTask;
}
#Override
public void afterPropertiesSet() {
super.destroy();
super.addTriggerTask(triggerTask);
super.afterSingletonsInstantiated();
}
}
Properties definition:
#ConfigurationProperties(prefix = "demo.config", ignoreUnknownFields = false)
public class DemoProperties {
private String cronExpression;
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
}
Main definition:
#SpringBootApplication
#EnableConfigurationProperties(DemoProperties.class)
#EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Based on previous answers I added the following interface and used it on #RefreshScope annotated beans:
public interface RefreshScopeScheduled {
#EventListener(RefreshScopeRefreshedEvent.class)
default void onApplicationEvent() { /*do nothing*/ }
}
I do some initializations in a CommandLineRunner's run function and I want my Scheduled task to begin schedule after the initialization, how can I achive this?
For example, I have the CommandLineRunner:
#Component
public class MyCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
// Initializations
// ...
}
}
And the task scheduler:
public class SchedClass {
#Scheduled(fixedRate = ONE_SECOND)
public void sched() {
}
}
What can I do to make sched() runs after run() runs?
class MySchedulingConfigurer implements SchedulingConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
private IntervalTask task;
public MySchedulingConfigurer(IntervalTask task) {
this.task = task;
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
}
public void resume() {
this.taskRegistrar.scheduleFixedRateTask(task);
}
}
#Configuration
class SchedulerConfiguration {
#Autowired
private MySchedulingConfigurer schedulingConfigurer;
#Bean
public MySchedulingConfigurer mySchedulingConfigurer() {
IntervalTask task = new IntervalTask(new MyTask(), 5000L);
return new MySchedulingConfigurer(task);
}
public static class MyTask implements Runnable {
#Override
public void run() {
System.out.println("===> task executed...");
}
}
#EventListener
public void startScheduler(ApplicationReadyEvent event){
schedulingConfigurer.resume();
}
}
#Component
class CacheLoadingRunner implements CommandLineRunner {
#Autowired
private MySchedulingConfigurer schedulingConfigurer;
#Override
public void run(String... args) throws Exception {
schedulingConfigurer.resume();
}
}
1) Add #EnableScheduling
#EnableScheduling
#SpringBootApplication
public class MyCommandLineRunner implements CommandLineRunner {
...
}
2) It makes no sense to denote Spring Boot application with #Component
UPDATED
It could be achieved manually. Provide a TaskScheduler
#Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
Inject it into MyCommandLineRunner along with SchedClass and run
#Component
public class MyCommandLineRunner implements CommandLineRunner {
#Autowired
private ThreadPoolTaskScheduler taskScheduler;
#Autowired
private SchedClass schedBean;
#Override
public void run(String... args) throws Exception {
// ...
taskScheduler.scheduleWithFixedDelay(() -> schedBean.sched(), ONE_SECOND);
// ...
}
}