spring+quartz整合--解决quartz任务service注入失败

来源:互联网 发布:sql 修改字段为自增列 编辑:程序博客网 时间:2024/05/21 09:23

   这几天需要用到定时任务调度,开始准备用timer,后来经过别人介绍与spring集成的quartz,看了下更适合开发,因此转向quartz的学习,特此记录下来。

quartz使用起来及其方便,与spring整合配置,只需要写一个类继承QuartzJobBean,重写其executeInternal(JobExecutionContext arg0)即可,我们只需要将自己的逻辑代码写在该方法即可。一旦你配置好实现类并设定好调度时间,Quartz将密切注意剩余时间。当调度程序确定该是执行作业的时候,Quartz框架将调用你作业类上的executeInternal()方法并执行相应的任务。无需报告任何东西给调度器或调用任何特定的东西。仅仅执行任务和结束任务即可。如果配置你的作业在随后再次被调用,Quartz框架将在恰当的时间再次调用它。


quartz有三个核心概念,调度器、任务和触发器。三者关系简单来说就是,调度器负责调度各个任务,到了某个时刻或者过了一定时间,触发器触动了,特定任务便启动执行。概念相对应的类和接口有:


  1)JobDetail:描述任务的相关情况,包括配置任务执行的类和方法。

  2)Trigger:描述出发Job执行的时间触发规则。有SimpleTrigger和CronTrigger两个子类代表两种方式,一种是每隔多少分钟小时执行,则用SimpleTrigger;另一种是日历相关的重复时间间隔,如每天凌晨,每周星期一运行的话,通过Cron表达式便可定义出复杂的调度方案。

  3)Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail要注册到Scheduler中才会生效,也就是让调度器知道有哪些触发器和任务,才能进行按规则进行调度任务。

quartz的配置一般有两种,一种是配置无参数的任务,就是一个普通方法,要求该执行任务的方法必须为无参方法,否则会报错。另一种是配置可传参方法,因为这里我需要给任务传递一些参数,所以采用第二种配置方法。

首先,需要下载quartz和spring对其提供支持的jar包。我这里采用的是quartz-2.2.1.jar,spring-context-support-4.2.4.RELEASE.jar,下载其他版本请注意包版本之间的兼容。

任务类配置:

<!-- 配置Job的bean -->  <bean id="myJob" class="com.wego.task.Syn_Scheduling"/>

JobDetail的配置


<!-- 配置jobDetail -->  <bean id="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">  <!-- 用到的Job实例 -->  <property name="jobClass" value="com.wego.task.Syn_Scheduling"/>  </bean>



Trigger的配置


<!-- 配置触发器Trigger -->  <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">    <property name="name" value="work_default_name"/>    <property name="group" value="work_default"/>    <property name="jobDetail" ref="myJobDetail"/>   <!--  每天1点10分30执行  30 10 1 * * ?    每五秒执行一次 0/5 * * * * ? -->    <property name="cronExpression" value="0/30 * * * * ?"/>  </bean>



scheduler配置


<!-- 配置scheduler工厂 -->  <bean id="scheduler"  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">      <property name="triggers" ref="myTrigger"/>      <!-- 这里配置任务不随spring容器初始化而自动启动 -->      <property name="autoStartup" value="false"/></bean>



注意我标红的地方,因为我需要通过前台点击按钮触发任务启动,所以我设置了不随spring初始化而启动,一般情况下默认是随spring初始化自动启动任务的。手动启动任务需要下面:


Resource res = new ClassPathResource("beans.xml");          BeanFactory bf = new XmlBeanFactory(res);               Scheduler scheduler=(Scheduler)bf.getBean("scheduler");  try {        scheduler.start();//启动任务       } catch (SchedulerException e) {        e.printStackTrace();    }



任务类:


public class Syn_Scheduling extends QuartzJobBean{    @Override  protected void executeInternal(JobExecutionContext arg0)          throws JobExecutionException {    }   }


完整的配置文件


<!-- quartz配置 -->  <!-- 配置Job的bean --><!-- com.wego.action.IndexAction -->  <bean id="myJob" class="com.wego.task.Syn_Scheduling"/>  <!-- 配置jobDetail -->  <bean id="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">    <!-- 用到的Job实例 -->    <property name="jobClass" value="com.wego.task.Syn_Scheduling"/>  </bean>  <!-- 配置触发器Trigger -->  <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">    <property name="name" value="work_default_name"/>    <property name="group" value="work_default"/>    <property name="jobDetail" ref="myJobDetail"/>   <!--  每天1点10分30执行  30 10 1 * * ?     每五秒执行一次0/5 * * * * ? -->    <property name="cronExpression" value="0/30 * * * * ?"/>  </bean>  <!-- 配置scheduler工厂 -->  <bean id="scheduler"  class="org.springframework.scheduling.quartz.SchedulerFactoryBean">    <property name="triggers" ref="myTrigger"/>    <!-- 这里配置任务不随spring容器初始化而自动启动 -->    <property name="autoStartup" value="false"/>     </bean>




好了,简单配置大概就是这个样子,关于quartz更复杂的配置请参考其官方文档。这里说下我遇到的问题,我需要在一个action中进行任务的调度,但是在任务类中直接加注解注入service报空指针。后来网上查才知道spring是将bean放在ApplicationContext中的。而quartz初始化是自己的JobContext,不同于Spring的ApplicationContext,所以无法直接注入。但我们可以采用SchedulerContext来进行传参,它类似一个map。我们可以先将service注入action,并放入SchedulerContext中,在任务执行类中就可以取到相应的service了。代码如下:

action:


@Controller  @Scope("prototype")  public class IndexAction extends ActionSupport {  Resource(name = "organizeService")  private OrganizationService organizationService;    public void doTask(){     Resource res = new ClassPathResource("applicationContext.xml");     BeanFactory bf = new XmlBeanFactory(res);     Scheduler scheduler = (Scheduler) bf.getBean("scheduler");     try {     // 放入service对象     scheduler.getContext().put("organizationService",organizationService);     scheduler.start();    }catch (SchedulerException e){              e.printStackTrace();          }    }  }


任务类:



public class Syn_Scheduling extends QuartzJobBean{  private OrganizationService organizationService;    @Override  protected void executeInternal(JobExecutionContext arg0)throws JobExecutionException {  try {          organizationService= (OrganizationService) arg0.getScheduler().getContext().get("organizationService");      } catch (SchedulerException e1) {                e1.printStackTrace();      }    }  }


这样就能解决任务注入失败的问题了,其实网上还有很多其他解决办法,有的使用JobDataMap,有的直接将参数写在配置文件中,有时间一定还要琢磨琢磨,知道得多才能找到更合适的方法,解决问题才能更加高效