Java定時(shí)任務(wù)詳解
定時(shí)任務(wù)在項(xiàng)目中經(jīng)常會使用到,本文主要根據(jù)博主自己使用定時(shí)的經(jīng)驗(yàn)分如下幾點(diǎn)介紹定時(shí)任務(wù):
1、Quartz定時(shí)任務(wù)簡介及Spring配置Quartz定時(shí)任務(wù)
2、SchedulerFactory對定時(shí)任務(wù)進(jìn)行增刪改查
3、總結(jié)
Quartz定時(shí)任務(wù)簡介:
Quartz是項(xiàng)目中經(jīng)常用到的定時(shí)任務(wù)之一,是一個(gè)完全由java編寫的開源作業(yè)調(diào)度框架,可以與J2EE與J2SE應(yīng)用程序相結(jié)合也可以單獨(dú)使用,其主要組成部分包括Job、Scheduler、CronExpression,這里就不一一介紹了,下面介紹Spring如何配置Quartz。
配置Quartz需要明白的一點(diǎn)是配置Quartz即配置Job、Scheduler和CronExpression,這三部分配置完成后,就是一個(gè)完整的定時(shí)任務(wù),配置如下:
<bean id= "TestJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="xx.TestQuartzJob"/> <!-- 可以封裝各種數(shù)據(jù)到JobExecutionContext里,包括接口、類,其中testServiceImpl是Spring管理的Bean,需要什么聲明 --> <property name="jobDataAsMap"> <map> <entry key="test" value="test"/> <entry key ="testServiceImpl" value-ref="testServiceImpl"/> </map> </property> </bean> <bean id= "TestTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="TestJobDetail" /> <property name="cronExpression" value="0 0/1 * * * ?" /> </bean> <bean id= "testSchedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers" > <list> <ref bean="TestTrigger" /> </list> </property> </bean> <bean id="testServiceImpl" class="xx.service.impl.TestServiceImpl">
配置完成后,就是增加一個(gè)為你執(zhí)行一個(gè)任務(wù)的Java類。每一個(gè)Quartz Job必須有一個(gè) 實(shí)現(xiàn)了org.quartz.Job接口的具體類,代碼如下:
public class TestQuartzJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException
{
// 獲取Job配置的接口
TestServiceImpl testServiceImpl = (TestServiceImpl) arg0.getJobDetail().getJobDataMap().get("testServiceImpl");
// 執(zhí)行業(yè)務(wù)方法
quartzStart();
}
public void quartzStart(){
// 業(yè)務(wù)方法...
}
}
當(dāng)然,在Job中其實(shí)不用特意的配置接口,使用Spring的注入即可,這里只是把這個(gè)配置接口的方法進(jìn)行說明。
到這里,簡單的配置Quartz定時(shí)任務(wù)已經(jīng)完成,下面附加一個(gè)我在項(xiàng)目中使用Quartz的情形:cronExpression表達(dá)式從數(shù)據(jù)庫讀取。
當(dāng)時(shí),我解決這個(gè)問題的方法是繼承org.springframework.scheduling.quartz.CronTriggerBean,設(shè)置cronExpression,具體代碼如下:
Trigger的配置要改:
<bean id="TestTrigger" class="xx.InitCronTriggerFactoryBean"> <property name="jobDetail" ref="TestJobDetail" /> <property name="key" value="key" /> </bean>
xx.InitCronTriggerFactoryBean代碼:
public class InitCronTriggerFactoryBean extends CronTriggerFactoryBean implements Serializable {
private static final long serialVersionUID = 1L;
@Resource
private SysParamService sysParamService;
private String key;
public void setKey(String key)
{
this.key = key;
setCronExpression(getCronExpressionFromDB());
}
private String getCronExpressionFromDB()
{
if(StringUtils.isEmpty(key))
return "0 0/5 * * * ?";
SysParam sysParam = new SysParam();
try
{
sysParam = sysParamService.getNameByKey(key);
}
catch (Exception e)
{
e.printStackTrace();
}
if(sysParam != null && !StringUtils.isEmpty(sysParam.getParamValue()))
return sysParam.getParamValue();
return "0 0/5 * * * ?";
}
}
其中,SysParamService是根據(jù)key到數(shù)據(jù)庫查詢對應(yīng)的cronExpression表達(dá)式的接口,這個(gè)接口除了使用Spring注入,也可以使用set方法設(shè)置,如下:
<bean id="TestTrigger" class="xx.InitCronTriggerFactoryBean"> <property name="jobDetail" ref="TestJobDetail" /> <property name="key" value="key" /> <property name="sysParamService" ref="sysParamService" /> </bean> <bean id="sysParamService" class="xx.service.impl.SysParamServiceImpl">
這樣,在xx.InitCronTriggerFactoryBean就要加入sysParamService的set方法,此時(shí)的xx.InitCronTriggerFactoryBean代碼為:
public class InitCronTriggerFactoryBean extends CronTriggerFactoryBean implements Serializable {
private static final long serialVersionUID = 1L;
private SysParamServiceImpl sysParamService;
private String key;
public void setKey(String key)
{
this.key = key;
}
public void setSysParamService(SysParamServiceImpl sysParamService)
{
this.sysParamService = sysParamService;
setCronExpression(getCronExpressionFromDB());
}
private String getCronExpressionFromDB()
{
if(StringUtils.isEmpty(key))
return "0 0 0/1 * * ?";
SysParam sysParam = new SysParam();
try
{
sysParam = sysParamServiceImpl.getNameByKey(key);
}
catch (Exception e)
{
e.printStackTrace();
}
if(sysParam != null && !StringUtils.isEmpty(sysParam.getParamValue()))
return sysParam.getParamValue();
return "0 0 0/1 * * ?";
}
}
Quartz定時(shí)任務(wù)到這里就差不多了,基本的配置和使用就是上面將的,想要深入了解Quartz可以在網(wǎng)上查找更多的資料,并在實(shí)踐中使用。接下來將講解在項(xiàng)目中使用的可以隨時(shí)對定時(shí)任務(wù)進(jìn)行增刪改查操作的實(shí)例。
定時(shí)任務(wù)的增刪改查,其實(shí)可以看做是對數(shù)據(jù)進(jìn)行增刪改查。不過,定時(shí)任務(wù)的增刪改查是操作Job和Trigger,具體代碼如下:
定時(shí)任務(wù)管理器代碼:
public class QuartzManager {
private static final Logger logger = LogPresident.getRootLogger();
private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
private static Map<String, JobKey> jobKeyMap = new HashMap<String, JobKey>();
/**
* 增加一個(gè)定時(shí)任務(wù)
* @author zhiyuan.wu
* @date 2017年3月30日
* @param jobName
* @param triggerName
* @param cls
* @param date
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void addJob(String jobName, String triggerName, Class cls, Date date)
{
try
{
Scheduler scheduler = schedulerFactory.getScheduler();
// job
JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, Scheduler.DEFAULT_GROUP).build();
// 觸發(fā)器
SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity(triggerName, Scheduler.DEFAULT_GROUP).startAt(date).build();
scheduler.scheduleJob(jobDetail, trigger);
// 啟動(dòng)
scheduler.start();
jobKeyMap.put(jobDetail.getKey().getName(), jobDetail.getKey());
}
catch (Exception e)
{
logger.error("--------添加定時(shí)任務(wù)出錯(cuò):" + e.getMessage(), e);
}
}
/**
* 刪除對應(yīng)的定時(shí)任務(wù)
* @author zhiyuan.wu
* @date 2017年3月29日
* @param jobKey
*/
public static void removeJob(JobKey jobKey)
{
Scheduler scheduler;
try
{
scheduler = schedulerFactory.getScheduler();
scheduler.deleteJob(jobKey);
jobKeyMap.remove(jobKey.getName());
}
catch (SchedulerException e)
{
logger.error("--------刪除定時(shí)任務(wù)出錯(cuò):" + e.getMessage(), e);
}
}
/**
* 啟動(dòng)所有定時(shí)任務(wù)
* @author zhiyuan.wu
* @date 2017年3月29日
*/
public static void startJobs()
{
try
{
Scheduler sched = schedulerFactory.getScheduler();
sched.start();
}
catch (Exception e)
{
logger.error("--------啟動(dòng)所有定時(shí)任務(wù)出錯(cuò):" + e.getMessage(), e);
}
}
/**
* 停止所有定時(shí)任務(wù)
* @author zhiyuan.wu
* @date 2017年3月29日
*/
public static void shutdownJobs()
{
try
{
Scheduler sched = schedulerFactory.getScheduler();
if (!sched.isShutdown())
{
sched.shutdown();
}
}
catch (Exception e)
{
logger.error("--------停止所有定時(shí)任務(wù)出錯(cuò):" + e.getMessage(), e);
}
}
/**
* 修改定時(shí)任務(wù)
* @author zhiyuan.wu
* @date 2017年3月29日
* @param jobKey
* @param jobName
* @param triggerName
* @param cls
* @param date
*/
@SuppressWarnings("rawtypes")
public static void modifyJobTime(JobKey jobKey, String jobName, String triggerName, Class cls, Date date)
{
try
{
removeJob(jobKey);
addJob(jobName, triggerName, cls, date);
}
catch (Exception e)
{
logger.error("--------修改定時(shí)任務(wù)出錯(cuò):" + e.getMessage(), e);
}
}
/**
* 打印當(dāng)前定時(shí)任務(wù)的jobName
* @author zhiyuan.wu
* @date 2017年3月29日
* @throws SchedulerException
*/
public static void printJobName() throws SchedulerException
{
for (String jobName : jobKeyMap.keySet())
{
logger.info("--------jobName:" + jobName);
}
}
}
Job代碼:
public class TestJob implements Job {
private static final Logger logger = LogPresident.getRootLogger();
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobKey jobKey = context.getJobDetail().getKey();
logger.info("--------定時(shí)任務(wù)開始執(zhí)行:" + jobKey.getName());
// 業(yè)務(wù)方法...
// 移除定時(shí)任務(wù)
QuartzManager.removeJob(jobKey);
}
}
增加一個(gè)定時(shí)任務(wù)代碼:
QuartzManager.addJob(JobName, TriggerName , TestJob.class, Date);
這樣,在到達(dá)時(shí)間Date時(shí),定時(shí)任務(wù)將會執(zhí)行。不過這樣增加的任務(wù)是保存在內(nèi)存中,項(xiàng)目重啟將會丟失定時(shí)任務(wù),所以,最好增加一個(gè)類,在項(xiàng)目啟動(dòng)時(shí)將會掃描數(shù)據(jù)庫,將未執(zhí)行的定時(shí)任務(wù)重新加載到內(nèi)存中去。
定時(shí)任務(wù)在項(xiàng)目中運(yùn)用需要根據(jù)業(yè)務(wù)具體調(diào)整,但只要弄清楚定時(shí)任務(wù)的原理和實(shí)現(xiàn),那么就可以在項(xiàng)目中靈活運(yùn)它用來具體的業(yè)務(wù),希望這篇文章可以讓大家快速了解和運(yùn)用定時(shí)任務(wù),并運(yùn)用到實(shí)踐中。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
- java定時(shí)任務(wù)框架elasticjob詳解
- Quartz實(shí)現(xiàn)JAVA定時(shí)任務(wù)的動(dòng)態(tài)配置的方法
- Java實(shí)現(xiàn)終止線程池中正在運(yùn)行的定時(shí)任務(wù)
- 在Java Web項(xiàng)目中添加定時(shí)任務(wù)的方法
- Java實(shí)現(xiàn)Web應(yīng)用中的定時(shí)任務(wù)(實(shí)例講解)
- 詳解java解決分布式環(huán)境中高并發(fā)環(huán)境下數(shù)據(jù)插入重復(fù)問題
- Java中實(shí)現(xiàn)分布式定時(shí)任務(wù)的方法
相關(guān)文章
Java設(shè)計(jì)模式之模板方法模式Template Method Pattern詳解
在我們實(shí)際開發(fā)中,如果一個(gè)方法極其復(fù)雜時(shí),如果我們將所有的邏輯寫在一個(gè)方法中,那維護(hù)起來就很困難,要替換某些步驟時(shí)都要重新寫,這樣代碼的擴(kuò)展性就很差,當(dāng)遇到這種情況就要考慮今天的主角——模板方法模式2022-11-11
基于Socket類以及ServerSocket類的實(shí)例講解
下面小編就為大家?guī)硪黄赟ocket類以及ServerSocket類的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
SpringBoot集成MybatisPlus報(bào)錯(cuò)的解決方案
這篇文章主要介紹了SpringBoot集成MybatisPlus報(bào)錯(cuò)的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
詳解如何在項(xiàng)目中應(yīng)用SpringSecurity權(quán)限控制
本文主要介紹了如何在項(xiàng)目中應(yīng)用SpringSecurity權(quán)限控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06

