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的实的方式主要有两种:
第一种:利用JobDetailFactoryBean包装的QuartzJobBean,自定义job继承QuartzJobBean,重写executeInternal()即可。但是如果想自动注入spring的服务,就需要自定义jobFactory继承AdaptableJobFactory 。
第二种:直接创建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、效果