Получил nullPointerException при использовании кварца в кластерной системе, но при удалении конфигурации базы данных он работает нормально

Это мой кварцевый конфиг:

@Bean
public SchedulerFactoryBean startQuartz(DataSource dataSource, PlatformTransactionManager annotationDrivenTransactionManager,
                                        CronTriggerFactoryBean remoteProjectTrigger, ApplicationContext applicationContext) throws IOException {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    schedulerFactoryBean.setDataSource(dataSource);
    schedulerFactoryBean.setTransactionManager(annotationDrivenTransactionManager);
    schedulerFactoryBean.setOverwriteExistingJobs(false);
    schedulerFactoryBean.setQuartzProperties(quartzProperties());
    schedulerFactoryBean.setTriggers(remoteProjectTrigger.getObject());
    schedulerFactoryBean.setApplicationContext(applicationContext);
    return schedulerFactoryBean;
}

@Bean(name = "detailFactoryBean")
public MethodInvokingJobDetailFactoryBean detailFactoryBean(){
    MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean ();
    bean.setTargetBeanName("quartzRemoteProject");
    bean.setTargetMethod ("run");
    bean.setConcurrent (false);
    return bean;
}


@Bean
public CronTriggerFactoryBean remoteProjectTrigger(MethodInvokingJobDetailFactoryBean detailFactoryBean){
    CronTriggerFactoryBean trigger = new CronTriggerFactoryBean ();
    trigger.setJobDetail (detailFactoryBean.getObject ());
    trigger.setCronExpression ("0/5 * * ? * *");
    return trigger;
}


@Bean("quartzRemoteProject")
public QuartzRemoteProject quartzRemoteProject(){
    return new QuartzRemoteProject();
}


public Properties quartzProperties() throws IOException {
    Properties prop = new Properties();
    prop.put("quartz.scheduler.instanceName", "ServerScheduler");
    prop.put("org.quartz.scheduler.instanceId", "AUTO");
    prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
    prop.put("org.quartz.scheduler.instanceId", "CLUSTERED");
    prop.put("org.quartz.scheduler.jobFactory.class", "org.quartz.simpl.SimpleJobFactory");
    prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
    prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
    prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
    prop.put("org.quartz.jobStore.isClustered", "true");
    prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
    prop.put("org.quartz.threadPool.threadCount", "5");

    return prop;
}

этот конфиг получит исключение

org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.2.3.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.2.3.jar:na]
Caused by: java.lang.NullPointerException: null
at org.springframework.scheduling.quartz.JobMethodInvocationFailedException.<init>(JobMethodInvocationFailedException.java:40) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFactoryBean.java:271) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75) ~[spring-context-support-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.2.3.jar:na]
... 1 common frames omitted

я нахожу, что проблема в внутреннем классе MethodInvokingJobDetailFactoryBean.class MethodInvokingJob

context.setResult(this.methodInvoker.invoke());

methodInvoker нулевой;

когда удалить schedulerFactoryBean.setDataSource(dataSource);schedulerFactoryBean.setTransactionManager(annotationDrivenTransactionManager);schedulerFactoryBean.setQuartzProperties(quartzProperties());

этот конфиг будет работать. любой может помочь

1 ответ

Решение

Это потому, что класс MethodInvokingJobDetailFactoryBean нельзя сериализовать в базу данных,spring-context-support не предоставил сериализованную версию, предполагается, бросить SerializationException вместо NullPointerExceptionЯ не понимаю этого. Но только решить эту проблему я нахожу двумя способами.

1, перезаписать MethodInvokingJobDetailFactoryBean Сериализуемая версия, вы можете найти образец в https://jira.spring.io/browse/SPR-3797, но для последней версии кварца (2.2.3) вы можете выполнить некоторые обновления. затем измените конфигурацию MethodInvokingJobDetailFactoryBean на BeanInvokingJobDetailFactoryBean, как показано ниже

@Bean(name = "jobDetailFactoryBean")
public BeanInvokingJobDetailFactoryBean detailFactoryBean(){
    BeanInvokingJobDetailFactoryBean bean = new BeanInvokingJobDetailFactoryBean ();
    bean.setTargetBean("quartzRemoteProject");
    bean.setTargetMethod ("run");
    bean.setDurable(true);
    bean.setShouldRecover(true);
    bean.setConcurrent (false);
    return bean;
}

2、 пиши сам QuartzJobBean, как это

@Log4j
@NoArgsConstructor
public class MyDetailQuartzJobBean extends QuartzJobBean {

private String targetObject;
private String targetMethod;
private ApplicationContext ctx;

@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    try {
        Object bean = ctx.getBean(targetObject);
        Method m = bean.getClass().getMethod(targetMethod);
        m.invoke(bean, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void setApplicationContext(ApplicationContext applicationContext) {
    this.ctx = applicationContext;
}

public void setTargetObject(String targetObject) {
    this.targetObject = targetObject;
}

public void setTargetMethod(String targetMethod) {
    this.targetMethod = targetMethod;
}
}

я использую lombok аннотаций. затем использовать этот класс заменить MethodInvokingJobDetailFactoryBeanКонфиг, как это

@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
    JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
    jobDetailFactoryBean.setJobClass(MyDetailQuartzJobBean.class);
    Map map=new HashMap<>();
    map.put("targetObject","quartzRemoteProject");
    map.put("targetMethod","run");
    jobDetailFactoryBean.setJobDataAsMap(map);
    jobDetailFactoryBean.setDurability(true);
    return jobDeta
}

PS: второе решение, которое вы должны добавить quartz-jobs иначе не могу найти JobDetailFactoryBean

<dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz-jobs</artifactId>
        <version>2.2.3</version>
    </dependency>
Другие вопросы по тегам