gpt4 book ai didi

java - 在 JVM 中并行加载同一库的不同版本

转载 作者:行者123 更新时间:2023-12-02 13:21:50 24 4
gpt4 key购买 nike

我需要测试同一库但不同版本的执行之间的差异 - 并且在运行时。因此我需要加载很多具有相同包名的类。整个执行仅从一个类开始,其余所有类都作为其依赖项。

我尝试将库 #1 作为项目文件加载(即通过 ClassPath 类加载器),将库 #2 作为 jar 加载,并通过 UrlClassLoader 加载其类。

问题是当我从 UrlClassLoader 加载类时 - 所有依赖类均取自库 #1,这些类已由 ClassPath 类加载器加载。

我知道类加载器形成了一个层次结构,从 Bootstrap 类加载器开始,然后结束自定义类加载器 - 但通过哪种方式,您可以使 Java 不仅加载一个明确提到的类,还加载自定义类中的所有依赖关系树装载机?

最佳答案

您可以从类路径加载一个版本,并使用 URLClassLoader 加载其余版本,方法是传递 parent: null 并使用反射来实例化成员。

另一种方法是使用自定义类加载器,基于描述的方法 here可能看起来像:

import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;

public class ParentLastClassLoader extends ClassLoader {

private ClassLoader parentClassLoader;
private URLClassLoader noParentClassLoader;

public ParentLastClassLoader(URL[] urls, ClassLoader parent) {
super(parent);
this.noParentClassLoader = new URLClassLoader(urls, null);
}

public ParentLastClassLoader(URL[] urls) {
super(Thread.currentThread().getContextClassLoader());
this.noParentClassLoader = new URLClassLoader(urls, null);
}

public ParentLastClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
super(parent);
this.noParentClassLoader = new URLClassLoader(urls, null, factory);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
try {
// resolve using child class loader
return noParentClassLoader.loadClass(name);
} catch (ClassNotFoundException ex) {
// resolve using parent class loader
return super.loadClass(name, resolve);
}
}
}

我为此创建了一个 POC this Git repo 。详细信息位于 README.md 中。

POC内容

考虑到您有一个包含多个版本的库(项目 v1、v2、v3),由以下部分组成:

我们希望我们的库有这个界面:

public interface Core {

String getVersion();
String getDependencyVersion();
}

实现者:

package sample.multiversion;

import sample.multiversion.deps.CoreDependency;

public class ImportantCore implements Core {

private Utility utility;
private CoreDependency coreDependency;

public ImportantCore() {
utility = new Utility();
coreDependency = new CoreDependency();
}

public String getVersion() {
return utility.getVersion();
}

public String getDependencyVersion() {
return coreDependency.getVersion();
}
}

使用同一库的另一个类:

package sample.multiversion;

public class Utility {

public String getVersion() {
return "core-v1";
}
}

最后,库(项目 v1、v2、v3)具有依赖项(项目 v1dep、v2dep、v3dep),其中包含:

package sample.multiversion.deps;

public class CoreDependency {

public String getVersion() {
return "core-dep-v1";
}
}

然后我们可以加载所有三个版本:

  1. V1 - 来自文件
  2. V2 - 来自文件
  3. V3 - 来自类路径

代码是:

    // multiple versions of the same library to be used at the same time
URL v1 = Paths.get("./../v1/build/libs/v1.jar").toUri().toURL();
URL v2 = Paths.get("./../v2/build/libs/v2.jar").toUri().toURL();

// library dependencies
URL v1Dep = Paths.get("./../v1dep/build/libs/v1dep.jar").toUri().toURL();
URL v2Dep = Paths.get("./../v2dep/build/libs/v2dep.jar").toUri().toURL();

/**
* version 1 and 2 loaders
* - these loaders do not use the root loader - Thread.currentThread().getContextClassLoader()
* - using the root loader new URLClassLoader(new URL[]{v1, v1Dep}, Thread.currentThread().getContextClassLoader());
* will solve any class with the root loader and if no class is found then the child loader will be used
* - because version 3 is loaded from classpath, the root loader should not be used to load version 1 and 2
* - null needs to be passed to parent argument, else will not work
*/
URLClassLoader loaderV1 = new URLClassLoader(new URL[]{v1, v1Dep}, null);
URLClassLoader loaderV2 = new URLClassLoader(new URL[]{v2, v2Dep}, null);

