您的位置:首页 > 编程语言 > Java开发

Spring2.5.6定时器的使用

2016-01-15 13:54 447 查看
最近在工作中需要用到定时器,而项目使用的是SSH,所以就考虑使用Spring的定时器实现功能,但是了解需求后发现Spring的SimpleTriggerBean和CronTriggerBean没办法满足.需求是这样的:

1.需要3个定时器在特定的时间对2张数据表的加密字段进行检查.如果发现数据库的敏感数据被修改过(加密字段不一样),则针对被修改的数据生成一条警告数据保存到系统警告表中,在后台管理系统中,管理员能及时处理.同时把被修改数据的校验字段改成false.以便维护人员查询数据.

2.第一个定时器:在每天凌晨2点开始执行, 检查昨天凌晨2点到今天凌晨2点的数据.

3.第二个定时器:每隔2天,凌晨2:10分执行一次,检查2天前凌晨2点到今天凌晨2点的数据.

4.第三个定时器:每隔3天,凌晨2:20分执行一次,检查3天前凌晨2点到今天凌晨2点的数据.

5.需要保证在3个定时器的周期中每条记录被检查3次.在检查数据时,避免数据库锁表和代码中创建过多的object,每次只能查询前100条记录,校验100条数据后需要记录最后的ID,避免重复校验.

用CronTriggerBean做的时候,可以用cronExpression的表达式配置出每隔几天在几点几分几秒几毫秒运行,但是后来发现cronExpression表达式'天'的参数,是从每月的1号开始计算的,而不是从服务器启动的那天开始算的,当某个月只有30天或者29天的时候就会导致第3个定时器少执行一次.

用SimpleTriggerBean的话,在网上搜了一下配置,通篇大论的都是Ctrl+C,Ctrl+V的说startDelay和repeatInterval的配置和使用.由于服务器启动的时间不确定,所以这里不能配置第一次执行的延迟时间.

2种触发器都不行,都是只能满足部分的需求,所以就去看了一上午Spring定时任务的源码.然后自己就有个思路,SimpleTriggerBean能配置每个定时器循环执行的周期,那么只要确定3个定时器第一次执行的时间能配置就行,这样的话自定义个一个类,继承SimpleTriggerBean,自己定义配置的参数,然后重写setStartTime方法,问题就能解决.下面是代码:

XML的配置:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" default-autowire="no" default-lazy-init="false">

<!-- 定时任务 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="checkTicketAndACL24Trigger" />
<ref bean="checkTicketAndACL48Trigger" />
<ref bean="checkTicketAndACL72Trigger" />
</list>
</property>
</bean>

<!-- 任务调度类-->
<bean id="checkTicketAndAcctCreditLog" class="com.ct.spade.gs.core.task.CheckTicketAndAcctCreditLog" >
<property name="genericHibernateDao" ref="genericHibernateDao" />
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 24小时检查1次 -->
<bean id="checkTicketAndAcctCreditLog24" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="checkTicketAndAcctCreditLog"/>
<property name="targetMethod" value="execute24Hrs" />
</bean>
<bean id="checkTicketAndACL24Trigger" class="com.ct.spade.gs.core.task.CustomSimpleTriggerBean">
<property name="jobDetail" ref="checkTicketAndAcctCreditLog24" />
<property name="checkTimeValue" value="2" />
<property name="delayExpression" value="1 2 0 0 0" />
<property name="repeatInterval" value="86400000" /><!--每天凌晨2点执行一次  -->
</bean>

<!-- 48小时检查1次 -->
<bean id="checkTicketAndAcctCreditLog48" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="checkTicketAndAcctCreditLog"/>
<property name="targetMethod" value="execute48Hrs" />
</bean>
<bean id="checkTicketAndACL48Trigger" class="com.ct.spade.gs.core.task.CustomSimpleTriggerBean">
<property name="jobDetail" ref="checkTicketAndAcctCreditLog48" />
<property name="checkTimeValue" value="2" />
<property name="delayExpression" value="2 2 10 0 0" />
<property name="repeatInterval" value="172800000" />
</bean>

<!-- 72小时检查1次 -->
<bean id="checkTicketAndAcctCreditLog72" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="checkTicketAndAcctCreditLog" />
<property name="targetMethod" value="execute72Hrs" />
</bean>
<bean id="checkTicketAndACL72Trigger" class="com.ct.spade.gs.core.task.CustomSimpleTriggerBean">
<property name="jobDetail" ref="checkTicketAndAcctCreditLog72" />
<property name="checkTimeValue" value="2" />
<
4000
;property name="delayExpression" value="3 2 20 0 0" />
<property name="repeatInterval" value="259200000" />
</bean>
</beans>

