背景:在引入第三方jar包以后,偶尔会遇到不同jar包中的类冲突。这里所说的冲突,是指类的包名和类型完全相同(有的时候希望同时使用相同类的不同版本)。
参考地址:https://baijiahao.baidu.com/s?id=1636309817155065432&wfr=spider&for=pc
处理思路:见上图,使用原生的类加载是实现不了这个功能的,需要使用自定义类加载器,分别从不同jar中或者目录加载class文件,然后进行实例化,最后使用反射来调用(因Class动态变化,所以需要反射机制)
代码实现:
package com.mp.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassloader extends ClassLoader {
private String libPath;
public MyClassloader(String path) {
libPath = path;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
if (libPath.endsWith(".zip") || libPath.endsWith(".jar")) {
try {
return this.loadClassFromJarOrZip(name);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
String fileName = getFileName(name);
File file = new File(libPath, fileName);
try (FileInputStream is = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
int len = 0;
while ((len = is.read()) != -1) {
bos.write(len);
}
byte[] data = bos.toByteArray();
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
// 获取要加载 的class文件名
private String getFileName(String name) {
int index = name.lastIndexOf(".");
if (index > 0) {
return name.substring(index + 1) + ".class";
} else {
return name + ".class";
}
}
private Class<?> loadClassFromJarOrZip(String className)
throws MalformedURLException, ClassNotFoundException {
File file = new File(libPath);
URL url = file.toURI().toURL();
URLClassLoader classLoader = null;
try {
classLoader =
new URLClassLoader(new URL[] {url}, Thread.currentThread().getContextClassLoader());
return classLoader.loadClass(className);
} finally {
if (classLoader != null) {
try {
classLoader.close();
} catch (IOException e) {
// pass
}
}
}
}
}
package com.mp.util;
import java.lang.reflect.Method;
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("使用目录加载class");
call("/tmp/t1", "basic.javastd.HelloWorld", "printVersion");
call("/tmp/t2", "basic.javastd.HelloWorld", "printVersion");
System.out.println("使用jar加载class");
call("/tmp/t1/javastd-0.0.1-SNAPSHOT.jar", "basic.javastd.HelloWorld", "printVersion");
call("/tmp/t2/javastd-0.0.1-SNAPSHOT.jar", "basic.javastd.HelloWorld", "printVersion");
}
public static void call(String classFilePath, String className, String methodName) {
ClassLoader loader = new MyClassloader(classFilePath);
try {
Class<?> loadClass = loader.loadClass(className);
Method declaredMethod = loadClass.getDeclaredMethod(methodName);
declaredMethod.invoke(loadClass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出:
使用目录加载class
v1 static code
version 1
v2 static code
version 2
使用jar加载class
v1 static code
version 1
v2 static code
version 2