前天禅道上给了我一个BUG,我最终找到了原因但是 需要修改原有框架里面的东西,修改的东西不多,但是如何去修改呢?
第一时间我想到的就是先将框架的jar包进行反编译修改完之后在进行打包,在放到仓库里面。于是在网上搜集这样的工具,网上也有很多反编译的工具,但是我看下来有一个问题,每一个工具都不能原模原样的把源码里面的东西进行复原,并且由于不同的解析语法,可能解析出来的代码格式也不一致,比如有的解析支持Java8的lamdam表达式和stream流,有的却不支持。
于是就暂时举步维艰,今天早上来问了下老大,人家给了我一个最简单的方案:
如果你想要修改你引入Jar包的源码,你可以在你的项目目录下创建一个相同路径的类,再把要扩展的类的内容全部都粘贴复制到自己定义的类,然后再次基础上进行自己的扩展,这样的话JVM类加载器就会加载你自己定义的类,而不会加载框架里的类。原因肯定是跟JVM类加载的顺序有关。
类加载的过程
-
class file
存在于本地硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载到JVM当中来根据这个文件实例化出n个一模一样的实例。 -
class file
加载到JVM中,被称为DNA元数据模板
,放在方法区
。 - 在
.class文件->JVM->最终成为元数据模板
,此过程就要一个运输工具(类装载器Class Loader
),扮演一个快递员的角色。
加载阶段完成三件事
- 通过一个类的
全限定名
获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为
方法区
的运行时数据结构 -
在内存中生成一个代表这个类的java.lang.Class对象
,作为方法区这个类的各种数据的访问入口
在这里突然想到了JVM类加载器的双亲委派机制,双亲委派机制的作用就是① 避免类的重复加载 ②保护程序安全,防止核心API被随意篡改 【保护核心API】
虚拟机自带的加载器
1. 启动类加载器(引导类加载器,Bootstrap ClassLoader
)
- 这个类加载使用
C/C++
语言实现的,嵌套在JVM内部。 - 它用来加载Java的核心库(
JAVAHOME/jre/1ib/rt.jar、resources.jar或sun.boot.class.path
路径下的内容),用于提供JVM自身需要的类 - 并不继承自
java.lang.ClassLoader
,没有父加载器。 - 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
- 出于安全考虑,Bootstrap启动类加载器只加载包名为
java、javax、sun
等开头的类
2. 扩展类加载器( Extension ClassLoader
)
-
Java
语言编写,由sun.misc.Launcher$ExtClassLoader
实现。 - 派生于
ClassLoader
类 - 父类加载器为启动类加载器
- 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的
jre/1ib/ext子目录(扩展目录)
下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
3. 应用程序类加载器(系统类加载器,AppClassLoader
)
-
java
语言编写,由sun.misc.LaunchersAppClassLoader
实现 - 派生于
ClassLoader
类 - 父类加载器为扩展类加载器
- 它负责加载环境变量
classpath
或系统属性java.class.path
指定路径下的类库 - 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
- 通过
classLoader#getSystemclassLoader()
方法可以获取到该类加载器
不言而喻,JVM是根据全限定名进行加载clsss,而且同一个路径的类只会加载一次,于是这就引起了类的加载优先级的问题,由结果可得知,
- 先是
Bootstrap ClassLoader
加载java、javax、sun
等开头的类,Extension ClassLoader
加载java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/1ib/ext子目录(扩展目录)
下加载类库。AppClassLoader
加载我们正常项目中的classAppClassLoader
加载我们maven项目中引入jar包的class