spring集成quartz:定時任務的實現

1、前言

在開發中經常會使用定時任務來完成一些掃描任務,比如掃描過期的訂單、定時的發佈等等。但是在使用定時任務的同時,會調用spring容器的中的一些服務,當注入的時候,總是會報錯,或者注入的服務爲空。今天記錄一下字節在spring集成quzrtz的測試結果。

2、需要的依賴

  <!-- spring核心 -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.3.RELEASE</version>
  </dependency>

  <!-- spring集成web:非必須(這裏直接啓動tomcat加載spring容器,所以需要配置) -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>4.1.3.RELEASE</version>
 </dependency>

  <!-- spring對事務的只支持:quartz運行時需要(沒有會直接報錯,原因暫時不清楚) -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>4.1.3.RELEASE</version>
  </dependency>

  <!-- spring對第三方插件包的支持 -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>3.2.9.RELEASE</version>
  </dependency>

  <!-- 定時任務的依賴 -->
  <dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
 </dependency>

3、配置web.xml

<!-- 初始化容器 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-quartz.xml</param-value>
</context-param>
<!-- 配置spring容器的核心監聽器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

4、實現方式

quartz定時任務的實現主要分三步走:①創建job任務 ②創建觸發器 ③註冊調度器
quartz的實的方式主要有兩種:

  1. 第一種:利用JobDetailFactoryBean包裝的QuartzJobBean,自定義job繼承QuartzJobBean,重寫executeInternal()即可。但是如果想自動注入spring的服務,就需要自定義jobFactory繼承AdaptableJobFactory 。

  2. 第二種:直接創建Job類,無須繼承、實現,直接配置MethodInvokingJobDetailFactoryBean即可。但需要指定一下兩個屬性:

 targetObject:  指定包含任務執行體的Bean實例。(也可以指定targetBeanName,兩者選其一)
 targetMethod:   指定將指定Bean實例的該方法包裝成任務的執行體。 

5、spring繼承quartz實現方式一

<!-- 註解掃描 -->
<context:component-scan base-package="ws.quartz.spring.service"></context:component-scan>

<!-- 
    重要:spring容器中的service注入的關鍵
    自定義jobFactory繼承AdaptableJobFactory。此代碼可以實現service的自動注入
 -->
<bean id="myJobFactory" class="ws.quartz.spring.MyJobFactory"></bean>

<!-- 定義任務 :jobClass和durability必須配置 -->
<bean id="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <!-- 具體的job任務 :必須 -->
    <property name="jobClass" value="ws.quartz.spring.Myjob"/>
    <!-- 指定任務名稱 -->
    <property name="name" value="myJob"/>
    <!-- 指定job的分組 -->
    <property name="group" value="myJobs"/>
    <!-- 
        默認爲false,表示沒有任務的觸發器與 之相關的會在觸發器中刪除該任務。
        重要:這裏必須設置爲true ,否則報錯。
    -->
    <property name="durability" value="true" />
    <!--
         指定spring的key,要不然獲取不到spring容器,就無法使用容器中的對象
         注意:這裏是手動裝配。通過spring容器的getBean()獲取服務。
     -->
    <property name="applicationContextJobDataKey" value="applicationContext"/>
</bean>

<!-- 定義觸發器 -->
<bean id="testJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="myJobDetail"/>
    <property name="cronExpression" value="0/5 * * * * ?"/>
</bean>

<!-- 註冊觸發器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <!-- 註冊自定義的jobFactory -->
    <property name="jobFactory" ref="myJobFactory" />
    <property name="triggers">
        <list>
            <ref bean="testJobTrigger" />
        </list>
    </property>
</bean>

6、代碼實現一

/*
    MyJobFactory:實現自動注入 
*/
public class MyJobFactory extends AdaptableJobFactory {

    //這個對象Spring會幫我們自動注入進來,也屬於Spring技術範疇.
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //調用父類的方法
        Object jobInstance = super.createJobInstance(bundle);
        //進行注入:使用Autowired注入實例
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

/*
    Job實現:
*/
public class Myjob extends QuartzJobBean {

    @Autowired
    private HelloService helloService;

    @Override
    protected void executeInternal(JobExecutionContext context)throws JobExecutionException {
        System.out.println("定時任務開始執行了..." + context.getTrigger().getKey().getName()+ "----" +new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        //手動獲取spring容器
        ApplicationContext applicationContext = (ApplicationContext) context.getJobDetail().getJobDataMap().get("applicationContext");  
        System.out.println("獲取到的Spring的容器是: " + applicationContext);

        //手動裝配,獲取服務
        //helloService = (HelloService) applicationContext.getBean("helloService");

        System.out.println(helloService);
        helloService.sayHello();

    }
}

7、spring繼承quartz實現方式二

<!--
    該方式的實現,不需要在定義jobFactory
-->

<context:component-scan base-package="ws.quartz.spring.service"></context:component-scan>

<bean id="myjobMethod" class="ws.quartz.spring.MyjobMethod" />

<!-- 定義任務 :注意這裏的 MethodInvokingJobDetailFactoryBean -->
<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <!-- <property name="targetBeanName" value="myjobMethod"/> 可以替換的 -->
    <property name="targetObject" ref="myjobMethod"/>
    <property name="targetMethod" value="doJob"/>

    <!-- 時間到了,但是任務還沒有執行完。是否同時執行。false表示等待任務結束後執行,true表示時間到了就執行 -->
    <property name="concurrent" value="false" />
</bean>

<!-- 定義觸發器 -->
<bean id="testJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="myJobDetail"></property>
    <property name="cronExpression" value="0/5 * * * * ?"></property>
</bean>

<!-- 註冊觸發器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="testJobTrigger" />
        </list>
    </property>
</bean>

8、代碼實現二

/*
    這種方式的實現沒有代碼的侵入性
*/
public class MyjobMethod {

    @Autowired
    private HelloService helloService;

    public void doJob(){

        System.out.println("定時任務開始執行了..." + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

        System.out.println(helloService);
        helloService.sayHello();
    }
}

9、效果
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章