Java –手工Classloader隔离

在最近的项目中,我们遇到了一个典型的库冲突问题 。 我们可以控制的一个组件需要特定版本的Apache Commons库,而另一个组件则需要一个不同的版本。 由于外部限制, 我们无法在Container级别指定任何类加载隔离 。 这不是我们的选择。 相反,我们决定要做的是同时使用两个不同的类定义。 为此,我们必须让当前线程类加载器加载一个类,并手动加载第二个 ; 这样,这两个类仍具有相同的完全限定名称。

这种方法的唯一限制是,我们只能通过反射与手动加载的类进行交互,因为当前上下文使用的是不同的类加载器,
具有不同的类定义,我们可以将加载有类加载器的类的实例强制转换或分配给在另一个上下文中定义的变量。 实际上,我们的实现是Classloader本身:

DirectoryBasedParentLastURLClassLoader extends ClassLoader

这个Classloader的特点是我们给它传递了一个文件系统文件夹路径

public DirectoryBasedParentLastURLClassLoader(String jarDir)

我们的实现扫描文件系统路径以生成URL,并使用此信息将它们传递给我们用CustomClassloader封装的URLClassLoader的包装实例

public DirectoryBasedParentLastURLClassLoader(String jarDir) {
    super(Thread.currentThread().getContextClassLoader());

    // search for JAR files in the given directory
    FileFilter jarFilter = new FileFilter() {
        public boolean accept(File pathname) {
            return pathname.getName().endsWith('.jar');
        }
    };

    // create URL for each JAR file found
    File[] jarFiles = new File(jarDir).listFiles(jarFilter);
    URL[] urls;

    if (null != jarFiles) {
        urls = new URL[jarFiles.length];

        for (int i = 0; i < jarFiles.length; i++) {
            try {
                urls[i] = jarFiles[i].toURI().toURL();
            } catch (MalformedURLException e) {
                throw new RuntimeException(
                        'Could not get URL for JAR file: ' + jarFiles[i], e);
            }
        }

    } else {
        // no JAR files found
        urls = new URL[0];
    }

    childClassLoader = new ChildURLClassLoader(urls, this.getParent());
}

通过此设置,我们可以覆盖主要类加载功能的行为,仅当能够找到请求的类时,才优先考虑从文件夹中进行加载并回落到父类加载器

@Override
protected synchronized Class
                      loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
    try {
        // first try to find a class inside the child classloader
        return childClassLoader.findClass(name);
    } catch (ClassNotFoundException e) {
        // didn't find it, try the parent
        return super.loadClass(name, resolve);
    }
}

有了我们的CustomClassloader之后,我们可以通过以下方式使用它:

//instantiate our custom classloader
DirectoryBasedParentLastURLClassLoader classLoader = new DirectoryBasedParentLastURLClassLoader(
        ClassLoaderTest.JARS_DIR    );
//manually load a specific class
Class
                      classManuallyLoaded = classLoader
        .loadClass('paolo.test.custom_classloader.support.MyBean');
//request a class via reflection
Object myBeanInstanceFromReflection = classManuallyLoaded.newInstance();
//keep using the class via reflection
Method methodToString = classManuallyLoaded.getMethod('toString');
assertEquals('v1', methodToString.invoke(myBeanInstanceFromReflection));

这篇文章的想法及其部分代码来自有关Stackoverflow的有趣讨论 GitHub上提供了一个可以正常运行的Maven项目,并进行了大量的单元测试以验证正确的行为。

参考: Java –我们的JCG合作伙伴 Paolo Antinori的Handmade Classloader IsolationSomeday Never Comes博客上发表。

翻译自: https://www.javacodegeeks.com/2013/03/java-handmade-classloader-isolation.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值