jeecgboot/JeecgBoot · error · IllegalArgumentException
非法的任务类: ,必须实现 org.quartz.Job 接口
Error message
非法的任务类:${classname},必须实现 org.quartz.Job 接口 What it means
The second guard in getClass(): after the whitelist passes and Class.forName succeeds, the loaded class must be assignable to org.quartz.Job. This prevents scheduling arbitrary classes (e.g. a service bean or a class implementing a custom interface) that the Quartz scheduler cannot execute.
Solutions
- Make the class implement org.quartz.Job (override execute(JobExecutionContext)) or extend a Job base such as QuartzJobBean.
- Point sys_quartz_job.job_class_name at the class that already implements Job.
Example fix
// before
public class ReportTask {
public void run() { ... }
}
// after
public class ReportTask implements org.quartz.Job {
@Override public void execute(JobExecutionContext ctx) { run(); }
public void run() { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// Confirm the class implements Job before scheduling.
Class<?> c = Class.forName(jobClassName);
if (!org.quartz.Job.class.isAssignableFrom(c)) {
throw new IllegalArgumentException(jobClassName + " 必须实现 org.quartz.Job");
} Type guard
public static boolean implementsJob(String name) {
try {
return org.quartz.Job.class.isAssignableFrom(
Class.forName(name, false, Thread.currentThread().getContextClassLoader()));
} catch (Throwable t) { return false; }
} Try / catch
try {
quartzJobService.schedulerAdd(job);
} catch (JeecgBootException e) {
if (e.getMessage() != null && e.getMessage().contains("必须实现 org.quartz.Job")) {
return Result.error("所选类未实现 Job 接口, 无法调度");
}
throw e;
} Prevention
- Create job classes from a Job base template so the interface is never forgotten.
- Run an automated check that all scheduled job classes implement Job at build time.
When it happens
Trigger: A jobClassName that resolves to an org.jeecg.* class which does NOT implement org.quartz.Job — e.g. a plain @Service bean, an entity, or a class implementing only java.lang.Runnable.
Common situations: A developer wrote the job logic as a method on an existing service rather than a dedicated Job class; the class was refactored and the `implements Job` clause dropped; pointing the scheduler at a DTO/entity by mistake.
Related errors
- 非法的任务类名: ,仅允许 org.jeecg 包下的Job类
- 后台找不到该类名:
- admin角色,不允许删除!
- 删除AI知识库文档失败,不能删除其他租户的AI知识库文档!
- 非法业务路径,禁止访问上传目录之外的路径
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/26ad1404df5d41a9.
Report an issue: GitHub.
Appendix: source
Thrown at jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java:190
log.error(e.getMessage(), e);
throw new JeecgBootException("删除定时任务失败");
}
}
/**
* 安全加载Job类:仅允许 org.jeecg. 包下的类,且必须实现 org.quartz.Job 接口
*/
private static Job getClass(String classname) throws Exception {
// 包名白名单校验,防止任意类实例化导致RCE
if (classname == null || !classname.startsWith("org.jeecg.")) {
throw new IllegalArgumentException("非法的任务类名:" + classname + ",仅允许 org.jeecg 包下的Job类");
}
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
Class<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
// 校验是否实现了 org.quartz.Job 接口
if (!Job.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("非法的任务类:" + classname + ",必须实现 org.quartz.Job 接口");
}
return (Job) clazz.getDeclaredConstructor().newInstance();
}
}
View on GitHub (pinned to 96fb33f5ec)