servletcontext数据为null_如何解决quartz调度时候,job中的service为null的问题?

1074a05073527299d4bde953413bea87.png

在需要用到调度任务的时候,发现job中的通过依赖注入的service对象为null.如下:

@Component
public class ExpiredOrderJob implements Job {
    
    @Autowired
    private EmployeeService employeeService; //这里的service对象为null
    
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
      ……
    }
}

问题的原因:

出现这个问题的根本原因,还得从spring的IOC和AOP的基本原理开始讲起。

在IOC中,是spring先把需要放置到IOC容器的对象放入,然后在通过IOC机制去请求获得该对象的时候,然后调用出来IOC容器中准备好的对象。具体在springboot中的表现,如果你在一个类中增加了Component的注解,或者在一个Configure中增加了Bean的注解,IOC就会明白你想要把该对象放入到容器,然后在需要容器帮你实例化的地方加入Autoware,容器就会把该对象给注入。需要注意的地方是,容器只能对容器注入的对象内部的属性注入,如果你通过自己的代码new了一个对象,这对象里面的Autoware的属性是不起作用的。很好理解,因为你的对象不在容器的管理范围,容器就无法去注入。

而在quartz的job对象,是通过直接传入job类的class,由quartz框架去实例化的,而非通过spring框架去实例化的,自然就无法完成注解。

 JobDetail jobDetail = JobBuilder.newJob(ExpiredOrderJob.class) //把job.class传入了jobBuilder
                .withIdentity("expireOrder","group1")
                .build();

解决的思路:

在job中通过Autoware注解去实现,是不太可能了。而JobDetail 可以通过jobDataMap的属性来传递对象,我们可以在需要spring注入的地方,把我们要注入的对象放到jobDataMap中去,然后在job中取出来使用,来绕道完成注解。

版本1:

//在调用调度器的地方去实现注入
@Autowired
private EmployeeService employeeService;
private void scheduleSumJob(Scheduler scheduler) throws SchedulerException
    {
        JobDetail jobDetail = JobBuilder.newJob(SumJob.class)
                .withIdentity("sumJob","group1")
                .build();
        jobDetail.getJobDataMap().put("service",employeeService);

        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1","group1")
                .withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail,cronTrigger);
    }
//在job中去实现调用
public class SumJob implements Job {


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException
    {
        EmployeeService employeeService = (EmployeeService)jobExecutionContext.getJobDetail()
                .getJobDataMap().get("service");
        employeeService.freshAreaEmployeeNum();
    }
}

经过测试,我们已经能解决了在job中无法注入的问题。但是也有一些缺点,比如我们要再数据库中保存很多的任务,而每个任务所调用service都不一样。我们就无法在我们的使用调度器的地方去实现找到需要注入的对象,然后放到jobDataMap中去。我们得一个更加通用的办法:比如我们把spring的容器的context注入,然后job中需要什么注入对象,就直接从context中去获得 ,这样就实现了通用性:

版本2:

@Autowired
private ServletContext servletContext;
private void scheduleSumJob(Scheduler scheduler) throws SchedulerException
    {
        JobDetail jobDetail = JobBuilder.newJob(SumJob.class)
                .withIdentity("sumJob","group1")
                .build();
        jobDetail.getJobDataMap().put("context",servletContext);

        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1","group1")
                .withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail,cronTrigger);
    }

job中对应的变化

public class SumJob implements Job {


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException
    {
        ServletContext context = (ServletContext)jobExecutionContext.getJobDetail()
                .getJobDataMap().get("context");
        WebApplicationContext cxt = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        EmployeeService employeeService = cxt.getBean(EmployeeService.class);
        employeeService.freshAreaEmployeeNum();
    }
}

这样做的好处就是把service的注入的获得延迟了,可以在job中更加灵活的调用所需要的对象,而无需在启用调度器的地方去思考,更加通用,降低了耦合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值