Spring4学习:任务调度和异步执行器之Quartz框架

一、Quartz基础知识 1、Quartz基础结构

(1)Job:接口,只有一个方法void execute(JobExecutionContext context),通过实现该接口来定义需要执行的任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。

(2)JobDetail:Quartz在每次支持Job时,都重新创建一个Job实例,所以它不是直接接收一个Job实例,而是接受一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。通过JobDetail来描述Job的实现类及其他相关的静态信息,如Job名称、描述、关联监听器等信息。

(3)Trigger:描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定间隔周期性执行时,选择SimpleTrigger,而CronTrigger则可以通过Cron表达式定义出各种复杂的调度方法。

(4)Calendar:一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。Quartz提供了若干个Calendar的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义。

(5)Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,二者在Scheduler中拥有各自的组及名称。组及名称是Scheduler查找定位容器中某一对象的依据。Scheduler可以将Trigger绑定到某一JobDetail中,这样,当Trigger被触发时,对应的Job就被执行。

(6)ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程来提高运行效率。

2、使用SimpleTrigger

SimpleTrigger(String name,String group):通过构造函数指定Trigger所属组和名称。

SimpleTrigger(String name,String group,Date startTime):指定Trigger所属组、名称和触发的时间。

SimpleTrigger(String name,String group,Date startTime,Date endTime,int repeatCount,long repeatInterval):指定所属组、名称、开始时间、结束时间、重复执行次数、时间间隔。

SimpleTrigger(String name,String group,Date startTime,String jobName,String jobGroup,Date endTime,int repeatCount,long repeatInterval)在指定触发参数的同时,通过jobGroup和jobName,使该Trigger和Scheduler中的某个任务关联起来。

