1. 技术概述
服务器经常需要进行定时的数据更新,比如重置状态、重置计数、定时清空数据等,比如简时需要凌晨更新待办的状态,所以找到了@Scheduled来进行定时执行任务。
2. 技术详述
使用方式:
如果pom.xml中没有导入web依赖,请导入(这个就不多说啦)。
在入口程序(@SpringBootApplication注解的类)加上注解@EnableScheduling。
@Controller
@SpringBootApplication
@EnableScheduling
public class IndexControler {
@RequestMapping("/index")
@ResponseBody
public String index() {
return "helloword!";
}
}
编写专门控制定时任务的Bean(@Component注解的),利用@Scheduled注解定时方法,下图(下面讲解注解参数的具体功能和使用)。
@Component
@EnableScheduling
public class ScheduleUtil {
@Autowired
private UserTodoService userTodoService;
@Autowired
private TeamTodoService teamTodoService;
@Scheduled(cron = "0 0 0 * * ?")
public void updateStatus() {
userTodoService.updateSchedule();
teamTodoService.updateSchedule();
}
}
具体参数使用讲解:
1. cron
时间表达式,可以通过简单的配置就能完成各种时间的配置,我们通过CRON表达式几乎可以完成任意的时间搭配,它包含了七个域:
表达式参数 | 必填 | 允许填写的值 | 允许的通配符 |
---|---|---|---|
second | 是 | 0-59 | , - * / |
minute | 是 | 0-59 | , - * / |
hour | 是 | 0-23 | , - * / |
day | 是 | 1-31 | , - * ? / L W |
month | 是 | 1-12 / JAN-DEC | , - * / |
week | 是 | 1-7 or SUN-SAT | , - * ? / L # |
year | 否 | 1970-2099 | , - * / |
/**
* 通配符说明
* * 表示所有值,代表每次。
* ? 表示不指定值,即跳过
* - 表示区间,1-5代表1、2、3、4、5
* , 表示指定多个值,1,2,3代表就是1、2、3触发
* / 用于递增触发,start/jump,start开始jump间隔
* L 表示最后,在日字段设置最后一天,比如年、月、周最后一天
* W 表示离指定日期的最近那个工作日(周一至周五),例如3W三号最近的工作日,且不能为区间必须数字(例如前面的3)
* # 序号(表示每月的第几个周几),例如在周字段上设置”6#3”表示在每月的第三个周六
*/
/**
* 样例
* "0 0 12 * * ?" 每天中午十二点触发
* "0 15 10 ? * *" 每天早上10:15触发
* "0 15 10 * * ?" 每天早上10:15触发
* "0 15 10 * * ? *" 每天早上10:15触发
* "0 15 10 * * ? 2005" 2005年的每天早上10:15触发
* "0 * 14 * * ?" 每天从下午2点开始到2点59分每分钟一次触发
* "0 0/5 14 * * ?" 每天从下午2点开始到2:55分结束每5分钟一次触发
* "0 0/5 14,18 * * ?" 每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发
* "0 0-5 14 * * ?" 每天14:00至14:05每分钟一次触发
* "0 10,44 14 ? 3 WED" 三月的每周三的14:10和14:44触发
* "0 15 10 ? * MON-FRI" 每个周一、周二、周三、周四、周五的10:15触发
*/
@Scheduled(cron = "0 0 0 * * ?")
void testPlaceholder() {
System.out.println("Execute at " + System.currentTimeMillis());
}
2. zone
时区,接收一个java.util.TimeZone#ID。cron表达式会基于该时区解析。默认是一个空字符串,即取服务器所在地的时区。比如我们一般使用的时区Asia/Shanghai。该字段我们一般留空。
3. fixedDelay
@Scheduled(fixedDelay = 1000) //上一次执行完毕之后5秒再执行
4. fixedRate
@Scheduled(fixedRate = 1000) //上一次项目启动之后1秒再执行
5. initialDelay
@Scheduled(initialDelay = 1000) //延迟1秒后开始执行
代码实例
/**
* index 入口程序
* 需要@SpringBootApplication、@EnableScheduling注解
*/
@Controller
@SpringBootApplication
@EnableScheduling
public class IndexControler {
@RequestMapping("/index")
@ResponseBody
public String index() {
return "helloword!";
}
}
/**
* 管理定时任务的Bean
* 需要@Component注解
*/
@Component
@EnableScheduling
public class ScheduleUtil {
@Autowired
private UserTodoService userTodoService;
@Autowired
private TeamTodoService teamTodoService;
//注解定时任务,这里是每天0点0分0秒开始执行userTodo的state更新
@Scheduled(cron = "0 0 0 * * ?")
public void updateStatus() {
userTodoService.updateSchedule();
teamTodoService.updateSchedule();
}
//注解定时任务,上一次项目启动5s后执行
@Scheduled(fixedRate = 50000)
public void updateStatus() {
System.out.println("fixedRate: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
//注解定时任务,上次执行结束后10s执行
@Scheduled(fixedDelay = 100000)
public void updateStatus() {
System.out.println("fixedDelay: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
//注解定时任务,第一次延迟5s后开始执行,往后按照fixedRate参数规则执行
@Scheduled(initialDelay = 50000,fixedRate = 6000)
public void updateStatus() {
System.out.println("fixedRate after initialDelay: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
/**
* userTodoDAO 用于处理userTodo的DAO实现类
*/
@Repository(value = "userTodoDAO")
@Transactional(rollbackFor = Exception.class)
public class UserTodoDAOImpl extends BaseDAOImpl<UserTodo> implements UserTodoDAO {
@Override
public void updateSchedule() {
Session session = getSession();
String hqlUpdate = "update UserTodo as obj set todoStatusId = :status where todoStatusId != :oldStatus";
int updatedEntities = session.createQuery(hqlUpdate)
.setParameter("status", 1)
.setParameter("oldStatus", 1)
.executeUpdate();
}
}
/**
* userTodoService 用于处理userTodo的service实现类
*/
@Service("userTodoService")
@Transactional(rollbackFor = Exception.class)
public class UserTodoServiceImpl implements UserTodoService {
@Autowired
private UserTodoDAO userTodoDAO;
@Override
public void updateSchedule() {
userTodoDAO.updateSchedule();
}
}
3. 技术使用中遇到的问题和解决过程
问题1:如果对于cron的通配符不够熟练记忆使用,导致cron表达式写不出来,或者出现编写的cron表达式无法准确的定位自己需要的定时时间
解决方法:在线生成cron表达式
问题2:没有效果,未能在设置的时间执行定时任务
解决方法:
/**
* 1、可能原因没有@EnableScheduling注解,需要在启动入口或者任务配置类进行注解开启定时任务
* 2、定时任务并未至于Bean中,Spring无法接管,导致定时任务没有执行
*/
/**
* 第一种方法配置
*/
@Service
public class ScheduledTaskService {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 5000)
public void reportCurrentTime(){
......
}
}
@Configuration
@ComponentScan("")
@EnableScheduling //通过@EnableScheduling注解开启对计划任务的支持
public class TaskScheduleConfig {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskScheduleConfig.class);
}
}
/**
* 第二种方法配置
*/
@Controller
@SpringBootApplication
@EnableScheduling
public class IndexControler {
}
@Component
@EnableScheduling
public class ScheduleUtil {
......//任务
}
4. 总结
@Scheduled注解可以满足我们基本全部的定时任务,使用@Schedule不必熟练记住具体的cron编写,但是需要记住参数作用和参数名(cron、fixedRate、fixedDelay、initialDelay),利用在线生成corn表达式可以生成所需的corn表达式非常方便,掌握@Schedule对spring的使用有很大的帮助。