1. 程式人生 > >【SSH專案實戰】國稅協同平臺-34.quartz&CronTrigger

【SSH專案實戰】國稅協同平臺-34.quartz&CronTrigger

我們上次使用quartz任務排程框架建立了一個在後臺按照時間間隔不停執行的任務,我們使用的是simpleTrigger簡單觸發器,為了實現我們的月末自動回覆的功能,我們要設定觸發器能夠在某個精確時間去自動執行任務,那麼使用simpleTrigger簡單觸發器就遠遠不夠了,我們需要使用CronTrigger任務觸發器來實現這個功能。

我們的CronTrigger設定步驟如下:
任務觸發器(CronTrigger)
① 設定任務詳細
② 設定執行時機(cronExpression)
cronExpression:秒 分 時 日 月 周 年(可選)

我們先在任務類中寫另外一個執行方法:
package cn.edu.hpu.tax.complain;

import java.text.SimpleDateFormat;
import java.util.Date;

public class QuartzTask {
	
	public void doSimpleTriggerTask() {
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("doing simpleTrigger task..."+sdf.format(new Date()));
	}
	
	public void doCronTriggerTask() {
		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("doing cronTrigger task..."+sdf.format(new Date()));
	}
}

就是上面的doCronTriggerTask()

然後修改我們的quartz的配置檔案quartz-spring.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" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <!-- 註冊一個普通bean -->
    <bean id="quartzTask" class="cn.edu.hpu.tax.complain.QuartzTask"></bean>
    
    <!-- 1.指定任務詳細資訊 -->
    <bean id="jobDetial1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	    <!--  ① 設定執行物件 -->
	    <property name="targetObject" ref="quartzTask"></property>
	    <!--  ② 設定執行物件中對應的執行方法 -->
	    <property name="targetMethod" value="doSimpleTriggerTask"></property>
	    <!--  ③ 是否可以同步執行(這裡設定不同步執行) -->
	    <property name="concurrent" value="false"></property>
    </bean>
  
    <bean id="jobDetial2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	    <!--  ① 設定執行物件 -->
	    <property name="targetObject" ref="quartzTask"></property>
	    <!--  ② 設定執行物件中對應的執行方法 -->
	    <property name="targetMethod" value="doCronTriggerTask"></property>
	    <!--  ③ 是否可以同步執行(這裡設定不同步執行) -->
	    <property name="concurrent" value="false"></property>
    </bean>
    
    <!-- 2.制定任務執行時機(任務執行觸發器) -->
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    	<!-- ① 設定任務詳細 -->
    	<property name="jobDetail" ref="jobDetial1"></property>
  	 	<!-- ② 設定任務延遲執行時間(延遲2秒) -->
  	 	<property name="startDelay" value="2000"></property>
   		<!-- ③ 設定任務執行頻率(執行頻率為每2秒執行一下) -->
   		<property name="repeatInterval" value="2000"></property>
    </bean>
    
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    	<!-- 設定任務詳細 -->
    	<property name="jobDetail" ref="jobDetial2"></property>
    	<!-- 設定任務執行時機,cron表示式 -->
    	<property name="cronExpression" value="0/3 * * * * ?"></property>
    </bean>
    
    <!-- 3.設定排程工廠 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    	<property name="triggers">
    		<list>
    			<!-- 第一個觸發器 -->
    			<ref bean="simpleTrigger"/>
    			<!-- 第二個觸發器 -->
    			<ref bean="cronTrigger"/>
    		</list>
    	</property>
    </bean>
</beans>

CronTrigger中的 cronExpression 表示式屬性中接收的內容:
在表示式中時間格式間用空格隔開,每個時間表示:秒 分鐘 小時 日 月 周 年(可選填)
可出現的值和符合:


'*' 字元可以用於所有欄位,在“分”欄位中設為"*"表示"每一分鐘"的含義。

'?' 字元可以用在“日”和“周幾”欄位. 它用來指定 '不明確的值'. 這在你需要指定這兩個欄位中的某一個值而不是另外一個的時候會被用到。在後面的例子中可以看到其含義。

'-' 字元被用來指定一個值的範圍,比如在“小時”欄位中設為"10-12"表示"10點到12點".

',' 字元指定數個值。比如在“周幾”欄位中設為"MON,WED,FRI"表示"the days Monday, Wednesday, and Friday".

