如果在同一个JVM中使用不同jar包下面包名和类名完全相同的类

背景:在引入第三方jar包以后,偶尔会遇到不同jar包中的类冲突。这里所说的冲突,是指类的包名和类型完全相同(有的时候希望同时使用相同类的不同版本)。

如果在同一个JVM中使用不同jar包下面包名和类名完全相同的类_第1张图片

参考地址: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

你可能感兴趣的:(JAVA)