简体   繁体   中英

Using @Autowired service in quartz job

I'm using spring boot and I have a service which stores/retrieves some data from DB. I want to use a quartz job which uses my service. I tried many suggestions from the internet to integrate quartz with spring boot, but it doesn't work. Can you help me pls?
Here is my code:
1) I added org.quartz-scheduler as dependency in pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>

    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
    </dependency>
</dependencies>

2) This is my application.properties configured to use mysql as DB for my service:


    server.port=8281

    spring.datasource.driverClassName=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/mydb
    spring.datasource.username=myusername
    spring.datasource.password=mypassword

3) This is my service:


    @Service
    public class ReportService {

        JdbcTemplate jdbcTemplate;

        public ReportService(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }

        public List getPendingReports() {
            String sql = "SELECT * FROM report WHERE status = '" + ReportStatus.PENDING.name() +"'";
            ReportMapper reportsMapper = new ReportMapper();
            List reports = jdbcTemplate.query(sql, reportsMapper);
            return reports;
        }
    }

4) The scheduler class:


    import org.quartz.JobBuilder;
    import org.quartz.JobDetail;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.SchedulerFactory;
    import org.quartz.SimpleScheduleBuilder;
    import org.quartz.SimpleTrigger;
    import org.quartz.TriggerBuilder;
    import org.quartz.impl.StdSchedulerFactory;

    public class ReportScheduler {

        public void scanAndUpdateReports() {
            SchedulerFactory sf = new StdSchedulerFactory();
            try {
                Scheduler scheduler = sf.getScheduler();
                scheduler.start();

                JobDetail job = JobBuilder.newJob(ReportsJob.class)
                          .withIdentity("reportsJob")
                          .build();

                SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                          .withIdentity("reportsTrigger")
                          .startNow()
                          .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                                  .withIntervalInSeconds(60).repeatForever())
                          .build();

                scheduler.scheduleJob(job, trigger);
            } catch (SchedulerException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

5) And my job class where I want to autowire the ReportService, but it is null:


    public class ReportsJob implements Job {

        @Autowired
        ReportService reportService; //this doesn't work (it is null) 

        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            List reports = reportService.getPendingReports();
            System.out.println("Reports:\n"+reports);
        }
    }

6) Right now I'm calling the scheduler from main method:


    @SpringBootApplication
    public class ReportAppBeApplication {

        public static void main(String[] args) {
            SpringApplication.run(ReportAppBeApplication.class, args);

            ReportScheduler rs = new ReportScheduler();
            rs.scanAndUpdateReports();
        }

    }

It is null because @Autowired only works in a @Component , @Repository or @Service annotated class. Annotate your ReportJob class and make sure the map where you store the class is included in the component scan on startup. You can include the map for scanning with:

@ComponentScan(basePackages={"folderOfReportJob"})

This annotation should be added to an @Configuration or @SpringBootApplication annotated class.

EDIT:

If you don't want to make your job a component you can also get the bean manually through the ApplicationContext class. It goes like this:

private static ApplicationContext ac;
ReportService reportService = ac.getBean(ReportService.class);

I happened to find you a chinese blog writtig about a functioning implementation. may i say you're trying to implement something like spring-boot-starter-quartz .

To Summary, you should defined your own QuartzJobFactory as to modify createJobInstance method adding autowireBean to newly create JobDetail instance.

something like,

JobDetail job = JobBuilder.newJob(ReportsJob.class)
                          .withIdentity("reportsJob")
                          .build();
// Pseudocode here
autowiredBean(job);

working example,

    @Component("quartzJobFactory")
    public static class QuartzJobFactory extends AdaptableJobFactory {

        private final AutowireCapableBeanFactory capableBeanFactory;

        public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) {
            this.capableBeanFactory = capableBeanFactory;
        }

        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            //调用父类的方法,把Job注入到spring中
            Object jobInstance = super.createJobInstance(bundle);
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }
@Configuration
class Configuration {
    @Bean(name = "scheduler")
    public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setJobFactory(quartzJobFactory);
        factoryBean.afterPropertiesSet();
        Scheduler scheduler = factoryBean.getScheduler();
        scheduler.start();
        return scheduler;
    }
}

full post here: https://www.cnblogs.com/supiaopiao/p/14953978.html

In my case, I added annotation @DisallowConcurrentExecution. After that I able to access autowired services, smoothly.

import org.quartz.DisallowConcurrentExecution;

@DisallowConcurrentExecution
public class InvoiceJob implements Job {
   @Autowired
   private RuntimeConfigurationService runtimeConfigurationService;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM