spring+quartz兩種整合方式:代碼建立job+xml配置建立job

quartz交流QQ羣:77383408,喜歡的同行一塊兒來探討問題吧

 

最近在項目中須要用到quartz,開始使用的xml配置建立的job,一切ok。後來以爲每次添加任務都要寫一大段xml,就將job放入了數據庫,在spring啓動時去啓動數據庫中保存的全部job。java

其中遇到問題,沒法注入spring管理的bean。本文是解決方案,研究了幾天,終於找到緣由了!spring

 

首先,本文實現使用的是內存型,沒有持久化到數據庫。數據庫

1、quartz.properties文件

 #調度器名,可有可無,名字任意定
 org.quartz.scheduler.instanceName = XXScheduler
 org.quartz.scheduler.instanceId = AUTO
 #============================================================================
 # Configure ThreadPool   配置數據庫鏈接池
 #============================================================================
 org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
 org.quartz.threadPool.threadCount = 12
 org.quartz.threadPool.threadPriority = 5
 #============================================================================
 # Configure JobStore  配置作業存儲方式
 #============================================================================
 #至關於掃描頻率,若是系統基於秒級,應培植成1000,quartz默認爲分級(60000)
 org.quartz.jobStore.misfireThreshold = 1000
 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

 

2、建立任務Job

方法一:使用xml方式配置job

<!-- 自定義quartz的jobFactory,將spring管理bean放到jobFactory,這樣才能在job類裏面經過註解注入bean -->   
<bean id="jobFactory" class="com.cdrzt.pcs.timer.factory.MyJobFactory"></bean>
	
<!-- 總管理類 若是將lazy-init='false'那麼容器啓動就會執行調度程序  -->  
<bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
	<property name="jobFactory" ref="jobFactory"></property>
	<property name="configLocation" value="classpath:quartz.properties" />
	<!-- 管理trigger -->
	<property name="triggers">  
	     <list>  
	         <ref bean="trigger_1"/>  
	     </list>  
	</property>  
</bean> 
	
<!-- ********定時器1  ******** -->  
<!-- 定義jobDetail -->  
<bean id="detail_1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.xx.xx.xx.JobClassTest" /><!-- 這裏指定job任務類 -->
    <property name="durability" value="true" />
    <property name="group" value="group" />
    <property name="name" value="name" />
</bean>  
<!-- 定義trigger -->  
<bean id="trigger_1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
    <property name="jobDetail" ref="detail_1"></property>
    <property name="cronExpression">  
        <value>0/1 * * * * ?</value><!-- 測試使用每一秒運行一次 -->  
    </property>  
</bean>

好了,至此使用xml方式配置job就所有完畢了。app

其中值得注意的是下面兩句代碼:自定義MyJobFactory,在job類裏面才能夠注入spring的service。ide

<bean id="jobFactory" class="com.cdrzt.pcs.timer.factory.MyJobFactory"></bean>
<property name="jobFactory" ref="jobFactory"></property>
public class MyJobFactory extends AdaptableJobFactory {

	// 這個對象Spring會幫咱們自動注入進來,也屬於Spring技術範疇.
	@Autowired
	private AutowireCapableBeanFactory capableBeanFactory;

	protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
		// 調用父類的方法
		Object jobInstance = super.createJobInstance(bundle);
		// 進行注入,這屬於Spring的技術,不清楚的能夠查看Spring的API.
		capableBeanFactory.autowireBean(jobInstance);
		return jobInstance;
	}
}
/** 
* Job任務類,實現Job接口,能夠成功注入service
*/
public class JobClassTest implements Job {

	@Autowired
	private ProductBaseDao productBaseDao;

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		ProductBase p = productBaseDao.load(ProductBase.class, 2);
		System.out.println("注入service成功!查詢結果爲:"+p);
	}
}

至此,啓動容器,job成功啓動。測試

 

方式二:使用代碼建立Job

表結構:本示例只須要一張表便可!ui

數據庫有了記錄,那麼咱們就須要在spring容器啓動後,將全部記錄獲取出來,並經過代碼建立每個job。spa

<!-- 在Spring容器將全部的Bean都初始化完成以後的操做 -->
<bean class="com.xx.xx.timer.InstantiationTracingBeanPostProcessor"/>

在applicationContext.xml配置文件最末加上這一句,便可在spring啓動後,去執行指定類中的方法。code

/**
* 在Spring容器將全部的Bean都初始化完成以後的操做
*/
public class InstantiationTracingBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
	
    @Autowired
    private TimerJobService timerJobService;
    @Autowired
    private MyJobFactory myJobFactory;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    	// 避免onApplicationEvent方法被執行兩次
    	if(event.getApplicationContext().getParent() == null){
    	    try {
                // 獲取Scheduler對象,並自定義jobFactory
    		Scheduler scheduler = QuartzUtil.getInstance();
    		scheduler.setJobFactory(myJobFactory);
        		
        	// 查詢全部正常狀態的定時任務,並在容器啓動後,啓動任務 
        	List<TimerJob> jobs = timerJobService.getNormalList();
        	for(TimerJob record : jobs){	
        		Integer id = record.getId();
        		String name = record.getJobName()+"_"+id;
        		String group = record.getJobGroup()+"_"+id;
        			
        		// 加載job類
    			Class<? extends Job> clazz = null;
    			try {
    				clazz = (Class<? extends Job>) Class.forName(record.getClassName());
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			}
    				
        		//生成jobDetail
    			JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(name, group).build();
    				
    		        //表達式調度構建器
    			CronScheduleBuilder cornSB= CronScheduleBuilder.cronSchedule(record.getCronExpression());
    			//生成觸發器
    			CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(cornSB).build();
    				
    			//添加job
    			scheduler.scheduleJob(jobDetail, trigger);
        	}
        		
        	//開始執行shceduler
    		scheduler.start();
    		
	    } catch (Exception e) {
		e.printStackTrace();
	    }
       }
    }
}

其中建立Scheduler實例方法爲:component

public class QuartzUtil {
    private static SchedulerFactory ssf = new StdSchedulerFactory();
    
     /**
     * 獲取Scheduler實例,使用工廠模式獲取
     * @return
     */
    public static Scheduler getInstance(){
    	Scheduler sched = null;
    	try {
    		sched = ssf.getScheduler();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
    	return sched;
    }
}

其中關鍵代碼爲:scheduler.setJobFactory(myJobFactory) ;同xml配置同樣,須要指定自定義的JobFactory。

最開始,我是這樣設置的:scheduler.setJobFactory(new MyJobFactory());

但是這樣的結果就是,任務所有啓動了,但是在job任務類注入不了bean。思考了好久,才忽然發現, MyJobFactory是使用new關鍵字實例化出來的,在spring中,本身new出來的都不會交給spring的context去管理!!!

既然找到問題所在,解決就簡單多了,只須要將MyJobFactory交給spring中去,再在上文經過註解注入便可!

在MyJobFactory.java類最上面添加@component註解,啓動時spring去掃描組件會將該類注入。

@Component
public class MyJobFactory extends AdaptableJobFactory {......}

 

至此,問題所有解決,兩種建立job的方法,我的以爲第二種比較簡單。須要新增任務時,只須要在數據庫添加一條記錄,再添加一個對應的job類便可。

有了這一張表,一樣能夠配置後臺頁面,實現對任務的控制,這裏就不講啦!

最後,歡迎喜歡quartz的人,加入QQ羣:77383408

把你的問題說出來集思廣益,避免你們重蹈覆轍。

相關文章
相關標籤/搜索