You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 12:17:38