javaspring-bootquartz-scheduler

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();
        }

    }


Solution

  • It is null because @Autowired only works in a @Component, @Repository or @Service annotated class. Annotate your ReportJobclass 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);