关于定时任务的一些问题处理

定时任务

schedule定时任务

spring提供的定时任务器,

@Component
public class Schedule {

    @Scheduled(cron = "0/2 * * * * ?")
    public void test(){
        System.out.println("springboot定时任务:"+ new Date());
    }
}

@SpringBootApplication
@EnableJms
@EnableScheduling
public class DiancanApplication {


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

spring schedule是基于java提供的ScheduledExecutorService中的schedule(Runnable command, long delay, TimeUnit unit)来实现的

    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.scheduleAtFixedRate(()->{
            System.out.println("----------定时任务AAAAAAAAAAAAAAA-----------" + new Date());
        },0,5, TimeUnit.SECONDS);
    }

ScheduledExecutorService实现任务调度的主要是通过其延时队列,延时队列内部是一个堆的实现,其实就是最小堆,每个进入队列的任务会通过执行时间进行排序,时间相同则先提交的先执行

执行原理:

  • 线程从延时队列中获取该执行的任务,执行完后将执行时间修改为下一次的执行时间在放回队列中

cron表达式:

我对cron表达式具有的了解呢,他的语法应该是从左到右是,他有两种表达形式,7个字段和6个字段的区别是最后的year

second min hour day month week year

但是在开发过程中选择去网上找在线的cron转换来生成我需要用到的cron表达式

quartz

主要组件:

  • Job任务类
    • 实现Job接口,重写execute方法实现定时任务逻辑
  • 触发器
    • 绑定任务
    • 编写cron表达式
  • 调度器
    • 绑定任务和触发器
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

编写Job

public class MyJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("quartz定时任务:"+new Date());
    }
}

编写触发器和调度器,并且绑定任务

@Configuration
public class QuartzConfig {
    
    @Bean(value = "myJobDetail")
    public JobDetailFactoryBean myJobDetail() {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(MyJob.class);
        factoryBean.setGroup("group1");
        factoryBean.setName("MyJob");
        return factoryBean;
    }

    @Bean
    public CronTriggerFactoryBean myJobTriggrt(@Qualifier("myJobDetail") JobDetail jobDetail){
        CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
        factoryBean.setJobDetail(jobDetail);
        factoryBean.setStartDelay(1000L);
        factoryBean.setName("trigger1");
        factoryBean.setGroup("group1");
        factoryBean.setCronExpression("0/2 * * * * ? ");
        return factoryBean;
    }

    //调度工厂
    @Bean
    public SchedulerFactoryBean schedulerFactory1Bean(@Qualifier("myJobTriggrt") Trigger simpleJobTrigger)
            throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setTriggers(simpleJobTrigger);
        return factory;
    }
}

在这里插入图片描述

但是

这里有个问题需要注意:上文提到的job类的实现主要是打印的逻辑,即没有用到其他业务类去完成一些业务,实际开发中的时候,我们往往需要用到定时任务去完成一些业务,那就必然会引出一个问题,如果在job中注入业务对象会有问题吗

@Component
public class MyJob implements Job {

    @Autowired
    private TestService testService;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        testService.A();
    }
}

配置类不变直接运行,结果如下所示:

在这里插入图片描述

报的是空指针异常,证明我们的testService没有成功注入进来,那为什么没有成功注入呢?

我们再看配置类

    @Bean(value = "myJobDetail")
    public JobDetailFactoryBean myJobDetail() {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(MyJob.class);
        factoryBean.setGroup("group1");
        factoryBean.setName("MyJob");
        return factoryBean;
    }

可以知道Job的实例化是交由JobDetailFactoryBean去做的,那么他底层其实是通过AdaptableJobFactory的下面这个方法完成job的实例化

protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
   Class<?> jobClass = bundle.getJobDetail().getJobClass();
   return ReflectionUtils.accessibleConstructor(jobClass).newInstance();
}

可以看到,他底层实例化的方式就是通过getclass然后通过反射的newInstance方法完成实例化

所以,Job对象的实例化是不依靠spring IOC容器取做的,即spring IOC容器中没有这个job对象,所以,spring IOC容器管理的业务类肯定也无法在Job中完成注入,因为spring的要求是注入的对象和被注入的对象都要在IOC容器中,这样才能完成对象的注入

所以,问题的解决方案也出来,就是就是将AdaptableJobFactory创建的Job对象手动的添加到spring ioc容器中

@Configuration
public class MyAdaptableJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory autowireCapableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object job =  super.createJobInstance(bundle);
        //将创建出来的job手动添加到IOC容器中
        autowireCapableBeanFactory.autowireBean(job);
        return job;
    }
}

这样还不行,使用的还是默认的JobDetailFactoryBean。我们需要修改下使用我们新写的MyAdaptableJobFactory,在配置类的调度器中设置下即可

    @Autowired
    private MyAdaptableJobFactory myAdaptableJobFactory;
    
    //调度工厂
    @Bean
    public SchedulerFactoryBean schedulerFactory1Bean(@Qualifier("myJobTriggrt") Trigger simpleJobTrigger)
            throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //使用我们的myAdaptableJobFactory
        factory.setJobFactory(myAdaptableJobFactory);
        factory.setTriggers(simpleJobTrigger);
        return factory;
    }

就此,任务成功执行:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41922289/article/details/105707072