开启定时任务的方式有几种:包括quartz定时框架和java自带Timer定时器,区别在于,quartz定时任务在程序报错后能不断执行,而在Timer定时任务中,程序一旦报错,定时任务即不再执行,可根据需求选用。
前两天工作需求,需要写个定时任务去跑一些数据,我首先选择的就是quartz定时器,不过后来因为环境问题而改用了Timer定时器,下面简单介绍一下两种定时器的书写。
1,quaetz定时器,需要在applicationContext.xml中进行一些配置,代码如下:
<!-- 实现定时器任务-要调度的对象 -->
<bean id="jobBean" class="com.mer.listener.job.TimerJob" />
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="jobBean" />
<property name="targetMethod" value="execute" />
<!-- 将并发设置为false -->
<property name="concurrent" value="false" />
</bean>
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<!-- 定时任务,每天凌晨2点执行一次 -->
<!-- <property name="cronExpression" value="0 0 2 * * ?" /> -->
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<!-- 总管理类如果将lazy-init='false'那么容器启动就会执行调度程序 -->
<bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false" >
<property name="triggers">
<list>
<!-- 作业调度器,list下可加入其他的调度器 -->
<ref bean="trigger" />
</list>
</property>
</bean>
<!-- 注入工具类,普通类中能够调用server层方法 -->
<!-- <bean id="springContextUtil" class="com.mer.util.SpringContextUtil"></bean> -->
<bean id="springBeanFactoryUtils" class="com.mer.util.SpringBeanFactoryUtils"/>
配置完成后,只需在TimerJob中完成相关逻辑代码即可,execute方法即为定时任务执行方法,我的TimerJob类如下:
package com.mer.listener.job;
import org.springframework.context.ApplicationContext;import com.mer.service.BaseService;
import com.mer.service.cailanzidb.TranddelService;
import com.mer.util.LogUtil;
import com.mer.util.SpringBeanFactoryUtils;
import com.mer.util.SpringContextUtil;
public class TimerJob{
//使用工具类获取bean,在普通类中调用service
LogUtil log = new LogUtil();
public void execute(){
try {
//菜市场 市区 查看数据功能 每天定时录入交易数据到汇总表
System.out.println("定时器-----定时录入交易汇总表");
baseService.insert("transInsertGM");
baseService.insert("transInsertFJ");
baseService.insert("transInsertDM");
} catch (Exception e) {
log.errinfoe("执行定时器导入菜市场数据报错", e);
e.printStackTrace();
}
}
}
}
上面的quartz定时器就写好了,最后别忘了导入quartz定时器的相关jar包哦,下面说一下我遇到的一个坑,这里不属于定时器编写范围。
因为业务需求,定时器里要调用service层的相关方法跑数据,但是因为此类为普通类,不能直接通过注解直接注入service,如果直接使用的花,会报错baseService空指针。因此需要另外写一个工具类,工具类代码如下:
package com.mer.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 普通类调用Spring注解方式的Service层bean
* Created by HZC on 2015/10/21.
*/
public class SpringBeanFactoryUtils implements ApplicationContextAware {
private static ApplicationContext appCtx;
/**
* 此方法可以把ApplicationContext对象inject到当前类中作为一个静态成员变量。
*
* @param applicationContext ApplicationContext 对象.
* @throws BeansException
* @author hzc
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
appCtx = applicationContext;
}
/**
* 获取ApplicationContext
*
* @return
* @author hzc
*/
public static ApplicationContext getApplicationContext() {
return appCtx;
}
/**
* 这是一个便利的方法,帮助我们快速得到一个BEAN
*
* @param beanName bean的名字
* @return 返回一个bean对象
* @author hzc
*/
public static Object getBean(String beanName) {
return appCtx.getBean(beanName);
}
}
此工具类需要在applicationContext.xml配置里添加一行配置,如下:
<!-- 注入工具类,普通类中能够调用server层方法 -->
<bean id="springBeanFactoryUtils" class="com.mer.util.SpringBeanFactoryUtils"/>
如果有遇到相同问题的小伙伴,可以借鉴一下这个工具类,亲测可用。
2,好了,上面是使用quartz定时框架写定时任务,下面讲一下怎么用java自带的Timer定时器写定时任务,
Timer定时器用到的是litener监听器,所以需要在web.xml中将需要监听的定时任务加入定时器,配置如下:
<listener>
<listener-class>com.mer.conn.InitServletListener</listener-class>
</listener>
然后在监听类InitServletListener中完成相关代码,如下:
package com.mer.conn;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.mer.service.cailanzidb.TranddelService;
import com.mer.util.LogUtil;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
*
*********************************************************.<br>
* [类名] InitServletListener <br>
* [描述] 项目初始化监听器 <br>
* [作者] gw <br>
* [时间] 2016-1-9 下午9:48:07 <br>
*********************************************************.<br>
*/
public class InitServletListener implements ServletContextListener {
public void contextDestroyed(ServletContextEvent arg0) {
}
public void contextInitialized(ServletContextEvent sce) {
//获取service
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
final TranddelService tranddelService= (TranddelService) applicationContext.getBean("tranddelService");
LogUtil log = new LogUtil();
try {
//设置TimerTask
TimerTask task = new TimerTask() {
@SuppressWarnings("static-access")
@Override
public void run() {
//菜市场 市区 查看数据功能 每天定时录入交易数据到汇总表
System.out.println("定时器-----定时录入交易汇总表");
tranddelService.insert("transInsertGM");
tranddelService.insert("transInsertFJ");
tranddelService.insert("transInsertDM");
}
};
int dateSpan=24*60*60*1000;
//设置执行时间
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH)+1;//每天
//定制每天定时执行一次
calendar.set(year, month, day, 17,59,00); //时间设置 后三位是时分秒
Date date = calendar.getTime();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
Timer timer = new Timer();
//每天的date时刻执行task, 仅执行一次
timer.schedule(task, date,dateSpan);
Thread.sleep(1000); //延迟一秒执行
} catch (Exception e) {
log.errinfoe("执行定时器导入菜市场数据报错", e);
e.printStackTrace();
}
}
}
项目启动初始化监听器之后,即进行了任务监听。
后语:假如你跟我一样有业务需求,要通过普通类调用service层或dao层方法,建议使用Timer定时器,因为Timer和quartz的管理容器不同,所以在进行bean注入时,使用quartz会出现一些框架知识方面的坑,对此方面理解不太深的小伙伴,还是选择Timer比较保险,比如我哈。