'/' 字元用來指定一個值的的增加幅度. 比如在“秒”欄位中設定為"0/15"表示"第0, 15, 30, 和 45秒"。而 "5/15"則表示"第5, 20, 35, 和 50". 在'/'前加"*"字元相當於指定從0秒開始. 每個欄位都有一系列可以開始或結束的數值。對於“秒”和“分”欄位來說,其數值範圍為0到59,對於“小時”欄位來說其為0到23, 對於“日”欄位來說為0到31, 而對於“月”欄位來說為1到12。"/"欄位僅僅只是幫助你在允許的數值範圍內從開始"第n"的值。 因此對於“月”欄位來說"7/6"只是表示7月被開啟而不是“每六個月”, 請注意其中微妙的差別。

'L'字元可用在“日”和“周幾”這兩個欄位。它是"last"的縮寫, 但是在這兩個欄位中有不同的含義。例如,“日”欄位中的"L"表示"一個月中的最後一天" —— 對於一月就是31號對於二月來說就是28號(非閏年)。而在“周幾”欄位中, 它簡單的表示"7" or "SAT",但是如果在“周幾”欄位中使用時跟在某個數字之後, 它表示"該月最後一個星期×" —— 比如"6L"表示"該月最後一個週五"。當使用'L'選項時,指定確定的列表或者範圍非常重要,否則你會被結果搞糊塗的。

'W' 可用於“日”欄位。用來指定歷給定日期最近的工作日(週一到週五) 。比如你將“日”欄位設為"15W",意為: "離該月15號最近的工作日"。因此如果15號為週六,觸發器會在14號即週五呼叫。如果15號為週日, 觸發器會在16號也就是週一觸發。如果15號為週二,那麼當天就會觸發。然而如果你將“日”欄位設為"1W", 而一號又是週六, 觸發器會於下週一也就是當月的3號觸發,因為它不會越過當月的值的範圍邊界。'W'字元只能用於“日”欄位的值為單獨的一天而不是一系列值的時候。

'L'和'W'可以組合用於“日”欄位表示為'LW',意為"該月最後一個工作日"。

'#' 字元可用於“周幾”欄位。該字元表示“該月第幾個周×”,比如"6#3"表示該月第三個週五( 6表示週五而"#3"該月第三個)。再比如: "2#1" = 表示該月第一個週一而 "4#5" = 該月第五個週三。注意如果你指定"#5"該月沒有第五個“周×”,該月是不會觸發的。

'C' 字元可用於“日”和“周幾”欄位,它是"calendar"的縮寫。它表示為基於相關的日曆所計算出的值(如果有的話)。如果沒有關聯的日曆, 那它等同於包含全部日曆。“日”欄位值為"5C"表示"日曆中的第一天或者5號及其以後",“周幾”欄位值為"1C"則表示"日曆中的第一天或者週日及其以後"。

對於“月份”欄位和“周幾”欄位來說合法的字元都不是大小寫敏感的。

官方文件示例:



我們在其中設定的是"0/3 * * * * ?"意思就是每天每時每隔3秒執行一次任務。我們和之前的simpleTrigger一起執行,來測試我們的配置是否成功:
重啟伺服器,發現控制檯輸出以下資訊:
......
資訊: Server startup in 16932 ms
doing simpleTrigger task...2015-12-07 09:09:44
doing cronTrigger task...2015-12-07 09:09:45
doing simpleTrigger task...2015-12-07 09:09:46
doing cronTrigger task...2015-12-07 09:09:48
doing simpleTrigger task...2015-12-07 09:09:48
doing simpleTrigger task...2015-12-07 09:09:50
doing cronTrigger task...2015-12-07 09:09:51
doing simpleTrigger task...2015-12-07 09:09:52
doing cronTrigger task...2015-12-07 09:09:54
doing simpleTrigger task...2015-12-07 09:09:54
doing simpleTrigger task...2015-12-07 09:09:56
doing cronTrigger task...2015-12-07 09:09:57
doing simpleTrigger task...2015-12-07 09:09:58
......

我們可以看到simpleTrigger和cronTrigger同時執行,而且cronTrigger是每隔3秒執行一次。
這證明我們的配置是成功的。大家可以按照上面給的配置規則來測試其他的配置,這裡就不再一一測試了。

接下來我們就可以著手實現我們的需求
我們的需求是每月最後一天去自動處理尚未回覆的速投請求,將沒有回覆的投訴設定為“已失效”。在後臺管理中不能對該型別投訴進行回覆。

時機:每個月月底最後一天;cronExpression:10 10 2 L * ?(凌晨2點10分10秒自動處理,為了避開工作高峰時間)
執行什麼內容:對本月之前的待受理投訴進行自動處理;將投訴資訊的狀態改為 已失效
如何執行:
本月之前的待受理投訴:select * from Complain where 投訴時間 < 本月1號0時0分0秒