public class SimpleJob implements Job {
	public void execute(JobExecutionContext jobCtx)
			throws JobExecutionException {
		System.out.println(jobCtx.getTrigger().getName()
				+ " triggered. time is:" + (new Date()));
	}
}
public class SimpleTriggerRunner {
	public static void main(String args[]) {
		try {
			JobDetail jobDetail = new JobDetail("job1_1", "jgroup1",SimpleJob.class);
			SimpleTrigger simpleTrigger = new SimpleTrigger("trigger1_1","tgroup1");
			simpleTrigger.setStartTime(new Date());
			simpleTrigger.setRepeatInterval(2000);
			simpleTrigger.setRepeatCount(100);        
			SchedulerFactory schedulerFactory = new StdSchedulerFactory();
			Scheduler scheduler = schedulerFactory.getScheduler();
			scheduler.scheduleJob(jobDetail, simpleTrigger);
			scheduler.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

3、使用CronTrigger

(1)Cron表达式

1星号(*):可用在所有字段中,表示对应时间域的每一个时刻。如:*在分钟字段表示每分钟。

2问好(?):只在日期和星期字段中使用,相当于占位符。

3减号(-):表示一个范围。如:10-12表示10点到12点

4逗号(,):表示一个列表值。如:MON,WED,FRI表示星期一,星期但和星期五

5斜杠(/):x/y表示一个等步长序列,x为起始值,y为增量步长值。如:5/15表示5,20,35,50。

6L:只在日期和星期字段使用,代表Last。在日期字段中,表示这个月份的最后一天。在星期字段中,而且前面有一个数字N,则表示这个月的最后N天。例如:6L表示该月的最后一个星期五。

7W:只出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15日最近的工作日。

8LW组合:当月的最后一个工作日。

9井号(#):只在星期字段中使用,表示当月的某个工作日。6#3表示当月的第3个星期五。

10C:只在日期和星期字段使用,计划所关联的日期。5C在日期中表示5天后的那一天,1C在星期字段中表示星期日后的第一天。

(2)CronTrigger实例

public class CronTriggerRunner {
	public static void main(String args[]) {
		try {			
			JobDetail jobDetail = new JobDetail("job1_2", "jgroup1",
					SimpleJob.class);
			CronTrigger cronTrigger = new CronTrigger("trigger1_2", "tgroup1");

			CronExpression cexp = new CronExpression("0/5 * * * * ?");
			cronTrigger.setCronExpression(cexp);
			

			SchedulerFactory schedulerFactory = new StdSchedulerFactory();
			Scheduler scheduler = schedulerFactory.getScheduler();
			scheduler.scheduleJob(jobDetail, cronTrigger);
			scheduler.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
4、使用Calendar

使用Calendar把某天排除在执行程序的时间之外:

public class CalendarExample {

    public static void main(String[] args) throws Exception {
        SchedulerFactory sf = new StdSchedulerFactory();
        Scheduler scheduler = sf.getScheduler();

        AnnualCalendar holidays = new AnnualCalendar();
        //五一劳动节
        Calendar laborDay = new GregorianCalendar();
        laborDay.add(Calendar.MONTH,5);
        laborDay.add(Calendar.DATE,1);
        holidays.setDayExcluded(laborDay, true);       
        //国庆节
        Calendar nationalDay = new GregorianCalendar();
        nationalDay.add(Calendar.MONTH,10);
        nationalDay.add(Calendar.DATE,1);
        holidays.setDayExcluded(nationalDay, true);


        scheduler.addCalendar("holidays", holidays, false, false);
        
        //从5月1号10am开始
        Date runDate = TriggerUtils.getDateOf(0,0, 10, 1, 5);
        JobDetail job = new JobDetail("job1", "group1", SimpleJob.class);
        SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1", 
                runDate, 
                null, 
                SimpleTrigger.REPEAT_INDEFINITELY, 
                60L * 60L * 1000L);
        //让Trigger遵守节日的规则(排除节日)
        trigger.setCalendarName("holidays");
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        try {
            // wait 30 seconds to show jobs
            Thread.sleep(30L * 1000L); 
            // executing...
        } catch (Exception e) {
        }            
        scheduler.shutdown(true);
    }
}
5、任务调度信息存储

当需要持久化任务调度信息,则Quartz允许用户通过调整其属性文件,将这些信息保存到数据库中。在使用数据库保存了任务调度信息后,即使系统崩溃后重新启动,任务调度信息仍将得到恢复。

(1)通过配置文件调整任务调度信息的保存策略

Quartz JAR文件的org.quartz包下包含了一个quartz。properties属性配置文件,并提供了默认设置。Quartz的属性文件主要包括:集群信息、调度器线程池、任务调度现场数据的保存。

#配置调度器的线程池
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#配置任务调度现场数据保存机制
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

默认情况下Quartz采用org.quartz.simpl.RAMJobStore保存任务的现场数据,就是保存在RAM内存中,可以通过如下设置将任务调度线程数据保存到数据库中,

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库表前缀
org.quartz.jobStore.tablePrefix = QRTZ_
#数据源名称
org.quartz.jobStore.dataSource = qzDS
#定义数据源的具体属性
org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/sampledb
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = 123456
org.quartz.dataSource.qzDS.maxConnections = 30
(2) 从数据库中恢复任务的调度  
public class JDBCJobStoreRunner {  
    public static void main(String args[]) {  
        try {  
            SchedulerFactory schedulerFactory = new StdSchedulerFactory();  
            Scheduler scheduler = schedulerFactory.getScheduler();  
            String[] triggerGroups = scheduler.getTriggerGroupNames();//获取调度器中所有的触发器组  
            for (int i = 0; i  triggerGroups.length; i++) {//重新恢复在tgroup1组中名为triggerl_1的触发器的运行  
                String[] triggers = scheduler.getTriggerNames(triggerGroups[i]);  
                for (int j = 0; j  triggers.length; j++) {  
                    Trigger tg = scheduler.getTrigger(triggers[j],triggerGroups[i]);  
                    if (tg instanceof SimpleTrigger  tg.getFullName().equals("tgroup1.trigger1_1")) {  
                        scheduler.rescheduleJob(triggers[j], triggerGroups[i],tg);  
                    }  
                }  
            }  
            scheduler.start();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}
二、在Spring中使用Quartz 1、创建JobDetail

(1)JobDetailFactoryBean

JobDetailFactoryBean扩展于Quartz的JobDetail。使用该Bean声明JobDetail时,Bean的名字即任务的名字,没有指定所属组,就使用默认组。除JobDetail中的属性,还定义了如下属性:

1jobClass:类型为Class,实现Job接口的任务类

2beanName:默认为Bean的id名,通过该属性显式指定Bean名称,它对应任务的名称。

3jobDataAsMap:类型为Map,为任务所对应的JobDataMap提供值。

4applicationContextJobDataKey:将ApplicationContext的引用保存到JobDataMap中,以便在Job的代码中访问ApplicationContext。但是只有指定一个键,用于在jobDataAsMap中保存ApplicationContext。如果不设置此键,JobDetailBean不会将ApplicationContext放入JobDataMap中。

5jobListenerNames:类型为String[],指定注册在Scheduler中的JobListeners名称,以便让这些监听器对本任务的事件进行监听。

bean name="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"  
        p:jobClass="com.smart.quartz.MyJob"  
        p:applicationContextJobDataKey="applicationContext"  
        property name="jobDataAsMap"  
            map  
                entry key="size" value="10" /  
            /map  
        /property  
/bean

(2)、MethodInvokingJobDetailFactoryBean

MethodInvokingJobDetailFactoryBean可以将一个Bean的某个方法封装成满足Quartz要求的Job。如下:

bean id="jobDetail_1"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
		p:targetObject-ref="myService" p:targetMethod="doJob" p:concurrent="false" /
concurrent属性指定任务类型。默认情况下封装为无状态的任务。如果需要封装为由状态的任务,需要将concurrent属性设置为false。通过MethodInvokingJobDetailFactoryBean产生的JobDetail不能被序列化,所以不能被持久化到数据库中。 2、创建Trigger

(1)SimpleTriggerFactoryBean

SimpleTriggerFactoryBean配置的Trigger名称即为Bean的名称,属于默认组。SimpleTriggerFactoryBean中的属性有:

jobDetail:对应的JobDetail

beanName:默认为Bean的id名,对应Trigger的名称。

jobDataAsMap:以Map类型为Trigger关联的JobDataMap提供值。

startDelay:延迟多少时间开始触发,单位为毫秒,默认为0。

triggerListenerNames:类型为String[],指定注册在Scheduler中的TriggerListener名称,以便让这些监听器对本触发器的事件进行监听。

bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"
		p:jobDetail-ref="jobDetail" p:startDelay="1000" p:repeatInterval="2000"
		p:repeatCount="100"
		property name="jobDataAsMap"
			map
				entry key="count" value="10" /
			/map
		/property
	/bean
(2)CronTriggerFactoryBean

bean id="checkImagesTrigger" 
	      class="org.springframework.scheduling.quartz.CronTriggerBean"
	      p:jobDetail-ref="jobDetail"
	      p:cronExpression="0/5 * * * * ?"/
3、创建Scheduler

SchedulerFactoryBean的配置

bean id="scheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
		property name="triggers"
			list
				ref bean="simpleTrigger" /
			/list
		/property
		property name="schedulerContextAsMap"
			map
				entry key="timeout" value="30" /
			/map
		/property
		property name="quartzProperties"
			props
				prop key="org.quartz.threadPool.class"
					org.quartz.simpl.SimpleThreadPool
				/prop
				prop key="org.quartz.threadPool.threadCount"10/prop
			/props
		/property
	/bean











最新回复(0)
/jishuohvF1FgRVUc52dx1WMqHJuqLBTBh3n5IOiPvF_2FqA8Uk_3D4858267
8 简首页