在不修改源码的前提下扩展自己的需求

前天禅道上给了我一个BUG,我最终找到了原因但是 需要修改原有框架里面的东西,修改的东西不多,但是如何去修改呢?

第一时间我想到的就是先将框架的jar包进行反编译修改完之后在进行打包,在放到仓库里面。于是在网上搜集这样的工具,网上也有很多反编译的工具,但是我看下来有一个问题,每一个工具都不能原模原样的把源码里面的东西进行复原,并且由于不同的解析语法,可能解析出来的代码格式也不一致,比如有的解析支持Java8的lamdam表达式和stream流,有的却不支持。

于是就暂时举步维艰,今天早上来问了下老大,人家给了我一个最简单的方案:

如果你想要修改你引入Jar包的源码,你可以在你的项目目录下创建一个相同路径的类,再把要扩展的类的内容全部都粘贴复制到自己定义的类,然后再次基础上进行自己的扩展,这样的话JVM类加载器就会加载你自己定义的类,而不会加载框架里的类。原因肯定是跟JVM类加载的顺序有关。

类加载的过程

在不修改源码的前提下扩展自己的需求

  • class file存在于本地硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载到JVM当中来根据这个文件实例化出n个一模一样的实例。
  • class file加载到JVM中,被称为DNA元数据模板,放在方法区
  • .class文件->JVM->最终成为元数据模板,此过程就要一个运输工具(类装载器Class Loader),扮演一个快递员的角色。

在不修改源码的前提下扩展自己的需求

加载阶段完成三件事

  1. 通过一个类的全限定名获取定义此类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的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,而且同一个路径的类只会加载一次,于是这就引起了类的加载优先级的问题,由结果可得知,

  1. 先是Bootstrap ClassLoader加载 java、javax、sun等开头的类,
  2. Extension ClassLoader加载java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的 jre/1ib/ext子目录(扩展目录)下加载类库。
  3. AppClassLoader 加载我们正常项目中的class
  4. AppClassLoader加载我们maven项目中引入jar包的class
上一篇:类加载问题


下一篇:SpringAop-ProxyFactory