spring 4 quartz 2.x动态配置triggers

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 4 quartz 2.x动态配置triggers相关的知识,希望对你有一定的参考价值。

<bean name="scheduler" lazy-init="false"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="applicationContextSchedulerContextKey"
value="applicationContextKey" />
<property name="autoStartup" value="true" />
<property name="jobDetails">
<list>
<ref bean="jobDetail" />
</list>
</property>
<property name="configLocation" value="classpath:/config/quartz.properties" />
</bean>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.service.quartz.impl.MyQuartzJobBean" />
<property name="durability" value="true" />
</bean>
我的配置文件,可以保存到数据库表中,但是不执行任务
可以新建定时任务到数据库表中,但是不执行定时任务!
@Controller
public class HomeController

@Autowired
QuartzService quartzService;
@Autowired
private Scheduler scheduler;
@RequestMapping("home")
public ModelAndView home2()
return new ModelAndView("home");

@RequestMapping("quartz")
public void quartz()
quartzService.schedule("0/5 * * * * ?");
return ;


spring 4.x没有配置过,这里有一个spring 3.x 的quartz定时配置,你参考参考:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- 
1 、JobDetail:JobDetail是一个具体的类。
    2、Trigger:触发器,它用于定义Job何时执行。最常用的是SimpleTrigger和CronTrigger。
                   一般来说,如果你需要在一个固定的时间和重复次数或者一个固定的间隔时间,那么SimpleTrigger比较合适;
                  如果你有许多复杂的作业调度,那么CronTrigger比较合适。 
      CronTrigger和 Unix的cron机制基本一样,我们需要的只是一个cron表达式。
                  比如“0 0 12 * * ?”会在每天中午12点触发执行;“0 15 10 ? * 6L”会在每个月的最后一个星期五的早上10:15触发Job执行。
    3、 Scheduler和 SchedulerFactory:Scheduler负责管理 Trigger、调度 Job,
     SchedulerFactory则是Scheduler工厂,负责生成Scheduler。 
-->

<!--  总管理类如果将lazy-init='false'那么容器启动就会执行调度程序   -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<!-- 通过applicationContextSchedulerContextKey属性配置spring上下文  [此属性可省略] -->
<property name="applicationContextSchedulerContextKey">
<value>applicationContext</value>
</property>
<property name="triggers">
<list>
<!--作业调度器,list下可加入其他的调度器-->
<ref bean="testTrigger" />
</list>
</property>
<property name="autoStartup" value="true" />
</bean>

<!--要调度的对象-->
<bean id="testJob" class="name.zealze.test.JobTest" />

<bean id="testTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="testJobDetail" />
<!-- 按cron表达式时间点触发事件 http://cron.qqe2.com/ -->
<property name="cronExpression" value="0 0/2 * * * ?" />
</bean>

<bean id="testJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="testJob" />
<property name="targetMethod" value="execute" />
<!-- 是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程 -->
<property name="concurrent" value="false" />
</bean>

</beans>

追问

这个在spring4里也是可以的,但我需要配置动态的

参考技术A <bean id="scheduledJob" class="com.web.app.schedule.handlers.CustomScheduledJob">
<property name="schedulerFactory" ref="scheduledJobFactory"/>
<property name="triggerKey" ref="scheduledTrigger"/>
</bean>

<bean id="scheduledJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="scheduledJob"/>
<property name="targetMethod" value="doScheduledJob"/>
<property name="concurrent" value="false"/>
</bean>

<bean id="scheduledTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="scheduledJobDetail"/>
<property name="cronExpression">
<value>*/10 * * * * ?</value>
</property>
</bean>

<!-- Add scheduled job to project -->
<bean id="scheduledJobFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="scheduledTrigger"/>
</list>
</property>
</bean>12345678910111213141516171819202122232425261234567891011121314151617181920212223242526

在CustomScheduledJob类中注入schedulerFactory和triggerKey,那么就可以在这个类中修改定时器的时间了。
然而添加多个定时器任务后,随之而来是的项目启动时,SpringBean装载时异常:
org.springframework.beans.factory.BeanCreationException:…Cannot resolve reference to bean ‘scheduledJobFactory’ while setting bean property ‘schedulerFactory’…
不难看出这是在创建定时器时需要scheduledJobFactory,而scheduledJobFactory的triggers的list里有多个触发器,这些触发器都需要实例化,由此出现了嵌套,导致了创建Bean的异常发生,那该如何修正呢?
既然是创建时的嵌套导致的,那我们就要解除嵌套,即在初始化定时器时不要立即传入scheduledJobFactory的Bean,那么把定时器的配置中的ref改成value,如下:
<bean id="scheduledJob" class="com.web.app.schedule.handlers.CustomScheduledJob">
<property name="schedulerFactory" value="scheduledJobFactory"/>
<property name="triggerKey" value="scheduledTrigger"/>
</bean>12341234

那么问题又来了,我们该如何通过value取得spring的Bean呢?
我们可以通过实现接口ApplicationContextAware方式,重写setApplicationContext()的方法,即可取得Spring的Context,然后用getBean()就可以取得Spring容器里的Bean了。
为了把这个问题处理得更漂亮些,可以用一个抽象类去做这些事情,把其中需要的实现的定时任务方法写成抽象方法,每个定时器去继承此抽象类就可以了,抽象类的写法如下:
package com.web.app.schedule;

import java.text.ParseException;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;

public abstract class AbstractScheduledJob implements ApplicationContextAware

private static ApplicationContext applicationContext;

protected String schedulerFactory;

