{"record":{"id":"d1b5838186bf0fb6","repo":"jeecgboot/JeecgBoot","slug":"jobclassname","errorCode":null,"errorMessage":"后台找不到该类名：${jobClassName}","messagePattern":"后台找不到该类名：(.+?)","errorType":"exception","errorClass":"JeecgBootException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java","lineNumber":157,"sourceCode":"\t\t\t// 启动调度器\n\t\t\tscheduler.start();\n\n\t\t\t// 构建job信息\n\t\t\tJobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData(\"parameter\", parameter).build();\n\n\t\t\t// 表达式调度构建器(即任务执行的时间)\n\t\t\tCronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);\n\n\t\t\t// 按新的cronExpression表达式构建一个新的trigger\n\t\t\tCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build();\n\n\t\t\tscheduler.scheduleJob(jobDetail, trigger);\n\t\t} catch (SchedulerException e) {\n\t\t\tthrow new JeecgBootException(\"创建定时任务失败\", e);\n\t\t} catch (RuntimeException e) {\n\t\t\tthrow new JeecgBootException(e.getMessage(), e);\n\t\t}catch (Exception e) {\n\t\t\tthrow new JeecgBootException(\"后台找不到该类名：\" + jobClassName, e);\n\t\t}\n\t}\n\n\t/**\n\t * 删除定时任务\n\t * \n\t * @param id\n\t */\n\tprivate void schedulerDelete(String id) {\n\t\ttry {\n\t\t\tscheduler.pauseTrigger(TriggerKey.triggerKey(id));\n\t\t\tscheduler.unscheduleJob(TriggerKey.triggerKey(id));\n\t\t\tscheduler.deleteJob(JobKey.jobKey(id));\n\t\t} catch (Exception e) {\n\t\t\tlog.error(e.getMessage(), e);\n\t\t\tthrow new JeecgBootException(\"删除定时任务失败\");\n\t\t}\n\t}","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java#L139-L175","documentation":"Thrown from the generic catch(Exception) block in QuartzJobServiceImpl.schedulerAdd when loading or instantiating a Quartz job class fails with a checked exception (e.g. ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException) that is NOT a SchedulerException and NOT a RuntimeException. It signals that the target job class could not be located or instantiated by Thread.currentThread().getContextClassLoader(). Note: the IllegalArgumentException guards from getClass() (errors 162/163) are RuntimeExceptions, so they are caught by the earlier catch(RuntimeException) block and do NOT reach this message.","triggerScenarios":"POSTing a new scheduled job (e.g. /sys/quartzJob/add) with a jobClassName that (a) does not exist on the runtime classpath, (b) is outside the reach of the context classloader, (c) lacks a public no-arg constructor (getDeclaredConstructor().newInstance() throws), or (d) is abstract/an interface that passed the whitelist but cannot be instantiated.","commonSituations":"Typo in the class FQN stored in sys_quartz_job; the job class lives in a module not packaged into the deployed jeecg-system-start artifact; after a refactor the FQN changed but the DB row still references the old name; running in microservices/cloud mode where the class is in a different service; fat-jar deployment where the context classloader differs from the system loader.","solutions":["Verify the exact FQN in the sys_quartz_job row exists in the deployed jar (jar tf <artifact> | grep <ClassName>).","Ensure the class is under the org.jeecg.* whitelist, implements org.quartz.Job, and has a public no-arg constructor.","Confirm the module containing the job is on the classpath of jeecg-system-start (the artifact you actually run).","After a rename/refactor, update existing sys_quartz_job rows to the new FQN."],"exampleFix":"// before: job class has only a parametrized constructor\npublic class MyJob implements Job {\n    public MyJob(String cfg) { ... }\n}\n// after: add an explicit no-arg constructor so newInstance() succeeds\npublic class MyJob implements Job {\n    public MyJob() { this(\"default\"); }\n    public MyJob(String cfg) { ... }\n}","handlingStrategy":"validation","validationCode":"// Validate the job class is loadable, whitelisted, Job-implementing, and instantiable BEFORE scheduling.\nprivate static void assertJobClassSchedulable(String jobClassName) throws ClassNotFoundException {\n    if (jobClassName == null || !jobClassName.startsWith(\"org.jeecg.\")) {\n        throw new IllegalArgumentException(\"jobClassName 必须以 org.jeecg. 开头\");\n    }\n    Class<?> c = Class.forName(jobClassName, false,\n            Thread.currentThread().getContextClassLoader());\n    if (!org.quartz.Job.class.isAssignableFrom(c)) {\n        throw new IllegalArgumentException(jobClassName + \" 未实现 org.quartz.Job\");\n    }\n    try { c.getDeclaredConstructor(); }\n    catch (NoSuchMethodException ex) {\n        throw new IllegalArgumentException(jobClassName + \" 缺少无参构造器\");\n    }\n}\n// call before schedulerAdd:\nassertJobClassSchedulable(jobClassName);","typeGuard":"public static boolean isSchedulableJobClass(String name) {\n    if (name == null || !name.startsWith(\"org.jeecg.\")) return false;\n    try {\n        Class<?> c = Class.forName(name, false, Thread.currentThread().getContextClassLoader());\n        return org.quartz.Job.class.isAssignableFrom(c)\n            && java.lang.reflect.Modifier.isPublic(c.getModifiers())\n            && !java.lang.reflect.Modifier.isAbstract(c.getModifiers());\n    } catch (Throwable t) { return false; }\n}","tryCatchPattern":"try {\n    quartzJobService.schedulerAdd(...);\n} catch (JeecgBootException e) {\n    // e.getMessage() contains the jobClassName; check classpath/constructor\n    log.error(\"调度任务创建失败, jobClassName={}\", jobClassName, e);\n    return Result.error(\"任务类加载失败, 请检查类名与部署包: \" + jobClassName);\n}","preventionTips":["Store job class names as constants or an enum rather than free text to avoid typos.","Add a startup health check that validates every sys_quartz_job.job_class_name is loadable in the current artifact.","After any rename/refactor, grep sys_quartz_job for stale FQNs and update them.","Keep job classes in the same module that is packaged into the runnable artifact."],"tags":["quartz","scheduler","classloader","rce-prevention"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}