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实例化。