protected String triggerKey;

@Override
public void setApplicationContext(ApplicationContext appCtx) throws BeansException
applicationContext = appCtx;


public void setSchedulerFactory(String schedulerFactory)
Assert.notNull(schedulerFactory, "Scheduler factory can not be null");
this.schedulerFactory = schedulerFactory;


public void setTriggerKey(String triggerKey)
Assert.notNull(triggerKey, "Trigger key can not be null");
this.triggerKey = triggerKey;


public void resetScheduledJob(String cronExpression) throws SchedulerException, ParseException
Scheduler schedulerFactory = (Scheduler) getObject(this.schedulerFactory);
CronTriggerImpl cronTrigger = (CronTriggerImpl) getObject(this.triggerKey);
String originCronExpression = cronTrigger.getCronExpression();

if (!originCronExpression.equalsIgnoreCase(cronExpression))
cronTrigger.setCronExpression(cronExpression);
schedulerFactory.rescheduleJob(cronTrigger.getKey(), cronTrigger);



public abstract void doScheduledJob();

protected Object getObject(String name)
return applicationContext.getBean(name);


12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152531234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

自定义的定时器里实现需要处理的计划任务逻辑,写法如下:
package com.web.app.schedule.handlers;

import com.web.app.schedule.AbstractScheduledJob;

public class CustomScheduledJob extends AbstractScheduledJob

@Override
public void doScheduledJob()
// TODO
// 需要处理的计划任务逻辑


Quartz总结:Quartz集成Spring的2个方法

零、引言

关于Spring集成Quartz有2种方法:

1. JobDetailBean.

2. MethodInvokeJobDetailFactoryBean.

以下从自身使用和理解以及掌握的知识对其进行阐述。

需要注意的是,在使用Spring集成Quartz的时候,一定不要忘记引入spring-support这个包:

        <!-- spring-support.jar 这个jar 文件包含支持UI模版(Velocity,FreeMarker,JasperReports),邮件服务,脚本服务(JRuby), 缓存Cache(EHCache),任务计划Scheduling(uartz)方面的类。 
            外部依赖spring-context, (spring-jdbc, Velocity, FreeMarker, JasperReports, BSH, Groovy, JRuby, Quartz, EHCache) -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.2.6.RELEASE</version>
        </dependency>

它协助Spring集成了很多有用的第三方库,包括了邮件服务、定时任务、缓存等。。。

当然也不要忘记引入Quart的jar包:

        <!-- quartz定时任务 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>

一、JobDetailBean

1. 创建一个Job方法,此方法必须继承QuartzJobBean或者实现Job方法。

public class TestJob extends QuartzJobBean {
    
    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println(TimeUtils.getCurrentTime());
    }
}

2. XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.mc.bsframe.job.TestJob"></property>
        <property name="durability" value="true"></property>
    </bean>

    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail" />
        <property name="startDelay" value="3000" />
        <property name="repeatInterval" value="2000" />
    </bean>

    <!-- 总管理类 如果将lazy-init=‘false‘那么容器启动就会执行调度程序 -->
    <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 管理trigger -->
        <property name="triggers">
            <list>
                <ref bean="simpleTrigger" />
            </list>
        </property>
    </bean>
</beans>

二、MethodInvokeJobDetailFactoryBean

1. 创建一个Job类,此类不需要继承任何类或者实现任何接口:

package com.mc.bsframe.job;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.SimpleTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;

import com.mc.bsframe.service.TestService;
import com.mc.bsframe.util.TimeUtils;


public class TestJob2 {
    
    public void doSomething() {
        System.err.println("****:" + TimeUtils.getCurrentTime());
    }

}

2. 配置XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 要执行任务的任务类。 -->
    <bean id="testQuartz" class="com.mc.bsframe.job.TestJob2">
    </bean>

    <!-- 将需要执行的定时任务注入JOB中。 -->
    <bean id="testJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="testQuartz"></property>
        <!-- 任务类中需要执行的方法 -->
        <property name="targetMethod" value="doSomething"></property>
        <!-- 上一次未执行完成的,要等待有再执行。 -->
        <property name="concurrent" value="false"></property>
    </bean>

    <!-- 基本的定时器,会绑定具体的任务。 -->
    <bean id="testTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="testJob"></property>
        <property name="startDelay" value="3000"></property>
        <property name="repeatInterval" value="2000"></property>
    </bean>

    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="testTrigger"></ref>
            </list>
        </property>
    </bean>
</beans>

综上:定时任务的基本配置完成。

三、两种方法的说明

使用QuartzJobBean,需要继承。而使用MethodInvokeJobDetailFactoryBean则需要指定targetObject(任务实例)和targetMethod(实例中要执行的方法)

后者优点是无侵入,业务逻辑简单,一目了然,缺点是无法持久化(目前还不太清楚这点!)

从我使用的经验来说,我更推荐的第二种其中一个很重要的原因就是因为定时任务中注入相关Service的时候,后者可以直接注入,而前者还需要进行Schedular的替换修改。

 

以上是关于spring 4 quartz 2.x动态配置triggers的主要内容,如果未能解决你的问题,请参考以下文章

在spring中如何配置quartz使得quartz能动态 我邮箱:chenxing19900106@163.com

Spring Boot Quartz 动态配置,持久化

spring boot 整合 quartz 集群环境 实现 动态定时任务配置原

quartz spring 实现动态定时任务

使用Quartz框架集成Spring,动态配置定时任务(个人思考)

使用Quartz框架集成Spring,动态配置定时任务(个人思考)