Spring中使用Quartz调度线程

最近项目中采用spring的quartz调度自动发送邮件业务,应用JobDetailBean实现,在应用过程中发现并发情况下对事务控制做得不够好,看如下代码:
<!-- 定时自动发送邮件的配置 -->
	<!-- JobDetailBean方式,任务是无状态的,导致多个线程并发执行,产生业务重复操作,改用下面MethodInvokingJobDetailFactoryBean方式实现	-->	
	<!--
	 <bean id="autoSendEmailJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
		<property name="jobClass">
			<value>cn.xxx.videotrackerlive.service.takedown.quartztimer.AutoTakedownEmailCronTrigger</value>
		</property>
		<property name="jobDataAsMap">
			<map>
				<entry key="accountService">
					<ref bean="accountService" />
				</entry>
				<entry key="userService">
					<ref bean="userService" />
				</entry>				
				<entry key="takedownNoticeService">
					<ref bean="takedownNoticeService" />
				</entry>
				<entry key="autoTakedownSettingService">
					<ref bean="autoTakedownSettingService" />
				</entry>
				<entry key="mailListService">
					<ref bean="mailListService" />
				</entry>
				<entry key="emailTemplateService">
					<ref bean="emailTemplateService" />
				</entry>
				<entry key="matchService">
					<ref bean="matchService" />
				</entry>
			</map>
		</property>
	</bean>
	-->


google资料发现spring还有一种方式是MethodInvokingJobDetailFactoryBean,通过这个factroyBean包装Quartz任务,这样就不必继承job类去实现了,看如下:

<!--MethodInvokingJobDetailFactoryBean方式,指定concurrent设为false,多个job不会并发运行,默认concurrent为true-->
	<bean id="autoSendEmailJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
         <property name="targetObject" ref="autoSendEmailAction"/>
         <property name="targetMethod" value="autoSendEmail"/>
		 <property name="concurrent" value="false"/>
     </bean>
<bean id="autoSendEmailCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail" ref="autoSendEmailJobDetail"/>
		<property name="cronExpression">
			<value>${cronExpression}</value>
		</property>	
	</bean>
	<bean id="autoSendEmailSchedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
                <ref bean="autoSendEmailCronTrigger"/>
            </list>			
		</property>
	</bean> 
	<!-- end 定时自动发送邮件的配置 -->

action配置
<bean id="autoSendEmailAction" class="cn.xxx.videotrackerlive.web.struts.takedown.AutoSendEmailAction" init-method="clearSession" >
		<property name="accountService" ref="accountService" />		
		<property name="userService" ref="userService" />		
		<property name="takedownNoticeService" ref="takedownNoticeService" />		
		<property name="autoTakedownSettingService" ref="autoTakedownSettingService" />		
		<property name="mailListService" ref="mailListService" />		
		<property name="emailTemplateService" ref="emailTemplateService" />		
		<property name="matchService" ref="matchService" />		
	</bean>



参考资料:
Spring 为创建Quartz 的 Scheduler,Trigger 和 JobDetail提供了便利的FactoryBean类,以便能够在Spring容器中享受注入的好处。此外Spring还提供了一些便利工具类直接将Spring中的Bean包装成合法的任务。概括来说他提供了两方面的支持:        
              1.  为Quartz的重要组建类提供更具Bean风格的扩展类;        
              2.  提供创建Scheduler的BeanFactory类,方便在Spring环境下创建对应的组件对象,并结合Spring容器生命周期进行启动和停止的动作。
      让我们来具体研究研究Spring对Quartz的支持,相当好用哦。
创建JobDetail
       用户可以直接使用Quartz的JobDetail在Spring中配置一个JobDetail Bean,但是JobDetail使用带参的构造函数,对于习惯通过属性配制的Spring用户来说存在使用上的不便。为此Spring通过扩展JobDetail提供了一个更具Bean风格的JobDetailBean。

    JobDetailBean扩展于Quartz的JobDetail。使用该Bean声明JobDetail时,Bean的名字即是任务的名字,如果没有指定所属组,就使用默认组。除了JobDetail中的属性外,还定义了以下属性:

    JobClass:类型为Class,实现Job接口的任务。

   BeanName:默认为Bean的ID名,通过该属性显式指定Bean名称,它对应任务的名称。

   JobDateAsMap:类型为Map,为任务所对应的JobDataMap提供值。之所以需要提供这个属性,是因为用户无法在Spring配置文件为JobDataAsMap类型的属性提供信息,所以Spring通过jobDataAsMap设置JobDataMap的值。

  ApplicationContextJobDataKey:用户可以将Spring ApplicationContext的引用保存到JobDataMap中,以便在Job的代码中访问ApplicationContext。为了达到这个目的,用户需要指定一个键,用以在jobDataAsMap中保存  ApplicationContext,如果不设置此键,JobDetailBean就不将ApplicationContext放入到JobDataMap中。
          JobListenerNames;类型为String[],指定注册在Scheduler中的JobListeners名称,以便让这些监听器对本任务的事件进行监听。


下面配置片断使用JobDetailBean在Spring中配置一个JobDetail:

<bean name=”jobDetail” class=”org.springframework.schedling.quartz.jobDetaliBean”>

<property name=”jobClass”value=”com.test.Myjob”/>

<property name=”jobDataAsMap”>

  <map>

      <entry key=”size” value=”10”/>

    </map>

</property>

<property name=”applicationContextJobDataKey”value=”applicationContext”/>

</bean>



  


      

JobDetailBean封装了MyJob任务类,并为Job对应JobDataMap设置了一个size的数据。此外,通过指定 applicationContextDataKey让Job的JobDataMap持有Spring ApplicationContext的引用。
     同样的,Spring提供了一个MethodInvokingJobDetailFactoryBean,通过这个FactoryBean可以将Spring容器中Bean的方法包装成Quartz任务,这样开发者就不必为Job创建对应的类。而且定义起来想当方便

通常情况下,任务都定义在一个业务类方法中。这时,为了满足Quartz Job接口的规定,还需要定义一个引用业务类方法的实现类。为了避免创建这个只包含一行调用代码的Job实现类,Spring为我们提供了MethodInvokingJobDetailFactoryBean,借由该FactoryBean,我们可以将一个Bean的某种方法封装成满足Quartz 要求的Job。来看一个具体的例子:


<bean id="jobDetail_1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

          <property name="targetObject" ref="myService">

          <property name="targetMethod" value="doJob">

          <property name="concurrent" value="false">

</bean>

<bean id="myService" class="com.test.myService" />


        


           jobDetail_1将MyService#doJob()封装成一个任务,同时通过concurrent属性指定任务的类型,默认情况下封装为无状态的任务,如果希望目标封装为有状态的任务,仅需要将concurreng设置成false就可以了。

  Spring通过名为concurrent的属性指定任务的类型,能够更直接地描述任务执行的方式(有状态的任务不能并发执行,无状态的任务可并发执行)

public class MyService(){
         public void doJob(){
                 System.out.println("doJobing.....");
         }
}


  doJob()方法即可以是static,也可以是非static的,但不能拥有方法入参。通过MethodInvokingJobDetailFactoryBean产生的JobDetail不能被序列化,所以不能不持久化到数据库中,如果希望使用持久化任务,用户只能创建正规的Quartz的Job实现了。


你可能感兴趣的:(spring,bean,quartz,Google)