Spring与Quartz的整合实现定时任务调度

时间:2021-10-15 07:53:20

Quartz集成Spring的2个方法

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

1. JobDetailBean.

2. MethodInvokeJobDetailFactoryBean.

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

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

1、使用 MethodInvokeJobDetailFactoryBean

applicationTask.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" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <!-- 定时任务 下游回调任务-->
    <!-- ======================== 1、定义需要执行的任务类。 ======================== -->
    <bean id="notifyQuartz" class="com.hsmpay.mobile.timerTask.NotifyTimerAction">
    </bean>
    <!-- ======================== 2、将需要执行的定时任务注入JOB中。 ======================== -->
    <bean id="notifyQuartzDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
         <!--引入执行任务的类-->
        <property name="targetObject"><ref bean="notifyQuartz"/></property>
        <!--定义要执行具体类的方法-->
        <property name="targetMethod"><value>remoteNotify</value></property>
    </bean>
    <!-- ======================== 3、调度触发器 ======================== -->
    <bean id="notifyTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">

        <property name="jobDetail"><ref bean="notifyQuartzDetail"/></property>

        <property name="cronExpression"> <!--每五分钟执行一次-->
            <!--<value>0 08 10 * * ?</value>-->
            <value>0 0/5 * * * ?</value><!--<value>秒 分 时 天 月 周 年</value> 0 55 17 ? * MON-FRI-->
        </property>
    </bean>
    <!--================================================================================================-->

    <!-- 半分钟执行定时任务 -->
    <!-- ======================== 1、定义需要执行的任务类。 ======================== -->
    <bean id="messageTask" class="com.hsmpay.mobile.timerTask.MessageTask"></bean>
    <!-- ======================== 2、将需要执行的定时任务注入JOB中。 ======================== -->
    <bean id="oneMessageRollBackTaskJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject"><ref bean="messageTask"/></property>
        <property name="targetMethod"><value>oneTaskJob</value></property>
    </bean>
    <!-- ======================== 3、调度触发器 ======================== -->
    <bean id="oneMessageStreamRollBackTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail">
            <ref bean="oneMessageRollBackTaskJobDetail"/>
        </property>
        <property name="cronExpression">
            <value>*/30 * * * * ?</value>
        </property>
    </bean>



    <!-- ======================== 4、调度工厂 ======================== -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="notifyTrigger"/>
            </list>
        </property>
    </bean>

    <bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="10" />
        <!-- 线程池维护线程所允许的空闲时间 -->
        <property name="keepAliveSeconds" value="200" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="50" />
        <!-- 线程池所使用的缓冲队列 -->
        <property name="queueCapacity" value="100" />
        <property name="rejectedExecutionHandler">
            <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
            <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
            <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
            <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
    </bean>
</beans>

相关代码配置

@Controller("notifyTimerAction")
public class NotifyTimerAction {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Resource(name="transOrderService")
    TransOrderService transOrderService;//查询订单service
    //设置核心池大小
    int corePoolSize = 5;
    //设置线程池最大能接受多少线程
    int maximumPoolSize=20;
    //当前线程数大于corePoolSize、小于maximumPoolSize时,超出corePoolSize的线程数的生命周期
    long keepActiveTime = 200;
    //设置时间单位,秒
    TimeUnit timeUnit = TimeUnit.SECONDS;

    public String url ="http://localhost:8080/mobile/forwardPort/port.action";


    /**
     * 下游回调通知,继续发送
     */
    public void remoteNotify() throws Exception{
        log.debug("test定时任务启动了!!!!!!!!");
        System.out.println("test定时任务启动了!!!!!!!!");
        TransOrder order=new TransOrder();
        order.setStatus(1);//设置成功状态
        order.setClientType(7);//设置客户类型 7外放接口类型
        order.setOrderTypeId(1L);//收款类型
        //设置线程池缓存队列的排队策略为FIFO,并且指定缓存队列大小为5
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(5);
        //创建ThreadPoolExecutor线程池对象,并初始化该对象的各种参数
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepActiveTime, timeUnit,workQueue);
        try {
            List<TransOrder> orderList = transOrderService.searchEntityList(order);
            Map<String,Object> map=new HashMap<String,Object> ();
            //准备给下游发送数据
            for(TransOrder transOrder:orderList){
            Thread thread=new Thread(new Runnable() {
                public void run() {
                map.put("status",transOrder.getStatus());//交易状态
                map.put("orderNum",transOrder.getOrderNum());//订单号
                JSONObject jsonObject = JSONObject.fromObject(map);
                HttpClientUtil.submitPost(transOrder.getOtherData(), map.toString(), "UTF-8",60000, 60000);
                }
            });
              executor.execute(thread);
            }
            executor.shutdown();//关闭线程池

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }

    }

}

==================================================================================================================================================================

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

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

三、两种方法的说明

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

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

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