Quartz使用总结

声明:这里参考了许多博客,加上自身的使用总结。如果您是知识的原创者,可以在评论区写上您的博文链接~

一、删除任务的顺序

delete from qrtz_cron_triggers; #存放cron类型的触发器
delete from qrtz_simple_triggers; #简单触发器的信息
delete from qrtz_triggers; #触发器的基本信息
delete from qrtz_job_details; #存放任务的详细信息
delete from qrtz_scheduler_state; #调度器状态
delete from qrtz_locks; #存储程序的悲观锁的信息

二、注入Service失败原因分析

1. 这个Job是由quartz实例化出来的,不受Spring的管理,所以就导致注入失败。
	
2. 在quartz框架中,Job 是通过反射出来的实例,不受spring的管理。
Scheduler现在交给Spring生成,在Spirng-context-support的jar包下
org.springframework.scheduling.quartz包中有个SpringBeanJobFactory的类,
job实例通过该类的createJobInstance方法创建。根据Scheduler context、job data map and trigger data map填充其属性。
但是创建的job实例并没被spring管理,这就需要我们自定义一个类将创建的job添加到applicationContext中,
该类需要继承SpringBeanJobFactory,并实现ApplicationContextAware接口。
	
3. ApplicationContextAware接口的作用:Spring容器会检测容器中的所有Bean,
如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,
自动调用该Bean的setApplicationContextAware()方法,调用该方法时,
会将容器本身作为参数传给该方法——该方法中的实现部分将Spring传入的参数(容器本身)赋给
该类对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。

三、解决自动注入的问题

  1. 新增一个类
public class CustomJobFactory extends SpringBeanJobFactory{
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}
  1. 修改spring的配置文件
<!-- 定时任务配置 start -->
<bean id="customJobFactory" class="com.gxuwz.quartz.CustomJobFactory"></bean>
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="dataSource" ref="dataSource"></property>
  <!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
  <property name="overwriteExistingJobs" value="true" />
  <!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
  <property name="startupDelay" value="10" />
  <!-- 设置自动启动 -->
  <property name="autoStartup" value="true" />
  <property name="jobFactory" ref="customJobFactory"></property>
  <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
  <property name="configLocation" value="classpath:spring-quartz.properties" />
</bean>  
<!-- 定时任务配置 end -->

四、常用的两个注解

@DisallowConcurrentExecution
禁止并发执行多个相同定义的JobDetail, 这个注解是加在Job类上的, 但意思并不是不能同时执行多个Job, 
而是不能并发执行同一个Job Definition(由JobDetail定义), 但是可以同时执行多个不同的JobDetail。
即对于同一个Job任务不允许并发执行,但对于不同的job任务不受影响。
@PersistJobDataAfterExecution
保存在JobDataMap传递的参数。加在Job上,表示当正常执行完Job后, JobDataMap中的数据应该被改动, 以被下一次调用时用。

五、提供一个模板

@Component
@DisallowConcurrentExecution
public class MyTimer implements Job {
	
	private Scheduler scheduler;
	@Autowired
	private IHelloService helloService;

	public MyTimer() { super(); }
	
	@Autowired
	public void setScheduler(Scheduler scheduler) {
		this.scheduler = scheduler;
		start();
	}
	public Scheduler getScheduler() {
		return scheduler;
	}

	@Override
	public void execute(JobExecutionContext arg0) throws JobExecutionException {
		helloService.say();
	}

	public void start() {
		// 1.创建JobDetail

		// 2.创建Trigger,如果使用了cron表达式,那么startNow()和startAt(date)方法会失效

		// 3.执行任务
		if(!scheduler.checkExists(jobDetail.getKey())){				
			scheduler.scheduleJob(jobDetail, trigger);
		}
	}
}

六、执行定时任务时,报了一个错误

"Could not obtain transaction-synchronized Session for current thread"

分析

1. quartz是启动子线程去执行的

2. SessionFactory的getCurrentSession并不能保证在没有当前Session的情况下会自动创建一个新的,
这取决于CurrentSessionContext的实现,SessionFactory将调用CurrentSessionContext的currentSession()方法来获得Session。
在Spring中,如果我们在没有配置事务并且没有事先调用SessionFactory.openSession()的情况直接调用getCurrentSession(),
那么程序将抛出"No Session found for current thread"异常。如果配置了事务并且通过@Transactional或者声明的方式
配置的事务边界,那么Spring会在开始事务之前通过AOP的方式为当前线程创建Session,此时调用getCurrentSession()将得到正确结果。

解决方案

使用声明式事务或者注解式事务,方法需要在事务内执行