package com.lnjgkj.water.task; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.lnjgkj.water.common.entity.ScheduleConfig; import com.lnjgkj.water.mapper.ScheduleConfigMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.support.CronTrigger; import java.util.Date; import java.util.concurrent.ScheduledFuture; /** * <p> * * </p> * * @author KU_RUI * @version 1.0 * @create 2025/4/2 14:15 * @since Java 8 */ @Slf4j @Configuration @EnableScheduling public class SchedulerConf implements SchedulingConfigurer { @Autowired private ScheduleConfigMapper scheduleConfigMapper; private ScheduledTaskRegistrar taskRegistrar; private volatile ScheduledFuture<?> scheduledFuture; // 定时刷新配置(建议间隔30秒-1分钟) @Scheduled(fixedRate = 10000) public void refreshConfig() { startDynamicTask(); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { this.taskRegistrar = taskRegistrar; startDynamicTask(); } private void startDynamicTask() { try { // 停止已有任务 if (scheduledFuture != null) { boolean isCancelled = scheduledFuture.cancel(true); if (!isCancelled) { log.warn("已有任务未能成功取消"); } } // 获取最新配置 ScheduleConfig config = null; try { config = scheduleConfigMapper.selectOne( new LambdaQueryWrapper<ScheduleConfig>() .eq(ScheduleConfig::getName, "月结定时任务") ); } catch (Exception e) { log.error("获取定时任务配置时发生异常", e); return; } if (config == null) { log.error("数据库未找到定时任务配置,任务名称: {}", "月结定时任务"); return; } if (!config.getEnabled()) { log.info("定时任务未启用,任务名称: {}", "月结定时任务"); return; } // 启动新任务 Runnable task = () -> { try { // 你的业务逻辑 System.out.println("Task executed at: " + new Date()); } catch (Exception e) { log.error("任务执行过程中发生异常", e); } }; try { scheduledFuture = taskRegistrar.getScheduler().schedule( task, new CronTrigger(config.getCron()) ); } catch (IllegalArgumentException e) { log.error("Cron表达式无效,任务名称: {}, Cron表达式: {}", "月结定时任务", config.getCron(), e); } catch (Exception e) { log.error("任务调度失败,任务名称: {}", "月结定时任务", e); } } catch (Exception e) { log.error("启动动态任务时发生未知异常", e); } } } 出现失败2025-04-02 14:30:57.073 ERROR 18672 --- [ main] com.lnjgkj.water.task.SchedulerConf : 任务调度失败,任务名称: 月结定时任务 java.lang.NullPointerException: null at java.util.Objects.requireNonNull(Objects.java:203) at com.lnjgkj.water.task.SchedulerConf.startDynamicTask(SchedulerConf.java:93) at com.lnjgkj.water.task.SchedulerConf.configureTasks(SchedulerConf.java:48)
时间: 2025-04-05 14:21:42 浏览: 44
从你的错误日志可以看出,在`startDynamicTask()`方法中出现了空指针异常(NullPointerException),导致任务调度失败。以下是分析及解决方案:
### 问题定位
1. **错误位置**
根据堆栈信息 `at com.lnjgkj.water.task.SchedulerConf.startDynamicTask(SchedulerConf.java:93)` 和代码内容可知,出错的位置可能是以下几处:
- `taskRegistrar.getScheduler().schedule(task, new CronTrigger(config.getCron()))`
这里需要检查是否 `taskRegistrar` 或者 `config.getCron()` 返回了null。
2. **可能原因**
- `taskRegistrar` 变量为空:在构造函数中没有正确初始化。
- 数据库查询返回的对象 `ScheduleConfig` 的字段值有误(例如 `getCron()` 返回 null)。
- 配置文件或依赖注入出现问题,导致某些组件未正常加载。
---
### 解决方案
#### 检查点一:确认 `taskRegistrar` 是否已正确注入
```java
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar; // 确保此处被调用了,并且不是null
startDynamicTask(); // 初始化时立即启动一次动态任务
}
```
如果 `configureTasks` 方法没有被执行,则可能导致 `this.taskRegistrar` 始终为null。你可以通过添加日志打印来验证该方法的执行情况:
```java
log.debug("Configure tasks method called.");
```
#### 检查点二:数据库查询结果是否有误
确保从数据库获取到的任务配置是非空并且包含有效的cron表达式:
```java
try {
config = scheduleConfigMapper.selectOne(new LambdaQueryWrapper<ScheduleConfig>()
.eq(ScheduleConfig::getName, "月结定时任务"));
} catch (Exception e) {
log.error("获取定时任务配置时发生异常", e);
}
// 添加额外校验逻辑
if (config == null || StringUtils.isBlank(config.getCron())) {
log.error("数据库中的定时任务配置无效,任务名称:{}, cron表达式:{}",
"月结定时任务", (config != null ? config.getCron() : ""));
return;
}
```
这里我们增加了对 `config` 对象以及其 `Cron` 属性的有效性判断。
#### 检查点三:测试环境下的数据完整性
如果你正在本地调试程序,请务必保证数据库表中有正确的记录存在,并满足条件 `"name"='月结定时任务'` 。可以尝试手动插入一条示例行用于验证:
```sql
INSERT INTO schedule_config(name,cron,enabled) VALUES('月结定时任务','0 0/5 * * * ?',true);
```
注意上述SQL仅作为参考模型,请依据实际项目结构修改对应列名等细节部分!
#### 最后一步——处理边界场景
即便前面步骤都顺利完成之后仍需考虑极端状况下可能出现的问题比如网络波动影响mapper层读取效率等等;因此建议引入超时机制或者重试策略提升系统稳定性。
---
### 修改后的关键代码片段
将以上所有改进合并起来形成最终版本的关键区域如下所示:
```java
private synchronized void startDynamicTask() {
if (scheduledFuture != null && !scheduledFuture.isDone()) {
boolean cancelledSuccessfully = scheduledFuture.cancel(true);
if(!cancelledSuccessfully){
log.warn("现有任务取消失败!");
}
}
try{
ScheduleConfig config = scheduleConfigMapper.selectOne(
new QueryWrapper<ScheduleConfig>().lambda()
.eq(ScheduleConfig::getName,"月结定时任务")
);
if(null==config||StringUtils.isEmpty(config.getCron())){
throw new RuntimeException("无法检索有效的时间计划规则!");
}
Runnable jobDetail=()->{/*...*/};
scheduledFuture=this.taskRegistrar.schedule(jobDetail,new CronTrigger(config.getCron()));
}catch(Throwable t){
log.error("重新创建周期型作业期间遇到严重故障.",t.getMessage());
}
}
```
---
阅读全文