您的位置:首页 > 编程语言 > Java开发

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

2017-07-30 00:00 519 查看
摘要: 版本:Spring4.2.0.RELEASE、quartz-2.2.2-distribution 使用代码创建job无法注入spring的bean的解决方案

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类即可。

有了这一张表,同样可以配置后台页面,实现对任务的控制,这里就不讲啦!

最后,给同学们推荐一款我自己用的机械手感键盘,主要是好用又便宜,程序员就是要对自己好一点的嘛~

https://item.taobao.com/item.htm?spm=686.1000925.0.0.3f11c9edQk7p4n&id=556142108693



最后,欢迎喜欢quartz的人,加入QQ群:77383408

把你的问题说出来集思广益,避免大家重蹈覆辙。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息