CustomSimpleTriggerBean类是自己定义继承SimpleTriggerBean 的类.checkTimeValue是判断的时间点,比如服务器启动时间如果超过了这个时间的设定,那么定时任务第一次运行的时间就从明天开始.delayExpression表达式有5个参数用空格分割,分别是天 时 分 秒 毫秒,代表定时任务第一次运行的时间,参数'天'是服务器启动当天往后延迟几天,后面的4个参数则是具体的设定时间,5个参数可以只配任意个,每个参数都有默认值,下面贴CustomSimpleTriggerBean的代码:

Java Code:

import java.io.Serializable;

import java.util.Calendar;

import java.util.Date;

import org.springframework.scheduling.quartz.SimpleTriggerBean;

import com.ct.spade.gs.core.randomiser_v2.util.StringUtil;

import com.ct.spade.gs.core.utils.ArrayUtils;

/**

 * 自定义触发器的开始时间

 */

public class CustomSimpleTriggerBean extends SimpleTriggerBean implements Serializable {

private static final long serialVersionUID = 2049345814989743480L;
// 小时
private static final int HOUR_OF_DAY = 0;
// 分钟
private static final int MINUTE = 1;
// 秒
private static final int SECOND = 2;
// 毫秒
private static final int MILLISECOND = 3;

// 第一次延迟执行判断的时间单位(默认:Calendar.HOUR_OF_DAY=天)
private int checkTimeType;
// 第一次延迟执行判断的时间值
private int checkTimeValue;
// 第一次执行的时间表达式(格式:'DAY_OF_MONTH HOUR_OF_DAY MINUTE SECOND MILLISECOND')
private String delayExpression;

@Override
public void setStartTime( Date startTime ) {
Calendar calendar = Calendar.getInstance( );
calendar.setTime( new Date() );
int checkTime = calendar.get( getTimeType( getCheckTimeType( ) ) );
if ( checkTime >= this.checkTimeValue ) {
setStartUpTime( calendar );
}
super.setStartTime( calendar.getTime( ) );
}

/**
* 设置启动的时间
* @param startCalendar
*/
private void setStartUpTime( Calendar startCalendar ) {
if ( null == startCalendar ) {
return;
}
if ( !StringUtil.isNotEmpty( delayExpression ) ) {
delayExpression = "1 0 0 0 0";
}
delayExpression = delayExpression.trim( ).replaceAll( "\\s+", " " );
String[] timeValues = delayExpression.split( " " );
if ( ArrayUtils.isEmpty( timeValues ) ) {
startCalendar.set( Calendar.DAY_OF_MONTH, startCalendar.get( Calendar.DAY_OF_MONTH ) + 1 );
startCalendar.set( Calendar.HOUR_OF_DAY, 0 );
startCalendar.set( Calendar.MINUTE, 0 );
startCalendar.set( Calendar.SECOND, 0 );
startCalendar.set( Calendar.MILLISECOND, 0 );
} else {
int[] intArrays = ArrayUtils.toIntArray( timeValues );
int length = ArrayUtils.length( intArrays );
if ( length > 0 ) {
startCalendar.set( Calendar.DAY_OF_MONTH, startCalendar.get( Calendar.DAY_OF_MONTH ) + intArrays[0] );
}
if ( length > 1 ) {
startCalendar.set( Calendar.HOUR_OF_DAY, intArrays[1] );
} else {
startCalendar.set( Calendar.HOUR_OF_DAY, startCalendar.get( Calendar.HOUR_OF_DAY ) );
}
if ( length > 2 ) {
startCalendar.set( Calendar.MINUTE, intArrays[2] );
} else {
startCalendar.set( Calendar.MINUTE, startCalendar.get( Calendar.MINUTE ));
}
if ( length > 3 ) {
startCalendar.set( Calendar.SECOND, intArrays[3] );
} else {
startCalendar.set( Calendar.SECOND, startCalendar.get( Calendar.SECOND ) );
}
if ( length > 4 ) {
startCalendar.set( Calendar.MILLISECOND, intArrays[4] );
} else {
startCalendar.set( Calendar.MILLISECOND, startCalendar.get( Calendar.MILLISECOND ) );
}
}
}

/**
* 获取时间类型
* @param delayType
* @return
*/
private int getTimeType( int delayType ) {
int result = 0;
switch ( delayType ) {
case HOUR_OF_DAY :
result = Calendar.HOUR_OF_DAY;
break;
case MINUTE :
result = Calendar.MINUTE;
break;
case SECOND :
result = Calendar.SECOND;
break;
case MILLISECOND :
result = Calendar.MILLISECOND;
break;
default :
break;
}
return result;
}

public int getCheckTimeValue( ) {
return checkTimeValue;
}

public void setCheckTimeValue( int checkTimeValue ) {
this.checkTimeValue = checkTimeValue;
}

public int getCheckTimeType( ) {
return checkTimeType;
}

public void setCheckTimeType( int checkTimeType ) {
this.checkTimeType = checkTimeType;
}

public String getDelayExpression( ) {
return delayExpression;
}

public void setDelayExpression( String delayExpression ) {
this.delayExpression = delayExpression;
}

}

