Timer定时器+mybatis
建立3个类:TimerManager类(定时器的启动和定时执行);XXXTimerTask类(定时任务类,即定时执行的服务);XXXTimeTaskListener类(定时器的监听器)。
1.TimerManager类
该类主要负责定时器的启动和执行。其中,PERIOD_DAY属性为定时任务的执行时间间隔(毫秒数)。我这里定的是2个小时。具体的方法中,先定义定时器第一次执行任务的时间,即下图中的date,然后new一个Timer(定时器)和TimerTask(定时任务),最后利用定时器的schedule方法,将定时任务、第一次执行时间和任务执行时间间隔传入定时器中。
其中要注意的一点,当项目启动时,启动时间若早于当日定时器第一次执行时间(下图中的date),定时任务不会立刻执行。
1 2
3 import java.util.Calendar;
4 import java.util.Date;
5 import java.util.Timer;
6
7 /*
8 * Service :需要定时器重复执行的服务
9 */
10 public class TimerManager {
11 //时间间隔
12 private static final long PERIOD_DAY = 2 * 60 * 60 * 1000;
13
14 public TimerManager(Service service) {
15 Calendar calendar = Calendar.getInstance();
16
17 /*** 定制第一次执行定时任务的时间 ***/
18 calendar.set(Calendar.HOUR_OF_DAY, 8);
19 calendar.set(Calendar.MINUTE, 10);
20 calendar.set(Calendar.SECOND, 0);
21
22 Date date=calendar.getTime(); //第一次执行定时任务的时间
23 //System.out.println("before 方法比较:"+date.before(new Date()));
24 //如果第一次执行定时任务的时间 小于 当前的时间
25 //此时要在 第一次执行定时任务的时间 加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。循环执行的周期则以当前时间为准
26 /* if (date.before(new Date())) {
27 date = this.addDay(date, 1);
28 System.out.println(date);
29 }
30 */
31 Timer timer = new Timer();
32 XXXTimerTask task = new XXXTimerTask(service);
33 //安排指定的任务在指定的时间开始进行重复的固定延迟执行。
34 timer.schedule(task,date,PERIOD_DAY);
35 }
36
37 // 增加或减少天数
38 /* public Date addDay(Date date, int num) {
39 Calendar startDT = Calendar.getInstance();
40 startDT.setTime(date);
41 startDT.add(Calendar.DAY_OF_MONTH, num);
42 return startDT.getTime();
43 }*/
44 }
2.XXXTimerTask类
该类即定时任务类,该类需要继承java中的TimerTask类。为了能让定时器融合spring和mybatis框架,我将此任务类看成一个对象。
具体代码如下:
其中run方法是由TimeTask继承而来,里面主要是定时器需要执行的具体服务。service是我在这个定时任务里需要调用的服务,是可以通过注解注入的。
1 import java.util.TimerTask;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.context.annotation.Configuration;
5
6
7
8 @Configuration
9 public class XXXTimerTask extends TimerTask {
10
11 @Autowired
12 private Service service;
13
14 public XXXTimerTask() {
15 }
16
17 public XXXTimerTask(Service service) {
18 super();
19 this.service = service;
20 }
21
22 @Override
23 public void run() {
24
25 try {
26 //在这里写你要执行的内容
27 service.xxx();
28 } catch (Exception e) {
29 e.printStackTrace();
30 System.out.println("-------------解析信息发生异常--------------");
31 }
32 }
33 }
34
3.XXXTimeTaskListener类
该类为定时器的监听器,需要继承ServletContextListener类。其中contextInitialized()方法和contextDestroyed()均是继承而来。contextInitialized()即Context()的初始化方法,contextDestroyed()是销毁方法。
1 package cn.dsmc.rivs.cmm.interceptor;
2
3 import javax.servlet.ServletContextEvent;
4 import javax.servlet.ServletContextListener;
5
6 import org.springframework.web.context.WebApplicationContext;
7 import org.springframework.web.context.support.WebApplicationContextUtils;
8
9
10 public class XXXTimeTaskListener implements ServletContextListener {
11 //Context()初始化方法
12 @Override
13 public void contextInitialized(ServletContextEvent sce) {
14 //获得Spring容器
15 WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
16 //从Spring容器中获得VisitatorialPlanService的实例
17 Service service = ctx.getBean(Service.class);
18 //新建一个定时管理器
19 new TimerManager(service);
20 }
21
22 public XXXTimeTaskListener() {
23 super();
24 }
25
26 @Override
27 public void contextDestroyed(ServletContextEvent sce) {
28 // 销毁
29
30 }
31 }
4.在web.xml中的配置
最后要在web.xml中进行配置。其中一定要注意的一点,监听器的配置一定一定要放在spring本身的监听器的后面。
1 <!--XXXTimeTaskListener 监听器--> 2 <listener> 3 <listener-class>cn...XXXTimeTaskListener</listener-class> 4 </listener>
个人总结:
在启动web项目时,Servlet容器(比如Tomcat)会读web.xml配置文件中的两个节点和,节点用来加载appliactionContext.xml(即Spring的配置文件),节点用来创建监听器(比如TestTaskListener)实例。Listener的生命周期是由servlet容器管理的,例中的TestTaskListener是由servlet容器实例化并调用其contextInitialized方法的,但是,service是通过@Service注解的,也就是说service是由Spring容器管理的,在Spring容器外无法直接通过依赖注入得到Spring容器管理的bean实例的引用。为了在Spring容器外得到Spring容器管理的bean,可以使用Spring提供的工具类WebApplicationContextUtils。也就是说,可以在servlet容器管理的Listener中使用该工具类获Spring管理的bean。
简单点,就是定时器在spring容器的外面,因此和spring的注解是有冲突的。而我们使用了mybatis框架,不可避免会使用注解注入(service层调用dao层)。所以我们需要将调用的service手动注入到spring容器中(XXXTimeTaskListener类中的contextInitialized()初始化方法)。
需要注意的地方:
7.1 虽然我的这个项目架构中没有appliactionContext.xml文件,但手动注入即WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());方法还是可以正常使用的。
7.2 不要忘记XXXTimeTask中的无参构造,否则启动时会报XXXTimeTask bean无法创建的异常。
7.3 我是在XXXTimeTaskListener类(监听器)中,将XXXTimeTask需调用的Service实例化。