业务背景
我有个业务,引用了第三方的包来实现,然后这个第三方的包中基本都能满足我的业务需求。但是,其中有某个方法,或者某个类的一些方法,跟我的需求不匹配。我想改一下这个方法,但是又没有这个包的源码(如果你直接反编译然后再重新打包,每次有变动就再修改再打包,那当我没说,文章到此结束哈哈)。
比如,你引用了一个数字处理的第三方包,里面提供了对数据各种丰富的操作,but,其中有个A方法是调B方法返回的所有正数进行操作的,但你想要的是A方法中处理的数是非负数,也就是0也能加入进去。此时因为各种依赖的原因,你只能用A方法来处理数据,那么这时候,我们就需要修改B方法,让它把0加上。
代码
一.先上代码先,因为上面的故事是虚构的,所以我需要自己手敲一个小demo。 首先是假设我们引用的第三方包,使用idea建一个项目然后打成包放进项目的lib文件夹中或者使用maven,我这里用maven
jar包结构
Ajar类
/**
* jar包中的a类<br>
* jar包中的a类<br>
*
* @author jasion
* @date 2021-10-28 16:08
*/
package com.xiaojian;
public class Ajar {
public void say(){
System.out.println("我是jar包中的say()方法");
}
}
AUseJar类
/**
* 使用Ajar这个类的包<br>
* 使用Ajar这个类的包<br>
*
* @author jasion
* @date 2021-10-28 16:18
*/
package com.xiaojian;
public class AUseJar {
public void useA(){
Ajar ajar=new Ajar();
ajar.say();
}
}
这个jar包中就是AuserJar类中的useA方法,调用了Ajar类中的say方法,简单来说就是在同一个jar包中,我用一个类调用了另一个类的方法(调用同类中的同个方法也是一样的效果,只不过是下面重写哪个类的区别而已)
二.此时我有个工程项目,导入了这个包
然后在这个项目里面建了个测试类Test,用来调用jar包中的类的方法
/**
* 主要测试类<br>
* 主要测试类<br>
*
* @author jasion
* @date 2021-10-28 16:13
*/
package com.jasion;
import com.xiaojian.AUseJar;
import com.xiaojian.Ajar;
public class Test {
public static void main(String[] args) {
//直接使用替换的类
// Ajar ajar = new Ajar();
// ajar.say();
//间接使用替换的类
AUseJar aUseJar=new AUseJar();
aUseJar.useA();
// System.out.println(System.getProperty("java.ext.dirs"));
// System.out.println(System.getProperty("java.class.path"));
}
}
此时我们跑下这个测试类,会打印出什么呢? 没错,就是
到这里为止,都是正常的操作。那这时候,假设我想在不改变jar包源码的情况下,重写Ajar中的say()方法,要怎么弄呢? 三.这个时候只需要我们在我们业务项目的建立一个跟jar包Ajar类相同的包路径以及相同的类名,然后重写say()方法就行了。
/**
* 本地的Ajar<br>
* 本地的Ajar<br>
*
* @author jasion
* @date 2021-10-28 16:14
*/
package com.xiaojian;
public class Ajar {
public void say(){
System.out.println("我是业务项目中的say()方法");
}
}
这个时候我们再重新跑一下测试类中的方法,会打印出什么呢?
jar包中的say方法被替换掉了!很强有没有。
结论
先说结论:如果想要替换jar包中的某个类或者某个方法,只需要在项目中创建一个相同包路径,相同类名的类就能够替换调了,而不需要修改jar包的源码。
原因
首先看到我测试类里面有个打印某个属性的代码
这个是怎么来的呢,这个实际上是jdk源码的应用类加载器中的。
这个路径记录应用类加载器加载的类路径,也就是应用类加载器要加载哪些路径下的类。我们把它跑一下,我这里使用了换行符号换了下,得到这个路径。
我们可以看到,对应应用类加载器来说,它是先去加载本项目的classpath下面的类,再去加载lib中引用的包中的中类。(本来想去看下顺序加载的源码的,结构是本地方法实现的看不了,或许是我没找到~)根据类加载的双亲委派机制(类加载的不拓展开了,后面有空再写一篇),因为Ajar这个类已经加载过了,所以当AUserJar用到Ajar这个类的时候,会从应用类加载器缓存中查找并返回业务项目中的Ajar而不会重新加载jar包里面的。