spring集成quartz中java.lang.ClassNotFoundException: org.quartz.impl.JobDetailImpl异常解决方法

一、异常重现

1、测试条件

1.1、quartz版本:1.8.6

1.2、spring版本:4.2.6.RELEASE

2、定义接口

public interface ServiceI {
	void getMsg();
}
3、定义任务实现类

@Service("serviceImpl")
public class ServiceImpl implements ServiceI {

	@Override
	public void getMsg() {
		System.out.println("hello world! i'm invoking!");
	}
}
4、配置文件

<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/task  
		http://www.springframework.org/schema/task/spring-task-3.1.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
	default-lazy-init="false">
	
	<!-- 开启注解支持,并扫描指定包下的注解 -->
	<context:annotation-config />
	<context:component-scan base-package="com.chhliu.myself.quartz.spring.impl"></context:component-scan>
	
	<!-- 配置需要定时执行的任务类以及方法 -->
	<bean id="doJob"
		class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
		<!-- 指定任务类 -->
		<property name="targetObject" ref="serviceImpl" />
		<!-- 指定任务执行的方法 -->
		<property name="targetMethod" value="getMsg" />
	</bean>
	
	<!-- 配置触发器 -->
	<bean id="jobTrigger"
		class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
		<property name="jobDetail" ref="doJob" />
		<!-- 每10秒运行一次 -->
		<property name="cronExpression" value="0/10 * * * * ?" /><!-- 每隔10秒钟触发一次 -->
	</bean>

	<!-- 触发定时任务 -->
	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="jobTrigger" /><!-- 此处可以配置多个触发器 -->
			</list>
		</property>
	</bean>
</beans>

5、测试结果

Caused by: java.lang.ClassNotFoundException: org.quartz.impl.JobDetailImpl
	at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
	... 21 more
发现出现了这个异常。

6、异常原因分析

spring4.1.x版本后,MethodInvokingJobDetailFactoryBean类发生了改变,在初始化JobDetail的时候,需要new JobDetailImpl类,代码如下:

public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
		prepare();

		// Use specific name if given, else fall back to bean name.
		String name = (this.name != null ? this.name : this.beanName);

		// Consider the concurrent flag to choose between stateful and stateless job.
		Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);

		// Build JobDetail instance.
		JobDetailImpl jdi = new JobDetailImpl();// 此处需要JobDetailImpl类,但是quartz-1.8.6版本中又没有这个类,因此抛异常
		jdi.setName(name);
		jdi.setGroup(this.group);
		jdi.setJobClass((Class) jobClass);
		jdi.setDurability(true);
		jdi.getJobDataMap().put("methodInvoker", this);
		this.jobDetail = jdi;

		postProcessJobDetail(this.jobDetail);
	}
spring3.2.x版本中的初始化代码如下:
static {
        try {// 兼容quartz2.x版本
            jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl");
        }
        catch (ClassNotFoundException ex) {
            jobDetailImplClass = null;
        }
        try {// 兼容quartz1.x版本
            Class jobExecutionContextClass =
                    QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext");
            setResultMethod = jobExecutionContextClass.getMethod("setResult", Object.class);
        }
        catch (Exception ex) {
            throw new IllegalStateException("Incompatible Quartz API: " + ex);
        }
    }

public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
		prepare();

		// Use specific name if given, else fall back to bean name.
		String name = (this.name != null ? this.name : this.beanName);

		// Consider the concurrent flag to choose between stateful and stateless job.
		Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);

		// Build JobDetail instance.
               if (jobDetailImplClass != null) {
                       // Using Quartz 2.0 JobDetailImpl class...                        
                        // 如果是quartz2.x版本,注意此处的instantiate的类型,是Class<>类型,这是因为在Quartz2.x版本后,JobDetail由类变成了接口                       
                        this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail);
			bw.setPropertyValue("name", name);
			bw.setPropertyValue("group", this.group);
			bw.setPropertyValue("jobClass", jobClass);
			bw.setPropertyValue("durability", true);
			((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this);
		}
		else {
			// Using Quartz 1.x JobDetail class...
			this.jobDetail = new JobDetail(name, this.group, jobClass);
			this.jobDetail.setVolatility(true);
			this.jobDetail.setDurability(true);
			this.jobDetail.getJobDataMap().put("methodInvoker", this);
		}

		// Register job listener names.
		if (this.jobListenerNames != null) {
			for (String jobListenerName : this.jobListenerNames) {
				if (jobDetailImplClass != null) {
					throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +
							"manually register a Matcher against the Quartz ListenerManager instead");
				}
				this.jobDetail.addJobListener(jobListenerName);
			}
		}

		postProcessJobDetail(this.jobDetail);
	}
总结如下:

导致这个异常的根本原因是因为使用的quartz版本和spring版本不兼容造成的,在二者集成的时候,需要注意对应的版本。

其实在spring4.1.x的源码中已经有对应的说明了,原文如下:

Compatible with Quartz 2.1.4 and higher, as of Spring 4.1.

7、异常修复

7.1、quartz版本:2.2.0

7.2、spring版本:4.2.6.RELEASE

7.3、正确结果

hello world! i'm invoking!
2017-01-04 14:00:10 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-3] DEBUG org.quartz.core.JobRunShell - Calling execute on job DEFAULT.doJob
hello world! i'm invoking!
2017-01-04 14:00:10 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
2017-01-04 14:00:20 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-4] DEBUG org.quartz.core.JobRunShell - Calling execute on job DEFAULT.doJob
hello world! i'm invoking!
2017-01-04 14:00:20 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
2017-01-04 14:00:30 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Worker-5] DEBUG org.quartz.core.JobRunShell - Calling execute on job DEFAULT.doJob
2017-01-04 14:00:30 [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
hello world! i'm invoking!
  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值