当前位置: 首页 > article >正文

解决Spring boot集成quartz时service注入失败为null的问题

解决Spring boot集成quartz时service注入失败为null的问题

  • 一、报错信息
  • 二、代码
    • 任务类源代码
    • 配置类原代码
  • 三、注入失败原因
  • 四、解决的思路1
    • 1、任务类修改
    • 2、配置类修改
  • 五、 解决的思路2

一、报错信息

java.lang.NullPointerException: null
at farbun.server.scheduledTask.ScheduledTasks.execute(ScheduledTasks.java:41) ~[classes/:na]

二、代码

任务类源代码

package farbun.server.scheduledTask;

import farbun.server.service.CustomerSerice;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @Author: 小新
 * @Date: 2024/12/15 10:18
 */
@Slf4j
@Component
public class ScheduledTasks implements Job {

    @Autowired
    private CustomerSerice customerSerice; // 这里customerSerice为null

    /**
     * 触发任务
     * 查询
     * 添加
     * @param context
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 获取当前日期时间
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 计算前一天的日期时间
        LocalDateTime previousDateTime = currentDateTime.minusDays(1);
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 格式化前一天的日期时间为字符串
        String previousDateTimeStr = previousDateTime.format(formatter);
        log.info(previousDateTimeStr);
        
        // 调用方法
        log.info("customerSearcher方法开始执行了。。。。");
        customerSerice.customerSearcher(previousDateTimeStr);
        log.info("customerSearcher方法执行完了===================");
        log.info("selectAndAdd方法开始执行了。。。。");
        customerSerice.selectAndAdd();
        log.info("selectAndAdd方法执行完了========================");
    }
}

配置类原代码

package farbun.server.config;

import farbun.server.scheduledTask.ScheduledTasks;
import farbun.server.service.CustomerSerice;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * @Author: 小新
 * @Date: 2023/8/15 10:28
 */
@Configuration
public class QuartzConfig {
    /**
     * 创建定时任务
     * @return
     */
    @Bean
    public JobDetailFactoryBean jobDetailFactoryBean() {
        JobDetailFactoryBean factory = new JobDetailFactoryBean();
        factory.setJobClass(ScheduledTasks.class); // 指定作业类
        factory.setDurability(true);
        return factory;
    }

    /**
     * 创建触发器
     * @param  jobDetail
     * @return
     */
    @Bean
    public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetail) {
        CronTriggerFactoryBean factory = new CronTriggerFactoryBean();
        factory.setJobDetail(jobDetail);
        factory.setCronExpression("0 0 0 * * ?"); // 每天凌晨0点触发
        return factory;
    }

    /**
     * 注册调度器
     * @param trigger
     * @return
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(Trigger trigger) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setTriggers(trigger);
        return factory;
    }
}

三、注入失败原因

  • 出现这个问题的根本原因,还得从spring的IOC和AOP的基本原理开始讲起
  • 在IOC中,是spring先把需要放置到IOC容器的对象放入,然后在通过IOC机制去请求获得该对象的时候,然后调用出来IOC容器中准备好的对象。
  • 具体在springboot中的表现,如果你在一个类中增加了Component的注解,或者在一个Configure中增加了Bean的注解,IOC就会明白你想要把该对象放入到容器,然后在需要容器帮你实例化的地方加入Autoware,容器就会把该对象给注入。
  • 需要注意的地方是,容器只能对容器注入的对象内部的属性注入,如果你通过自己的代码new了一个对象,这对象里面的Autoware的属性是不起作用的。
  • 很好理解,因为你的对象不在容器的管理范围,容器就无法去注入。
  • 而在quartz的job对象,是通过直接传入job类的class,由quartz框架去实例化的,而非通过spring框架去实例化的,自然就无法完成注解
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(ScheduledTasks.class); // 把ScheduledTasks.class传入了JobDetailFactoryBean
factory.setDurability(true);

四、解决的思路1

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

1、任务类修改

public void execute(JobExecutionContext context) throws JobExecutionException {
    ...

    CustomerSerice customerSerice = (CustomerSerice) context.getJobDetail().getJobDataMap().get("customerSerice");

    // 调用方法
    log.info("customerSearcher方法开始执行了。。。。");
    customerSerice.customerSearcher(previousDateTimeStr);
    log.info("customerSearcher方法执行完了===================");
    log.info("selectAndAdd方法开始执行了。。。。");
    customerSerice.selectAndAdd();
    log.info("selectAndAdd方法执行完了========================");
}

2、配置类修改

/**
 * 创建触发器
 * @param  jobDetail
 * @return
 */
@Autowired
private CustomerSerice customerSerice;
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetail) {
    // 调度器
    CronTriggerFactoryBean factory = new CronTriggerFactoryBean();
    //在调用调度器的地方去实现注入
    jobDetail.getJobDataMap().put("customerSerice",customerSerice);
    //
    factory.setJobDetail(jobDetail);
    factory.setCronExpression("0 0 0 * * ?"); // 每天凌晨0点触发
    return factory;
}
  • 经过测试,我们已经能解决了在job中无法注入的问题。但是也有一些缺点,比如我们要再数据库中保存很多的任务,而每个任务所调用service都不一样。
  • 我们就无法在我们的使用调度器的地方去实现找到需要注入的对象,然后放到jobDataMap中去。

五、 解决的思路2

  • 我们把spring的容器的context注入,然后job中需要什么注入对象,就直接从context中去获得 ,这样就实现了通用性。
@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);
    }
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();
    }
}

http://www.kler.cn/a/461843.html

相关文章:

  • RabbitMQ-基本使用
  • 树莓派 Pico RP2040 教程点灯 双核编程案例
  • Alist-Sync-Web 网盘自动同步,网盘备份相互备份
  • 基于深度学习算法的AI图像视觉检测
  • 《Vue3实战教程》34:Vue3状态管理
  • 【Patroni官方文档】介绍与目录
  • 【目标跟踪】checkpoint文件到底是什么?
  • 网页单机版五子棋小游戏项目练习-初学前端可用于练习~
  • 基于W2605C语音识别合成芯片的智能语音交互闹钟方案-AI对话享受智能生活
  • MySQL DBA需要掌握的 7 个问题
  • 使用 Vue CLI 创建 Vue.js 项目的详细指南
  • 【DevOps】Jenkins部署
  • Java jni调用nnom rnn-denoise 降噪
  • WebRTC的线程事件处理
  • 五、其他核心概念
  • 基于SpringBoot在线竞拍平台系统功能实现三
  • 免费的量化交易股票API有哪些局限性?
  • 人工智能-Python上下文管理器-with
  • Windows系统下Rancher安装全攻略:开启容器管理新征程
  • Oracle Dataguard(主库为 Oracle 11g 单节点)配置详解(2):配置主数据库
  • MATLAB条件判断(if_else_end型)
  • WPS计算机二级•表格初认识
  • 【UE5 C++课程系列笔记】18——蓝图变量自动加载“DefaultEngine.ini”文件变量作为默认值
  • 本地快速推断的语言模型比较:Apple MLX、Llama.cpp与Hugging Face Candle Rust
  • EasyPlayer.js遇到播放RTMP视频时,画面显示异常是什么原因?
  • 【递归、搜索与回溯算法】二叉树中的深搜