quartz任务加载的问题 麻烦近来看看

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了quartz任务加载的问题 麻烦近来看看相关的知识,希望对你有一定的参考价值。

任务在servlet的init加载的时候出现了下面那个错误,请问那个表是做什么的呢?这个错误应该如何解决,是否是配置问题,麻烦知道的告诉我一下 谢谢 (那个表我没有手动插过值,都是配置文件自动插入的)

ErrorLogger [ERROR] An error occured while scanning for the next trigger to fire.
org.quartz.JobPersistenceException: Couldn't acquire next trigger: ORA-01400: 无法将 NULL 插入 ("BOCMIS"."QRTZ_FIRED_TRIGGERS"."PRIORITY")
[See nested exception: java.sql.SQLException: ORA-01400: 无法将 NULL 插入 ("BOCMIS"."QRTZ_FIRED_TRIGGERS"."PRIORITY")
]
at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:1778)
at org.quartz.impl.jdbcjobstore.JobStoreTX.acquireNextTrigger(JobStoreTX.java:1218)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java(Compiled Code))
* Nested Exception (Underlying Cause) ---------------
java.sql.SQLException: ORA-01400: 无法将 NULL 插入 ("BOCMIS"."QRTZ_FIRED_TRIGGERS"."PRIORITY")

下面是配置
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
org.quartz.jobStore.misfireThreshold = 60000
first to create tables
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = qzDS
org.quartz.dataSource.qzDS.driver = oracle.jdbc.driver.OracleDriver
org.quartz.dataSource.qzDS.URL = jdbc:oracle:thin:@localhost:test
org.quartz.dataSource.qzDS.user = test
org.quartz.dataSource.qzDS.password = test
org.quartz.dataSource.qzDS.maxConnections = 30

参考技术A 就是你在init的时候就准备执行SQL,把空值NULL,插入到数据表里,而这个表的字段为("BOCMIS"."QRTZ_FIRED_TRIGGERS"."PRIORITY") ,这就要看你程序怎么写的了。

JVM的深入理解:由一次Quartz的定时任务引发的“A cannot cast to A”的问题

由Quartz框架引发的“A cannot cast to A”的问题


起因与问题描述

向新开的项目中添加定时任务,部署集群,添加了热加载(springboot-dev-tools),发现在转型时候出现了A cannot cast to A”的问题。自己怎么可能不认识自己???排查走起!!!
技术图片


排查

类确实是同一类,问题会出现在哪里呢?我们可以想到,类加载器不同会导致“我不是我”的问题,所以打印debug走起!
真的是类加载器不同!能注意到,我们想要的ScheduleJobEntity类,类加载器是RestartClassLoader,这一切发生在【添加热加载(springboot-dev-tools)】之后才出现的。这时候需要复习一下springboot-dev-tools与类加载器机制的知识了。

技术图片

 

知识回顾

 

类加载器机制

  1. 有两个术语,一个叫“定义类加载器”,一个叫“初始类加载器”。比如有如下的类加载器结构:
    • bootstrap
      • ExtClassloader
      • AppClassloader
    • -自定义clsloadr1
    • -自定义clsloadr2
      如果用“自定义clsloadr1”加载java.lang.String类,那么根据双亲委派最终bootstrap会加载此类,那么bootstrap类就叫做该类的“定义类加载器”,而包括bootstrap的所有得到该类class实例的类加载器都叫做“初始类加载器”。
  2. 所说的“命名空间”,是指jvm为每个类加载器维护的一个“表”,这个表记录了所有以此类加载器为“初始类加载器”(而不是定义类加载器,所以一个类可以存在于很多的命名空间中)加载的类的列表,所以,题目中的问题就可以解释了:
    CLTest是AppClassloader加载的,String是通过加载CLTest的类加载器也就是AppClassloader进行加载,但最终委派到bootstrap加载的(当然,String类其实早已经被加载过了,这里只是举个例子)。所以,对于String类来说,bootstrap是“定义类加载器”,AppClassloader是“初始类加载器”。根据刚才所说,String类在AppClassloader的命名空间中(同时也在bootstrap,ExtClassloader的命名空间中,因为bootstrap,ExtClassloader也是String的初始类加载器),所以CLTest可以随便访问String类。这样就可以解释“处在不同命名空间的类,不能直接互相访问”这句话了。
  3. 一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。
  4. 那么由不同类加载器实例(比如-自定义clsloadr1,-自定义clsloadr2)所加载的classpath下和ext下的类,也就是由我们自定义的类加载器委派给AppClassloader和ExtClassloader加载的类,在内存中是同一个类吗?所有继承ClassLoader并且没有重写getSystemClassLoader方法的类加载器,通过getSystemClassLoader方法得到的AppClassloader都是同一个AppClassloader实例,类似单例模式。在ClassLoader类中getSystemClassLoader方法调用私有的initSystemClassLoader方法获得AppClassloader实例,在initSystemClassLoader中:
    sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
    ...
    scl = l.getClassLoader();

AppClassloader是sun.misc.Launcher类的内部类,Launcher类在new自己的时候生成AppClassloader实例并且放在自己的私有变量loader里:

loader = AppClassLoader.getAppClassLoader(extclassloader);

值得一提的是sun.misc.Launcher类使用了一种类似单例模式的方法,即既提供了单例模式的接口getLauncher()又把构造函数设成了public的。但是在ClassLoader中是通过单件模式取得的Launcher 实例的,所以我们写的每个类加载器得到的AppClassloader都是同一个AppClassloader类实例。
这样的话得到一个结论,就是所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在方法区都是同一个类,堆中的Class实例也是同一个。

springboot-dev-tools

可以参考<a href = "https://blog.csdn.net/isea533/article/details/70495714">Spring DevTools 介绍一文,其中最关键的一句话就是

重启功能是通过使用两个类加载器实现的。 对于大多数应用程序,此方法运行良好,但有时可能会导致类加载问题。
默认情况下,IDE中的任何打开的项目都会使用“restart”类加载器加载,任何常规.jar文件将使用“base”类加载器加载。

 

解决问题

 

在项目 /resource/META-INF目录下(如果没有就创建一个)创建
spring-devtools.properties文件 加入下面代码:

restart.include.mapper=/mapper-[w-.]+jar
restart.include.pagehelper=/pagehelper-[w-.]+jar
restart.include.shiro=/shiro-[w-.]+jar

这里其实是
添加 jar 包到 restart 类加载器中 = 后面是具体的 jar 包名称, 或正则表达式

以上是关于quartz任务加载的问题 麻烦近来看看的主要内容,如果未能解决你的问题,请参考以下文章

定时器篇---java.util.TimerTask和quarts

Spring定时任务之Quartz

Spring定时任务之Quartz

定时发送邮件(利用quart)

[原创] 在spring 中使用quarts

Quart,聊天服务器示例