使用URLClassLoader加载不同Jar包中的相同包名相同类名的类
开发中会遇到这种情况,使用一项功能需要依赖jar,然而一段时间后这些包升级,包名和类名均不变的情况下功能发生变化。旧的jar依然需要使用,而新功能却需要使用新升级的jar。这时如果依赖两个包,使用时会造成冲突。因此需要进行jar隔离。有很多的隔离框架可以使用,如jarlinks。本文将演示通过URLClassLoader加载器来进行隔离的基本方法,而实应用中的的一些隔离框架中也会看到此种方式的使用。
首先原有系统内部依赖version782.jar包,并用到该包中的RequestHead类。而此时又希望用到version713.jar包中的RequestHead类。直接增加maven依赖会产生冲突,这里通过URLClassLoader进行加载。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import xxx.RequestHead;
import org.apache.commons.codec.binary.Hex;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoadTest {
public static void main(String[] args) throws Exception {
URL url = new File("/version713.jar").toURI().toURL();
// 创建URLClassLoader,并设置父加载器为null,这样通过加载器加载
// 类时就不会通过父加载器去通过classpath中加载version782.jar
// 中的RequestHead
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, null);
// 加载version713.jar包中的RequestHead类
Class<?> clz = urlClassLoader.loadClass("xxx.RequestHead");
// 加载序列化工具类,由于要通过反射调用ProtoBufferCore中的方法
// 而且需要指定Class参数,这个参数需要与ProtoBufferCore使用相同
// 的类加载器,因此后续加载参数CtripBusinessBean
Class<?> pb = urlClassLoader.loadClass("xxx.ProtoBufferCore");
// 加载参数CtripBusinessBean,调用ProtoBufferCore方法时使用
Class<?> ctripBizBean = urlClassLoader.loadClass("xxx.CtripBusinessBean");
RequestHead head782 = new RequestHead();
Object head713 = clz.newInstance();
Object pbObject = pb.newInstance();
System.out.println(pbObject);
System.out.println(JSON.toJSONString(head713, SerializerFeature.WriteNullStringAsEmpty));
System.out.println(JSON.toJSONString(head782, SerializerFeature.WriteNullStringAsEmpty));
clz.getField("appId").set(head713,"999111");
// 这里进行方法调用时,执行参数上文中加载的ctripBizBean,
// 这样才能保证正确调用
Method toByteArrayMethod = pb.getMethod("toByteArray",ctripBizBean);
String hex = Hex.encodeHexString((byte[]) toByteArrayMethod.invoke(pbObject,head713));
System.out.println(hex);
}
}
参考
java虚拟机规范5.3节