首先在我們的“投訴”模組的包下的spring配置檔案中加入我們的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <!-- 繼承了注入sessionFactory的抽象類,不用反覆出入sessionFactory -->
    <bean id="complainDao" class="cn.edu.hpu.tax.complain.dao.impl.ComplainDaoImpl" parent="xDao"></bean>
    
    <!-- 掃描Service -->
    <context:component-scan base-package="cn.edu.hpu.tax.complain.service.impl"></context:component-scan>
    
    <!-- 1.指定任務詳細資訊 -->
    <bean id="complainJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	    <!--  ① 設定執行物件 -->
	    <property name="targetObject" ref="complainService"></property>
	    <!--  ② 設定執行物件中對應的執行方法 -->
	    <property name="targetMethod" value="autoDeal"></property>
	    <!--  ③ 是否可以同步執行(這裡設定不同步執行) -->
	    <property name="concurrent" value="false"></property>
    </bean>
  
    <!-- 2.制定任務執行時機(任務執行觸發器) -->
    <bean id="complainCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    	<!-- 設定任務詳細 -->
    	<property name="jobDetail" ref="complainJobDetail"></property>
    	<!-- 設定任務執行時機,cron表示式 -->
    	<property name="cronExpression" value="10 10 2 L * ?"></property>
    </bean>
    
    <!-- 3.設定排程工廠 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    	<property name="triggers">
    		<list>
    			<!-- 觸發器 -->
    			<ref bean="complainCronTrigger"/>
    		</list>
    	</property>
    </bean>
	
</beans>

然後我們可以看到,我們的任務執行類是complainService,其中任務執行方法是autoDetail方法,我們下面在complainService類中去編寫autoDetail方法:
介面中定義此方法:
package cn.edu.hpu.tax.complain.service;

import cn.edu.hpu.tax.complain.entity.Complain;
import cn.edu.hpu.tax.core.service.BaseService;

public interface ComplainService extends BaseService<Complain> {

	//自動受理投訴
	public void autoDeal();
	
}


首先類中實現該方法:
package cn.edu.hpu.tax.complain.service.impl;

import java.util.Calendar;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import cn.edu.hpu.tax.complain.dao.ComplainDao;
import cn.edu.hpu.tax.complain.entity.Complain;
import cn.edu.hpu.tax.complain.service.ComplainService;
import cn.edu.hpu.tax.core.service.impl.BaseServiceImpl;
import cn.edu.hpu.tax.core.util.QueryHelper;

@Service("complainService")
public class ComplainServiceImpl extends BaseServiceImpl<Complain> implements
		ComplainService {

	private ComplainDao complainDao;
	
	@Resource
	public void setComplainDao(ComplainDao complainDao) {
		super.setBaseDao(complainDao);
		this.complainDao = complainDao;
	}

	@Override
	public void autoDeal() {
		//1、查詢本月之前的待受理的投訴列表
		QueryHelper queryHelper=new QueryHelper(Complain.class,"c");
		queryHelper.addCondition("c.state=?",Complain.COMPLAIN_STATE_UNDONE);
		
		Calendar cal=Calendar.getInstance();
		cal.set(Calendar.DAY_OF_MONTH, 1);//設定當前時間的日期為1號
		cal.set(Calendar.HOUR_OF_DAY, 0);//設定當前時間的日期為0時
		cal.set(Calendar.MINUTE, 0);//設定當前時間的日期為0分
		cal.set(Calendar.SECOND, 0);//設定當前時間的日期為0秒
		queryHelper.addCondition("c.compTime < ?", cal.getTime());
		
		List<Complain> list=findObjects(queryHelper);
		if(list != null && list.size()>0){
			//2、更新投訴資訊的狀態為已失效
			for(Complain comp:list){
				comp.setState(Complain.COMPLAIN_STATE_INVALID);
				update(comp);
			}
		}
	}
}

這樣,我們每個月的月底的凌晨2點10分10秒就會自動執行complainService中的autoDeal方法,來設定本月之前的所有未回覆的投訴資訊為"無效的"。

然後“無效的”投訴是不能夠回覆的,我們在jsp中加上這個控制,使得所有無效的投訴的“回覆”連結失效:
<td align="center">
     <s:if test="state != 2">
         <a href="javascript:doDeal('<s:property value='compId'/>')">受理</a>
     </s:if>
</td>
效果:


至此,我們的quartz與simpleTrigger、CronTrigger學習完畢,月底自動回覆功能也已經實現。