Is it possible to inject the TaskScheduler instance created by Spring?
I would like to schedule tasks programatically and for that, I guess I need to access the TaskScheduler but for some reason, it's not found by Spring for autowiring.
#Configuration
#EnableScheduling
public class MySpringConfig {
}
#Component
public class MyClass implements InitializingBean {
#Autowired
private TaskScheduler taskScheduler;
#Override
public void afterPropertiesSet() throws Exception {
...
}
}
Any idea?
Thanks!
#Configuration
#EnableScheduling
public class MySpringConfig {
#Bean
public TaskScheduler taskScheduler() {
//org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
return new ThreadPoolTaskScheduler();
}
}
You can choose which ever implementation you like. ThreadPoolTaskScheduler is the simpler one as mentioned in this link.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations
Related
I have read many questions and answers about using #Scheduled with #Async in Spring but no one resolves my problem and my asynchronous method still runs single-threaded. So here is my Configuration class:
#EnableScheduling
#EnableAsync
#Configuration
#RequiredArgsConstructor
public class SchedulerConfiguration {
private final ThreadPoolProperties threadPoolProperties;
#Bean
public TaskExecutor commonTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); // 10
taskExecutor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); // 20
taskExecutor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); // 5
taskExecutor.setThreadNamePrefix("TEST");
taskExecutor.initialize();
return taskExecutor;
}
}
Then we have a bean with the #Scheduled method:
#Component
#RequiredArgsConstructor
public class ScheduledTask {
private final ConfirmReservationTask confirmReservationTask;
#Scheduled(cron = "${booking.scheduler.confirmReservationsCron}")
public void process() {
confirmReservationTask.confirmReservations();
}
}
And finally, another bean (to avoid self-injection and proxy problems with asynchronous processing) with #Async method:
#Log4j2
#Component
#RequiredArgsConstructor
public class ConfirmReservationTask {
private final ReservationService reservationService;
#Async("commonTaskExecutor")
public void confirmReservations() {
...
}
}
unfortunately, this solution works in only one thread, however, the method uses the correct ThreadExecutor. How to solve it?
I am trying to use Spring AOP with Quartz job spring beans. The jobs are autowired into spring container using the following approach:
a. Create AutowiringSpringBeanJobFactory extending SpriongBeanJobFactory implementing ApplicationContextAware
b. override createJobInstance and use the AutowiringCapableBeanFactory to autowire the job beans.
See solution
Spring boot Application Configuration:
#Configuration
public class MyApplicationConfig {
#Bean
#Primary
public SchedulerFactoryBean schedulerFactoryBean() throws Exception {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(ApplicationContextProvider.getContext());
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setDataSource(datasource());
factoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
factoryBean.setFactory(jobFactory);
}
}
Autowiring of Jobs done as:
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext() {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
Using this setup, I have created an Aspect:
#Aspect
#Configuration
public class MyAspect {
#Around("execution(public void com.myapp.jobs.*.execute(..))")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
// do something with pjp
}
}
This is my sample job:
#Service
public class Myjob implements Job {
#Autowired
IAuditService auditService;
public void execute(JobExecutionContext jctx) throws JobExecutionException {
//do something
}
}
However, when the above job executes, spring aop does not invoke MyAspect.process().
I am already autowring my job beans and I am able to autowire other beans into my jobs but only the aspect does not get invoked. Whats missing here?
First of all you can use #Component instead of #Configuration as annotation of your aspect, thus it will look like this:
#Aspect
#Component
public class MyAspect {
#Around("execution(public void com.myapp.jobs.*.execute(..))")
public Object process(ProceedingJoinPoint pjp) throws Throwable {
// do something with pjp
}
}
Secondly make sure that the pointcut expression is correct, whether you are referring to the right package/classe/method.
Lastly, if you are not using spring boot you should add #EnableAspectJAutoProxy to your configuration class otherwise no need for that with spring boot as it's auto configured !
To enable AOP for my jobs, i had to enhance "AutowiringSpringBeanJobFactory " to manually weave aspects in for the jobs as follows:
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
private Object jobAspect;
public void setJobAspect(Object aspect) {
this.jobAspect = aspect;
}
#Override
public void setApplicationContext() {
beanFactory = context.getAutowireCapableBeanFactory();
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
AspectJProxyFactory pFactory = new AspectJProxyFactory(job);
pFactory.addAspect(this.jobAspect);
return pFactory.getProxy();
}
}
However, there are additional issues with this if your job uses annotations on its method(s). That's for another time though.
Does placement of beans make a different when loading them into a scoped context? Is this a bug or a timing of instantiation issue?
If I include the #StepScope and #Bean directly in the BatchConfiguration class, everything works seamlessly with StepScope. However, if I define another class, say "BatchProcessProcessor" as included below, and mark a method within that other class as a Bean with StepScope, it does not resolve properly. The actual symptom in spring batch is StepScope not triggering and the beans being loaded as Singletons.
Something about providing the #Bean and #StepScope from another class that is loaded via constructor injection in the BatchConfiguration does not resolve properly.
Format described above, included below:
Main batch configuration class
#Slf4j
#Configuration
#EnableAutoConfiguration
#EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {
private BatchProcessProcessor processor;
#Override
public void setDataSource(DataSource dataSource) {
// override to do not set datasource even if a datasource exist.
// initialize will use a Map based JobRepository (instead of database)
}
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
public StepBuilderFactory stepBuilderFactory;
#Autwired
public BatchConfiguration(BatchProcessProcessor processor){
this.processor = processor;
}
#Bean
#StepScope
public ListItemReader<String> reader() {
List<String> stringList = new ArrayList<>();
stringList.add("test");
stringList.add("another test");
log.info("LOGGING A BUNCH OF STUFF THIS IS UNIQUE" + String.valueOf(System.currentTimeMillis()));
return new ListItemReader<>(stringList);
}
#Bean
#StepScope
public CustomWriter writer() {
return new CustomWriter();
}
#Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
return jobBuilderFactory.get("importUserJob")
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1)
.end()
.build();
}
#Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.<String, String> chunk(10)
.reader(reader())
.processor(processor.processor())
.writer(writer()).build();
}
}
Processor container class
#Component
public class BatchProcessProcessor {
private MyService service;
#Autowired
BatchProcessProcessor(MyService service){
this.service= service;
}
/**
* Generate processor utilized for processing
* #return StringProcessor for testing
*/
#Bean
#StepScope
public DeploymentProcesser processor() {
return new DeploymentProcessor(service);
}
}
Actual Processor
#Slf4j
#Component
public class DeploymentProcesser implements ItemProcessor<Deployment, Model> {
private MyService service;
#Autowired
public DeploymentProcesser(MyService service){
this.service= service;
}
#Override
public Model process(final Deployment deployment) {
log.info(String.format("Processing %s details", deployment.getId()));
Model model = new Model();
model.setId(deployment.getId());
return model;
}
}
As far as I understand, when the BatchConfiguration loads it should inject the BatchProcessProcessor and load the bean with stepscope, but that doesn't seem to work.
As I said before, just copy-pasting the #Bean/#StepScope directly into the BatchConfiguration and returning the same DeploymentProcessor works perfectly and StepScope resolves.
Is this a lifecycle issue?
It does not make sense to declare a bean in a class annotated with #Component:
#Component
public class BatchProcessProcessor {
private MyService service;
#Autowired // This is correct, you can autowire collaborators
public DeploymentProcesser(MyService service){
this.service= service;
}
#Bean // THIS IS NOT CORRECT
#StepScope
public DeploymentProcesser processor() {
return new DeploymentProcessor(service);
}
}
You should rather do it in a configuration class annotated with #Configuration. That's why it works when you do it in BatchConfiguration.
when i am using #autowire to inject my dependencies in Configuration
class its giving me as null please refer the code below .
#Configuration
public class DataSourceConfig {
#Autowired
AppService appService;
#Bean
public BeanDefinitionRegistryPostProcessor beanPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
createBeans(beanRegistry);
}
};
}
private void createBeans(BeanDefinitionRegistry beanRegistry,DataSourceConfigService ds) {
appService.getDbDetails();
appService is null here if i will call it using this way
BeanDefinitionRegistryPostProcessor beanPostProcessor(AppService
appService) then in AppServiceImpl class AppDao dependency will be null
}
}
//// Service
#Service
public class AppServiceImpl implements AppService{
#Autowired
AppDao ds;
#Override
public List<A> getDatabaseConfiguration() {
return ds.getDbDetails(); // here ds is null
}
}
//dao
#Repository
public class AppDaoImpl implements AppDao {
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
#Override
public List<A> getDbDetails() {
return nameParamJdbcTemplate.query(SELECT_QUERY, new DataSourceMapper()); // nameParamJdbcTemplate is null
}
// datasource config
#Configuration
public class DataSourceBuilderConfig {
#Bean(name = "dbSource")
#ConfigurationProperties(prefix = "datasource")
#Primary
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nameParamJdbcTemplate")
#DependsOn("dbSource")
#Autowired
public NamedParameterJdbcTemplate jdbcTemplate1(#Qualifier("dbSource") DataSource dbSource) {
return new NamedParameterJdbcTemplate(dbSource);
}
}
What i want is when ever my beanPostProcessor()
is executed i want all my dependent beans should be instantiated ie
#Autowired
AppService appService;
#Autowired
AppDao ds;
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
I am new to spring so any help or working examples would be great. Thanks
It is null because this #Configuration class also defines a BeanDefinitionRegistryPostProcessor that forces the context to create that bean very early on.
Because you are using field injection, the context has to resolve AppService bean but it can't yet because the post-processor have to be applied first.
Your configuration looks very complex so you may want to simplify it a bit:
Separate low-level infrastructure configuration from main configuration
Always define such post processor as public static method so that the context can invoke the #Bean method without having to construct the class first.
I have created a custom API jar library where I'd like to provide some commonly used services.
But I'd like to use and autowire some of these services optionally in my implementation projects. They should not get autowired automatically.
How could I tell Spring explicit to include the following StatsLogger?
API jar:
package my.spring.config
//#Component
public class MyStatsLogger {
#Autowired
private MyService someOtherServiceForLogging;
#Scheduled(fixedDelay = 60000)
public void log() {
//logging
}
}
IMPL project:
#Configuration
#EnableScheduling
public class AppConfig {
}
Simply add the service to your context:
#Configuration
#EnableScheduling
public class AppConfig {
#Bean
public MyStatsLogger myStatsLogger() {
return new MyStatsLogger();
}
}
Since MyStatsLogger has a default constructor, all you need to is the following:
#Configuration
#EnableScheduling
public class AppConfig {
#Bean
public MyStatsLogger myStatsLogger() {
return new MyStatsLogger();
}
}
The MyService dependency in MyStatsLogger will automatically be wired by Spring if of course there is a bean of type MyService declared.