JAVA 类隔离加载解决JAR包冲突

JAVA通过类加载器名称、包名、类名、方法名唯一确定一个方法,因此JAR包冲突可以通过改变类加载器实现。JAVA8及以下版本破坏双亲委派机制,实现JAVA类隔离加载依赖 loadClass()方法实现。JAVA9开始模块化后,加载器之间的关系不再是双亲委派模型的树状结构,自然可以隔离不同类的加载。

建立maven工程,在 com.example 包下有TestA和TestB两个类。TestA调用了TestB中的方法。现希望将TestA与其所有的引用,也就是TestB类使用同一个自定义类加载器加载。代码如下。TestA与TestB是测试类,MyClassLoaderCustom是自定义类加载器。如果不使用这个类加载器,在使用Main类的main方法启动项目调用TestA的main时,TestA与TestB会被AppClassLoader加载;经过重写loadClass方法的处理后,TestA和TestB都使用自定义类加载器进行加载。

package com.example;

public class TestA {

    public static void main(String[] args) {
        TestA testA = new TestA();
        testA.hello();
    }

    public void hello() {
        System.out.println("com.example.TestA: " + this.getClass().getClassLoader());
        TestB testB = new TestB();
        testB.hello();
    }
}
package com.example;

public class TestB {
    public void hello() {
        System.out.println("com.example.TestB: " + this.getClass().getClassLoader());
    }
}
package com.example;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class MyClassLoaderCustom extends ClassLoader {

    private ClassLoader jdkClassLoader;

    private Map<String, String> classPathMap = new HashMap<>();

    public MyClassLoaderCustom(ClassLoader jdkClassLoader) {
        this.jdkClassLoader = jdkClassLoader;
        classPathMap.put("com.example.TestA", TestA.class.getResource("").getPath()+"TestA.class");
        classPathMap.put("com.example.TestB", TestB.class.getResource("").getPath()+"TestB.class");
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {        
        Class result = findLoadedClass(name);
        // 这里是为了防止类重复加载
        if(result==null){
	        try {
	            //这里要使用 JDK 的类加载器加载 java.lang 包里面的类
	            result = jdkClassLoader.loadClass(name);
	        } catch (Exception ignored) {
	        }
	        if (result != null) {
	            return result;
	        }
	        String classPath = classPathMap.get(name);
	        File file = new File(classPath);
	        if (!file.exists()) {
	            throw new ClassNotFoundException();
	        }
	
	        byte[] classBytes = getClassData(file);
	        if (classBytes.length == 0) {
	            throw new ClassNotFoundException();
	        }
	        return defineClass(name, classBytes, 0, classBytes.length);
        }
        return result;
    }

    private byte[] getClassData(File file) {
        try (InputStream ins = new FileInputStream(file); ByteArrayOutputStream baos = new
                ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new byte[]{};
    }
}

package com.example;

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        //这里取AppClassLoader的父加载器也就是ExtClassLoader作为MyClassLoaderCustom的jdkClassLoader
        MyClassLoaderCustom myClassLoaderCustom = new MyClassLoaderCustom(Thread.currentThread().getContextClassLoader().getParent());
        Class testAClass = myClassLoaderCustom.loadClass("com.example.TestA");
        Method mainMethod = testAClass.getDeclaredMethod("main", String[].class);
        mainMethod.invoke(null, new Object[]{args});
    }
}

输出结果可以看到,两个类都使用了自定义类加载器进行加载。

com.example.TestA: com.example.MyClassLoaderCustom@135fbaa4
com.example.TestB: com.example.MyClassLoaderCustom@135fbaa4

参考网站

如何实现Java类隔离加载?
呵,十个双亲委派问题,想问倒我?
如何手动获取maven项目中resource目录下的文件(包括二级目录下的文件)
ClassLoader源码分析

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值