文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java自定义类加载器如何实现类隔离

2022-11-21 22:51

关注

网上java自定义类加载器很多容易找到,但是都是加载的单个类,如果被加载的类,有引用了其他类怎么办呢?接下来看一下如何来处理这种情况

有时候一个项目中可能会引用不同版本的第三方依赖,比如笔者在升级hbase系统时,代理层就同时用到了1.X和2.X版本的hbase-client的jar包。

当时是使用的阿里的SOFAArk来实现的。

它的本质就是是哟个类加载来实现的,接下来就通过一个小例子来通过自定义类加载器实现类隔离。

下面实例实现了在同一个应用里加载了两个类名报名完全相同的Hello 和Dog类:

自定义类加载器

准备

准备工作,在D盘的a,b两个目录下准备Hello.class 和 Dog.class 两个文件,可以使用javac编译。

//a文件夹:
public class Hello {
    private String name;

    public Hello(String name) {
        this.name = name;
    }

    public void sayHello() {
		Dog d =new Dog();
		d.hi();
        System.out.println("a hello " + name + "  "+ this.getClass().getClassLoader());
    }
}

public class Dog {

    public void hi() {
        System.out.println("a hi ...  "+ this.getClass().getClassLoader());
    }
}


//b文件夹
public class Hello {
    private String name;

    public Hello(String name) {
        this.name = name;
    }

    public void sayHello() {
		Dog d =new Dog();
		d.hi();
        System.out.println("b hello " + name + "  "+ this.getClass().getClassLoader());
    }
}

public class Dog {

    public void hi() {
        System.out.println("b hi ...  "+ this.getClass().getClassLoader());
    }
}

通过URLClassLoader来实现【推荐】

类加载器会主动加载被引用的类。比如类A依赖引用了类B ,则只需要主动加载类A即可,类B会自动加载,而且加载类B的类加载器与类A相同。


@Test
public void test0() {
    try {
        System.out.println( this.getClass().getClassLoader());

        URLClassLoader diskLoader = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/a/")});//最后面的斜杠需要添加
        URLClassLoader diskLoader1 = new URLClassLoader(new URL[]{new URL("file:/D:/liubenlong/b/")});

        //加载class文件
        Class clz = diskLoader.loadClass("Hello");
        Constructor constructor = clz.getConstructor(String.class);
        Object obj = constructor.newInstance("tom");

        
        Method method = clz.getMethod("sayHello", null);
        //通过反射调用Test类的say方法
        method.invoke(obj, null);


        Class clz1 = diskLoader1.loadClass("Hello");
        Constructor constructor1 = clz1.getConstructor(String.class);
        Object obj1 = constructor1.newInstance("cat");

        Method method1 = clz1.getMethod("sayHello", null);
        //通过反射调用Test类的say方法
        method1.invoke(obj1, null);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出

jdk.internal.loader.ClassLoaders$AppClassLoader@1f89ab83
a hi ...  java.net.URLClassLoader@3891771e
a hello tom  java.net.URLClassLoader@3891771e
b hi ...  java.net.URLClassLoader@78ac1102
b hello cat  java.net.URLClassLoader@78ac1102

通过继承ClassLoader实现

网上说可以自定义类加载器实现,接下来我们看一下(jdk11直接忽略,因为编译不通过,下面是在jdk8版本测试)。

package 自定义类加载器;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;


public class MyClassLoader extends ClassLoader {
    protected Class<?> findClass(String path, String name) {
        try {
            FileInputStream in = new FileInputStream(path + name + ".class");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len = -1;
            while ((len = in.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            in.close();
            byte[] classBytes = baos.toByteArray();
            return defineClass(name, classBytes, 0, classBytes.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

编写测试类

package 自定义类加载器;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ClassLoaderTest {

    
    @Test
    public void test1() {
        try {
            MyClassLoader diskLoader = new MyClassLoader();
            MyClassLoader diskLoader1 = new MyClassLoader();

            //依赖的类需要提前加载,不是应该自动加载的吗?
            diskLoader.findClass("D:\\liubenlong\\a\\", "Dog");
            diskLoader1.findClass("D:\\liubenlong\\b\\", "Dog");

            //加载class文件
            Class clz = diskLoader.findClass("D:\\liubenlong\\a\\", "Hello");
            Constructor constructor = clz.getConstructor(String.class);
            Object obj = constructor.newInstance("tom");

            Method method = clz.getMethod("sayHello", null);
            //通过反射调用Test类的say方法
            method.invoke(obj, null);


            Class clz1 = diskLoader1.findClass("D:\\liubenlong\\b\\", "Hello");
            Constructor constructor1 = clz1.getConstructor(String.class);
            Object obj1 = constructor1.newInstance("cat");

            Method method1 = clz1.getMethod("sayHello", null);
            //通过反射调用Test类的say方法
            method1.invoke(obj1, null);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

a hi ...
a hello tom
b hi ...
b hello cat

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