分类: Java开发

JVM 类加载器应用之多版本库支持

问题

开发过程中,咱们经常需要使用不同的库版本,而这些版本又不是向后兼容的,或者出于某种原因需要支持同一库的多个版本。

在这种情况下,默认的类加载器已经是不支持了,因为 loadClass 方法只加载一次特定的类,之后所有的加载请求就直接返回现有 Class 实例的引用了。

解决办法

在这种情况下,可以自定义一个类加载器,用这个具有优先设置的加载器加载需要的库就可以了。

基本代码如下:

import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

public class CustomClassLoader extends ClassLoader {
    private ChildClassLoader childClassLoader;

    public CustomClassLoader(List<URL> classpath) {

        super(Thread.currentThread().getContextClassLoader());
        URL[] urls = classpath.toArray(new URL[classpath.size()]);
        childClassLoader = new ChildClassLoader(urls, new DetectClass(this.getParent()));
    }

    @Override
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        try {
            return childClassLoader.findClass(name);
        } catch(ClassNotFoundException e) {
            return super.loadClass(name, resolve);
        }
    }

    private static class ChildClassLoader extends URLClassLoader {
        private DetectClass realParent;

        public ChildClassLoader(URL[] urls, DetectClass realParent) {
            super(urls, null);
            this.realParent = realParent;
        }

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                Class<?> loaded = super.findLoadedClass(name);
                if(loaded != null) return loaded;
                return super.findClass(name);
            } catch(ClassNotFoundException e) {
                return realParent.loadClass(name);
            }
        }
    }

    private static class DetectClass extends ClassLoader {
        public DetectClass(ClassLoader parent) {
            super(parent);
        }

        @Override
        public Class<?> findClass(String name) throws ClassNotFoundException {
            return super.findClass(name);
        }
    }
}

All Done!

至此就算完成了,通过这种解决办法就可以使单个 jvm 中有多个库版本,或者同一个版本中由于静态变量导致的多实例共存了。

注意:有些库中可能还加载了一些 classpath 下的资源文件,这种情况下就按需要覆盖 getResource 等方法就可以了。

Recent Posts

Docker 容器非 root 用户监听 80 端口

起因是基于 CentOS 的 …

1 年 之前

基于 Docker 定时打印文件

先说背景,喷墨打印机有个很大的…

2 年 之前

Java 运行时反射获取来自继承的泛型

背景 正常情况下 Java 的…

2 年 之前

Java 基于 ByteBuddy 重写系统当前时间

背景 一般单元测试时总会有些代…

2 年 之前

华硕 B450F-Gaming 主板 I211-AT 网卡驱动安装

事情起因是买了块华硕的 ROG…

3 年 之前

PHP 安装 Memcached 扩展

登录服务器挨步执行: # su…

3 年 之前