背景
前段时间重构一个陈年老系统,顺便将jdk由7升级到了8,将spring从3.x升级到4.x,其他组件如MyBatis、swagger等也做了些升级。系统上线后,运行正常,皆大欢喜,这破系统总算也可以开心的写lambda了。
然而,好景不长,某日意外发现有个定时任务没有如期执行;再看下监控,定时任务失效的日期正是上线那天😳。慌乱之际,不由得庆幸并未造成资损,不然后果不堪设想啊!
问题分析
应用中定时任务是用的spring的@Scheduled注解实现的,在此次系统重构之前一直是正常执行的,本次重构也未改动。想必是spring升级导致的新旧版本兼容问题了。
对比了另外两个正常运行的task,找到了一些蛛丝马迹。
且看下这个task的代码:
@Controller
public class RefreshDictionaryJob {
private static final Logger logger = LoggerFactory.getLogger(RefreshDictionaryJob.class);
@RequestMapping(value = "/demo/dictionary/edit", method = RequestMethod.POST)
@ResponseBody
public String edit(XxxVO params) {
try {
// 略去若干行……
return "更新成功";
} catch (Exception e) {
logger.error("更新失败", e);
return "更新失败,请稍后再试";
}
}
@Scheduled(cron = "0 0/5 * * * ? ")
public void refreshDictionary() {
try {
// do something
} catch(Exception e){
logger.error("refreshDictionary执行失败" + e);
}
}
}
是不是很诡异?类命名为XxxJob,却加了个@Controller注解;同一个类中混合了@RequestMapping和@Scheduled方法,整体上比较混乱;而其他正常的task都是在@Component标注的类下。
测试一下试试吧,把@Controller替换为@Component,本地试一把————等待一会儿后,任务并未如期执行。
把@RequestMapping、@ResponseBody都注释掉,再试试看————果不其然,任务执行了!
翻了下spring关于@Scheduled的官方文档,并未提到这些限制,怀疑是spring4在生成代理类时产生了冲突。趁没有造成损失,赶紧先修复上线吧,否则领导那没法交代了! ̄□ ̄||
问题修复
尘归尘,土归土,Controller的归Controller,代码拆解调整如下:
@Component
public class RefreshDictionaryJob {
private static final Logger logger = LoggerFactory.getLogger(RefreshDictionaryJob.class);
@Scheduled(cron = "0 0/5 * * * ? ")
public void refreshDictionary() {
try {
// do something
} catch(Exception e){
logger.error("refreshDictionary执行失败" + e);
}
}
}
@Controller
@RequestMapping("/demo/dictionary")
public class DictionaryController {
private static final Logger logger = LoggerFactory.getLogger(DictionaryController.class);
@RequestMapping(value = "/edit", method = RequestMethod.POST)
@ResponseBody
public String edit(XxxVO params) {
try {
// 略去若干行……
return "更新成功";
} catch (Exception e) {
logger.error("更新失败", e);
return "更新失败,请稍后再试";
}
}
}
总结
编码不规范真是害死人啊,处处坑!
关于问题的根本原因,希望了解的朋友给个指引,感谢!