欢迎来到mcg的博客

扩大
缩小

spring的定时更新

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的使用有很大的帮助。

5. 参考文献

参考博客1参考博客2

posted @ 2020-06-24 21:30  mcgcG  阅读(478)  评论(0编辑  收藏  举报