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

devtools/2025/1/8 2:14:03/

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

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

一、报错信息

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

二、代码

任务类源代码

java">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 {@Autowiredprivate CustomerSerice customerSerice; // 这里customerSerice为null/*** 触发任务* 查询* 添加* @param context* @throws JobExecutionException*/@Overridepublic 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方法执行完了========================");}
}

配置类原代码

java">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*/@Beanpublic JobDetailFactoryBean jobDetailFactoryBean() {JobDetailFactoryBean factory = new JobDetailFactoryBean();factory.setJobClass(ScheduledTasks.class); // 指定作业类factory.setDurability(true);return factory;}/*** 创建触发器* @param  jobDetail* @return*/@Beanpublic CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetail) {CronTriggerFactoryBean factory = new CronTriggerFactoryBean();factory.setJobDetail(jobDetail);factory.setCronExpression("0 0 0 * * ?"); // 每天凌晨0点触发return factory;}/*** 注册调度器* @param trigger* @return*/@Beanpublic 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框架去实例化的,自然就无法完成注解
java">JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(ScheduledTasks.class); // 把ScheduledTasks.class传入了JobDetailFactoryBean
factory.setDurability(true);

四、解决的思路1

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

1、任务类修改

java">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、配置类修改

java">/*** 创建触发器* @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中去获得 ,这样就实现了通用性。
java">@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);}
java">public class SumJob implements Job {@Overridepublic 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.ppmy.cn/devtools/148272.html

相关文章

数据挖掘——认识数据

数据挖掘——认识数据 数据对象和属性数据对象属性和属性值 数据统计与可视化数据统计汇总离散度度量 数据的相似性和相异性度量数值属性的邻近性度量 数据对象和属性 数据对象 数据集由数据对象组成 一个数据对象代表一个实体 例子 销售数据库:客户,…

ROS2+OpenCV综合应用--10. AprilTag标签码追踪

1. 简介 apriltag标签码追踪是在apriltag标签码识别的基础上,增加了小车摄像头云台运动的功能,摄像头会保持标签码在视觉中间而运动,根据这一特性,从而实现标签码追踪功能。 2. 启动 2.1 程序启动前的准备 本次apriltag标签码使…

信创云之天翼云:引领信创云时代的先锋力量

数据显示,2024年中国云服务市场规模已达到4242.5亿元,显示出各行业对信息技术软硬件的依赖程度不断加深。在国家政策的持续支持下,数字化转型为云服务行业带来了前所未有的发展机遇。预计到2025年,中国云服务市场规模将突破4795.4…

《Opencv》基础操作详解(4)

目录 22、图像形态学操作 (1)、顶帽(原图-开运算) 公式: 应用场景: 代码示例: (2)、黑帽(闭运算-原图) 公式: 应用场景&#x…

【LLM】概念解析 - Tensorflow/Transformer/PyTorch

背景 本文将从算法原理、适用范围、强项、知名大模型的应用、python 调用几个方面,对深度学习框架 TensorFlow、PyTorch 和基于深度学习的模型 Transformer 进行比较。主要作用是基础概念扫盲。 一、 算法原理对比 Transformer Transformer 是一种基于深度学习的…

wordpress主题开发之function.php的10大作用

文章目录 1. 添加自定义功能2. 启用或禁用功能支持3. 添加自定义样式和脚本4. 过滤和修改默认行为5. 添加自定义短代码6. 注册自定义管理功能7. 添加 REST API 扩展8. 优化性能9. 集成第三方服务10. 创建自定义功能模块注意事项 在 WordPress 中, functions.php 文…

golang 编程规范 - 项目目录结构

原文:https://makeoptim.com/golang/standards/project-layout 目录结构 Go 目录 cmdinternalpkgvendor 服务端应用程序目录 api Web 应用程序目录 web 通用应用程序目录 buildconfigsdeploymentsinitscriptstest 其他目录 assetsdocsexamplesgithooksthird_par…

基于单片机的仓库环境无线监测系统(论文+源码)

1 系统方案设计 根据系统功能的设计要求,展开仓库环境无线监测系统设计。系统以STM32单片机作为主控核心,通过DHT11温湿度传感器、CO2传感器、光敏电阻实现仓储环境数据的温度、湿度、CO2浓度、光照强度监测,监测数据不仅能够通过OLED液晶实时…