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与类加载器机制的知识了。
知识回顾
类加载器机制
- 有两个术语,一个叫“定义类加载器”,一个叫“初始类加载器”。比如有如下的类加载器结构:
- bootstrap
- ExtClassloader
- AppClassloader
- -自定义clsloadr1
- -自定义clsloadr2
如果用“自定义clsloadr1”加载java.lang.String类,那么根据双亲委派最终bootstrap会加载此类,那么bootstrap类就叫做该类的“定义类加载器”,而包括bootstrap的所有得到该类class实例的类加载器都叫做“初始类加载器”。
- bootstrap
- 所说的“命名空间”,是指jvm为每个类加载器维护的一个“表”,这个表记录了所有以此类加载器为“初始类加载器”(而不是定义类加载器,所以一个类可以存在于很多的命名空间中)加载的类的列表,所以,题目中的问题就可以解释了:
CLTest是AppClassloader加载的,String是通过加载CLTest的类加载器也就是AppClassloader进行加载,但最终委派到bootstrap加载的(当然,String类其实早已经被加载过了,这里只是举个例子)。所以,对于String类来说,bootstrap是“定义类加载器”,AppClassloader是“初始类加载器”。根据刚才所说,String类在AppClassloader的命名空间中(同时也在bootstrap,ExtClassloader的命名空间中,因为bootstrap,ExtClassloader也是String的初始类加载器),所以CLTest可以随便访问String类。这样就可以解释“处在不同命名空间的类,不能直接互相访问”这句话了。 - 一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。
- 那么由不同类加载器实例(比如-自定义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 包名称, 或正则表达式
JVM的深入理解:由一次Quartz的定时任务引发的“A cannot cast to A”的问题的更多相关文章
- Quartz.net 定时任务之Cron表达式
一.cron表达式简单介绍和下载 1.在上一篇博客"Quartz.net 定时任务之简单任务"中,我简单介绍了quartz的使用,而这篇博客我将介绍cron的具体使用(不足之处望大 ...
- 使用Quartz创建定时任务
项目开发中经常需要定时循环执行某些任务 比如定时发送报表,定时发送邮件,亦或者定时清理缓存,定时更新数据等等 有些时候可以简单地利用Windows Server的计划任务执行程序 Linux也有相应的 ...
- springboot整合Quartz实现定时任务
1.maven依赖: <!--quartz--> <dependency> <groupId>org.quartz-scheduler</groupId> ...
- Spring+Quartz 实现定时任务的配置方法
Spring+Quartz 实现定时任务的配置方法 整体介绍 一.Quartz介绍 在企业应用中,我们经常会碰到时间任务调度的需求,比如每天凌晨生成前天报表,每小时生成一次汇总数据等等.Quartz是 ...
- Spring整合quartz2.2.3总结,quartz动态定时任务,Quartz定时任务集群配置
Spring整合quartz2.2.3总结,quartz动态定时任务,Quartz定时任务集群配置 >>>>>>>>>>>>&g ...
- 【spring-boot】 springboot整合quartz实现定时任务
在做项目时有时候会有定时器任务的功能,比如某某时间应该做什么,多少秒应该怎么样之类的. spring支持多种定时任务的实现.我们来介绍下使用spring的定时器和使用quartz定时器 1.我们使用s ...
- Spring Boot集成quartz实现定时任务并支持切换任务数据源
org.quartz实现定时任务并自定义切换任务数据源 在工作中经常会需要使用到定时任务处理各种周期性的任务,org.quartz是处理此类定时任务的一个优秀框架.随着项目一点点推进,此时我们并不满足 ...
- SpringBoot集成Quartz实现定时任务
1 需求 在我的前后端分离的实验室管理项目中,有一个功能是学生状态统计.我的设计是按天统计每种状态的比例.为了便于计算,在每天0点,系统需要将学生的状态重置,并插入一条数据作为一天的开始状态.另外,考 ...
- Quartz:定时任务工具类
Quartz:定时任务工具类 Quartz工具类 Quartz工具类 import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; ...
随机推荐
- bitmap以及异或运算法
一 有40亿个整数,再给一个新的整数,需要判断新的整数是否在1亿个整数中. 此处需要用到bitmap方法,每个整数用一个bit表示,1表示存在,0表示不存在.因此一个4字节的int=32个bit也就是 ...
- 映美FP-530K+打印发票的各种经验
本人虽然写了很多lodop博文,但是程序开发一般都是用虚拟打印机测试,实际打印机打印中还可能存在各种问题,毕竟理论都不如实践能获得更多的打印经验.当个人使用过的打印机不多,只有映美FP-530K+,用 ...
- Apache软件基金会核心项目Tomcat的那些事
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.今天就来为大家介绍一下关于T ...
- 超实用的 JavaScript 代码片段( ES6+ 编写)
Array 数组 Array concatenation (数组拼接) 使用 Array.concat() ,通过在 args 中附加任何数组 和/或 值来拼接一个数组. const ArrayCon ...
- 在Linux下,使用Xmanager图形化显示
linux在安装Oracle时,需要图形化展示,本博客将使用xclock显示钟表进行测试 首先需要下载Xmanager网址:http://www.xshellcn.com/ 安装之后 打开之后,点击t ...
- Word样式教程
目录 写在前面 样式可以解决什么问题? 本文适合于 快速入门 一切皆样式 样式与格式的关系 如何修改样式 建立新的样式 样式的匹配和更新 根据样式更新所选段落 根据所选段落更新样式 小结 进一步了解 ...
- Pycharm下直接升级库所遇到的'main'问题
Pycharm下直接升级库所遇到的pip模块中无'main'问题 Author : Benjamin142857 Date : 8/19/2018 对于Pycharm中直接升级库,只需在 \(Sett ...
- 【转载】Windows安装Redis并添加本地自启动服务
概况 在windows本地搭建redis缓存,添加到本地计算机的服务中,保证每次开机自动启动服务. 第一步:下载redis(我的是计算机win10,64位) https://github.com/Mi ...
- 记redis一次Could not get a resource from the pool 异常的解决过程
最近有个项目中的redis每天都会报 "Could not get a resource from the pool"的错误,而这套代码在另一地方部署又没有问题.一直找不到错误原因 ...
- Base64图片编码的使用
一.base64编码介绍 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,Base64编码可用于在HTTP环境下传递较长的标识信息.采用Base64编码具有不可读性,即所编码的数据 ...