CheckTicketAndAcctCreditLog 类:这个类是具体执行业务的,业务代码就没贴出来,execute方法中根据需求实现.

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Calendar;

import java.util.Date;

import java.util.List;

import org.framework.support.hibernate.GenericHibernateDao;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.transaction.annotation.Transactional;

public class CheckTicketAndAcctCreditLog {

private Logger logger = LoggerFactory.getLogger( CheckTicketAndAcctCreditLog.class );

private final static String CHECK_TIME = "2:00:00.000";
private final static String FORMAT_DATE = "yyyy-MM-dd";
private final static String FORMAT_TIME = "yyyy-MM-dd HH:mm:ss.SSS";

private GenericHibernateDao genericHibernateDao;
private JdbcTemplate jdbcTemplate;

/**
* 24小时执行一次
*/
public void execute24Hrs( ) {
logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 24H quartz check start" );

Date nowDate = new Date( );
String startTime = getDateBeforeString( nowDate, 1 ) + " " + CHECK_TIME;
String endTime = getDateString( nowDate, FORMAT_DATE ) + " " + CHECK_TIME;

execute( startTime, endTime );

logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 24H quartz check finished" );
}

/**
* 48小时执行一次
*/
public void execute48Hrs( ) {
logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 48H quartz check start" );

Date nowDate = new Date( );
String startTime = getDateBeforeString( nowDate, 2 ) + " " + CHECK_TIME;
String endTime = getDateString( nowDate, FORMAT_DATE ) + " " + CHECK_TIME;

execute( startTime, endTime );

logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 48H quartz check finished" );
}

/**
* 72小时执行一次
*/
public void execute72Hrs( ) {
logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 72H quartz check start" );

Date nowDate = new Date( );
String startTime = getDateBeforeString( nowDate, 3 ) + " " + CHECK_TIME;
String endTime = getDateString( nowDate, FORMAT_DATE ) + " " + CHECK_TIME;

execute( startTime, endTime );

logger.info( getDateString( new Date( ), FORMAT_TIME ) + " 72H quartz check finished" );
}

@Transactional
public void execute( String startTime, String endTime ) {
// ...
}

/**
* 根据条件查询AcctCreditLog前100条记录

* @param lastId
*            ID
* @param startTime
*            开始时间
* @param endTime
*            结束时间
* @return
*/
private List< AcctCreditLog > findAcctCreditLogList( long lastId, String startTime, String endTime ) {
if ( lastId < 0 ) {
return null;
}

//...
return null;
}

/**
* 查询Ticket前100条记录

* @param lastId
*            每轮最后的ID
* @param startTime
*            开始时间
* @param endTime
*            结束时间
* @return
*/
private List< Ticket > findTicketList( long lastId, String startTime, String endTime ) {
if ( lastId < 0
9fa3
) {
return null;
}
// ...
return null;
}

/**
* 批量保存或更新数据

* @param dataList
*/
private boolean batchSaveOrUpdate( String[] sql ) {
boolean flag = true;

//...

return flag;

}

private WarningData buildWarningData( String msgType, long id, String merchant, String player, String clientIp, String gameCode, String currency, String fingerprint ) {
WarningData warningData = new WarningData();
// ...

return warningData;
}

/**
* 校验加密字段是否一样

* @param acctCreditLog
* @return
*/
private boolean isEqualsSHA512Str( Object object ) {
if ( null == object ) {
return false;
}
boolean flag = false;
//...
return flag;
}

/**
* 获取时间的字符串表示形式

* @param date
* @return
*/
private String getDateString( Date date, String format ) {
return new SimpleDateFormat( format ).format( date );
}

/**
* 获取指定日期前几天的字符串表示形式

* @param date
*            指定日期
* @param before
*            往前几天
* @return
*/
private String getDateBeforeString( Date date, int before ) {
if ( before <= 0 ) {
return getDateString( date, FORMAT_DATE );
}
Calendar calendar = Calendar.getInstance( );
calendar.setTime( date );
calendar.set( Calendar.DATE, calendar.get( Calendar.DATE ) - before );
return getDateString( calendar.getTime( ), FORMAT_DATE );
}

public void setGenericHibernateDao( GenericHibernateDao genericHibernateDao ) {
this.genericHibernateDao = genericHibernateDao;
}

public void setJdbcTemplate( JdbcTemplate jdbcTemplate ) {
this.jdbcTemplate = jdbcTemplate;
}

}

写这边博客的目的是怕有段时间不用这个知识点后,可能会忘记. 代码中有些实现可能还有更好的实现方式, 如果哪位大神看到请手下留情.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息