spring+quartz两种整合方式:代码创建job+xml配置创建job

quartz交流QQ群:77383408,喜欢的同行一起来探讨问题吧


最近在项目中需要用到quartz,开始使用的xml配置创建的job,一切ok。后来觉得每次添加任务都要写一大段xml,就将job放入了数据库,在spring启动时去启动数据库中保存的所有job。

其中遇到问题,无法注入spring管理的bean。本文是解决方案,研究了几天,终于找到原因了!



首先,本文实现使用的是内存型,没有持久化到数据库。

一、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


二、创建任务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就全部完毕了。

其中值得注意的是下面两句代码:自定义MyJobFactory,在job类里面才可以注入spring的service。

<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

表结构:本示例只需要一张表即可!

数据库有了记录,那么我们就需要在spring容器启动后,将所有记录获取出来,并通过代码创建每一个job。

<!-- 在Spring容器将所有的Bean都初始化完成之后的操作 -->
<bean class="com.xx.xx.timer.InstantiationTracingBeanPostProcessor"/>

在applicationContext.xml配置文件最末加上这一句,即可在spring启动后,去执行指定类中的方法。

/**
* 在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实例方法为:

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

把你的问题说出来集思广益,避免大家重蹈覆辙。






你可能感兴趣的:(spring+quartz两种整合方式:代码创建job+xml配置创建job)