JobRunr 6.3.0反射实例化异常:抽象类定时任务执行失败
JobRunr 6.3.0 调度抽象类任务时的反射实例化异常问题
问题场景
使用JobRunr 6.3.0在Java中调度周期性后台任务,通过抽象类Blubb实现任务调度逻辑,子类实现抽象方法。任务创建成功,但执行时抛出InstantiationException,即使移除自定义构造函数后问题依旧。
抽象类代码
public abstract class Blubb { public Blubb() { this(...defaultArgs); } protected Blubb(defaultArgs) { // set up } protected abstract String name(); protected abstract Iterator<Object> getObjectsFromExternalSource(); public void load() { JobScheduler scheduler = JobRunr .configure() .useStorageProvider(new InMemoryStorageProvider()) .useBackgroundJobServer() .initialize() .getJobScheduler(); scheduler.createRecurrently( RecurringJobBuilder .aRecurringJob() .withId(name()) .withDuration(Duration.ofSeconds(10)) .withDetails(this::run) .withAmountOfRetries(2) ); } public void run() { Iterator<Object> objects = getObjectsFromExternalSource(); while (objects.hasNext()) { // do stuff } } }
数据库中任务信息
{ "_id": "name", "version": 0, "jobAsJson": "{\"version\":0,\"jobSignature\":\"org.example.blah.Blubb.run()\",\"jobName\":\"org.example.blah.Blubb.run()\",\"amountOfRetries\":2,\"labels\":[],\"jobDetails\":{\"className\":\"org.example.blah.Blubb\",\"staticFieldName\":null,\"methodName\":\"run\",\"jobParameters\":[],\"cacheable\":true},\"id\":\"name\",\"scheduleExpression\":\"PT10M\",\"zoneId\":\"Etc/UTC\",\"createdAt\":\"2023-08-25T06:39:29.844981739Z\"}", "createdAt": { "$numberLong": "1692945569844" } }
执行时异常栈
WARN o.j.server.BackgroundJobPerformer - Job(id=a444fd50-843a-4464-88a1-e51530056b3f, jobName='org.example.blah.Blubb.run()') processing failed: An exception occurred during the performance of the job org.jobrunr.JobRunrException: JobRunr encountered a problematic exception. Please create a bug report (if possible, provide the code to reproduce this and the stacktrace) at org.jobrunr.JobRunrException.shouldNotHappenException(JobRunrException.java:43) at org.jobrunr.utils.reflection.ReflectionUtils.newInstance(ReflectionUtils.java:156) at org.jobrunr.server.runner.AbstractBackgroundJobRunner$BackgroundJobWorker.getJobToPerform(AbstractBackgroundJobRunner.java:46) at org.jobrunr.server.runner.AbstractBackgroundJobRunner$BackgroundJobWorker.run(AbstractBackgroundJobRunner.java:36) at org.jobrunr.server.runner.AbstractBackgroundJobRunner.run(AbstractBackgroundJobRunner.java:20) at org.jobrunr.server.BackgroundJobPerformer.runActualJob(BackgroundJobPerformer.java:89) at org.jobrunr.server.BackgroundJobPerformer.performJob(BackgroundJobPerformer.java:64) at org.jobrunr.server.BackgroundJobPerformer.run(BackgroundJobPerformer.java:42) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.InstantiationException: null at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) at org.jobrunr.utils.reflection.ReflectionUtils.newInstance(ReflectionUtils.java:154)
问题原因
核心问题在于:JobRunr执行任务时尝试通过反射实例化抽象类Blubb,但抽象类本身无法被实例化。从数据库的jobDetails可以看到,className字段记录的是抽象类org.example.blah.Blubb,而非具体子类。这是因为创建任务时使用this::run,若该调用发生在抽象类内部,或子类调用load方法时JobRunr序列化了父类类型,导致执行时错误地尝试实例化抽象类。
构造函数并非问题根源——即使移除自定义构造函数,抽象类依然无法被实例化。
解决方案
1. 确保任务绑定具体子类实例
让JobRunr调度具体子类的run方法,而非抽象类的方法。例如:
定义具体子类
public class ConcreteBlubb extends Blubb { @Override protected String name() { return "concrete-blubb-job"; } @Override protected Iterator<Object> getObjectsFromExternalSource() { // 实现具体的外部数据源逻辑 return List.of(new Object()).iterator(); } }
在应用中初始化子类并调度任务
public class AppStartup { public static void main(String[] args) { // 实例化具体子类而非抽象类 ConcreteBlubb concreteBlubb = new ConcreteBlubb(); concreteBlubb.load(); } }
2. 验证任务详情
修改后检查数据库中的jobDetails.className,确认其值为具体子类的全类名(如org.example.blah.ConcreteBlubb),而非抽象类。此时JobRunr会实例化具体子类,正常执行任务。
3. 可选:分离调度逻辑(推荐)
将任务调度逻辑从抽象类中分离,避免抽象类直接参与JobRunr的任务注册,进一步降低耦合:
// 抽象任务逻辑类 public abstract class AbstractJobTask { protected abstract String name(); protected abstract Iterator<Object> getObjectsFromExternalSource(); public void run() { Iterator<Object> objects = getObjectsFromExternalSource(); while (objects.hasNext()) { // do stuff } } } // 具体任务类 public class ConcreteJobTask extends AbstractJobTask { @Override protected String name() { return "concrete-job-task"; } @Override protected Iterator<Object> getObjectsFromExternalSource() { return List.of(new Object()).iterator(); } } // 独立的调度器 public class JobSchedulerService { public void scheduleRecurrentJob(AbstractJobTask task) { JobScheduler scheduler = JobRunr .configure() .useStorageProvider(new InMemoryStorageProvider()) .useBackgroundJobServer() .initialize() .getJobScheduler(); scheduler.createRecurrently( RecurringJobBuilder .aRecurringJob() .withId(task.name()) .withDuration(Duration.ofSeconds(10)) .withDetails(task::run) .withAmountOfRetries(2) ); } } // 应用启动时使用 public class App { public static void main(String[] args) { JobSchedulerService schedulerService = new JobSchedulerService(); schedulerService.scheduleRecurrentJob(new ConcreteJobTask()); } }
内容的提问来源于stack exchange,提问作者Christina Unger
相关产品推荐
相关产品推荐