/**
* Use custom class loader for loading classes first from children and last from parent
*/
ParentLastClassLoader loaderV1Alt = new ParentLastClassLoader(new URL[]{v1, v1Dep});
ParentLastClassLoader loaderV2Alt = new ParentLastClassLoader(new URL[]{v2, v2Dep});
ParentLastClassLoader loaderV3Alt = new ParentLastClassLoader(new URL[]{});

// get class from loader
Class<?> coreV1Class = loaderV1.loadClass("sample.multiversion.ImportantCore");
Class<?> coreV2Class = loaderV2.loadClass("sample.multiversion.ImportantCore");

// get class from loader - custom version
Class<?> coreV1AltClass = loaderV1Alt.loadClass("sample.multiversion.ImportantCore");
Class<?> coreV2AltClass = loaderV2Alt.loadClass("sample.multiversion.ImportantCore");
Class<?> coreV3AltClass = loaderV3Alt.loadClass("sample.multiversion.ImportantCore");

// create class instance
Object coreV1Instance = coreV1Class.newInstance();
Object coreV2Instance = coreV2Class.newInstance();

// create class instance - obtained from custom class loader
Object coreV1AltInstance = coreV1AltClass.newInstance();
Object coreV2AltInstance = coreV2AltClass.newInstance();

// note that this is loaded from classpath
Core coreV3Instance = new ImportantCore();
Core coreV3AltInstance = (Core)coreV3AltClass.newInstance();

// get version
String v1Str = (String) coreV1Class.getMethod("getVersion").invoke(coreV1Instance);
String v2Str = (String) coreV2Class.getMethod("getVersion").invoke(coreV2Instance);
String v1AltStr = (String) coreV1AltClass.getMethod("getVersion").invoke(coreV1AltInstance);
String v2AltStr = (String) coreV2AltClass.getMethod("getVersion").invoke(coreV2AltInstance);

String v3Str = coreV3Instance.getVersion();
String v3AltStr = coreV3AltInstance.getVersion();

// get version of dependency
String v1DepStr = (String) coreV1Class.getMethod("getDependencyVersion").invoke(coreV1Instance);
String v2DepStr = (String) coreV2Class.getMethod("getDependencyVersion").invoke(coreV2Instance);
String v1AltDepStr = (String) coreV1AltClass.getMethod("getDependencyVersion").invoke(coreV1AltInstance);
String v2AltDepStr = (String) coreV2AltClass.getMethod("getDependencyVersion").invoke(coreV2AltInstance);

String v3DepStr = coreV3Instance.getDependencyVersion();
String v3AltDepStr = coreV3AltInstance.getDependencyVersion();

System.out.println(String.format("V1 loader :: version = '%s' :: dependency_version = '%s'", v1Str, v1DepStr));
System.out.println(String.format("V2 loader :: version = '%s' :: dependency_version = '%s'", v2Str, v2DepStr));
System.out.println(String.format("V3 loader :: version = '%s' :: dependency_version = '%s'", v3Str, v3DepStr));

System.out.println(String.format("V1 custom loader :: version = '%s' :: dependency_version = '%s'", v1AltStr, v1AltDepStr));
System.out.println(String.format("V2 custom loader :: version = '%s' :: dependency_version = '%s'", v2AltStr, v2AltDepStr));
System.out.println(String.format("V3 custom loader :: version = '%s' :: dependency_version = '%s'", v3AltStr, v3AltDepStr));

注意: Core 接口(interface)可以是由项目 v1、v2、v3 使用的另一个项目的一部分,这可以让我们强制转换新创建的实例并以类型安全的方式工作。但这可能并非每次都可行。

关于java - 在 JVM 中并行加载同一库的不同版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43541129/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com