$1.5、加载器的命名空间详解及双亲加载器总结

关于加载器的命名空间,我们首先介绍何为命名空间以及它与双亲委托模式之间的关联。

命名空间定义:该类加载器(定义类加载器)和它父类加载器所加载的类构成的

先介绍几个结论,后续我们通过代码来进行说明

  1. 每个类加载器都有自己的命名空间,各自命名空间里的类是互相不可见的。
  2. 子类加载器命名空间可以看见父类加载器命名空间的类,但父类加载器空间里的类不能看到下面子类加载器所加载的类。如果访问会报ClassNotFoundEception
  3. 各自命名空间的类可以包名,类名(即拥有相同的二进制名称)完全一样,但是在同一个命名空间下,不会存在相同的二进制名称所代表的类

如下代码,两个自定义的加载器代表不同的命名空间(因为本身就是2个不同的对象),那么它们所产生的class对象也就不一样,我先看第一种场景:

  public static void main(String[] args) throws Exception {
        //定义2个加载器,自然 因为他们不存在包含关系,对应的命名空间也是不一样的
        MyClassLoader loader1 = new MyClassLoader("loader1");
        Class<?> aClass = loader1.loadClass("com.example.demo.classloader.Person");

        //定义第二个加载器
        MyClassLoader loader2 = new MyClassLoader("loader2");
        Class<?> aClass2 = loader2.loadClass("com.example.demo.classloader.Person");
        System.out.println(aClass == aClass2);
        //同时调用反射方法,看能不能访问
        Method test = aClass.getMethod("test", Object.class);
        test.invoke((Person) aClass.newInstance(), (Person) aClass2.newInstance());
    }

对应的Person类

public class Person {

    public Person person;

    public void test(Object obj) {
        this.person = (Person) obj;
        System.out.println("method invoke");
    }

我们运行结果:这种情况下,是因为两个自定义的加载器都是委托父类进行加载的,因此他们的命名空间一样,返回一样的class对象,现在我们使用自定义的加载器进行加载:

 public static void main(String[] args) throws Exception {
        //定义2个加载器,自然 因为他们不存在包含关系,对应的命名空间也是不一样的
        MyClassLoader loader1 = new MyClassLoader(null,"loader1");
        Class<?> aClass = loader1.loadClass("com.example.demo.classloader.Person");

        //定义第二个加载器
        MyClassLoader loader2 = new MyClassLoader(null,"loader2");
        Class<?> aClass2 = loader2.loadClass("com.example.demo.classloader.Person");
        System.out.println(aClass == aClass2);
        //同时调用反射方法,看能不能访问
        Method test = aClass.getMethod("test", Object.class);
        test.invoke((Person) aClass.newInstance(), (Person) aClass2.newInstance());
    }

执行得到的结果首先false代表两个class对象不是同一个,后者进行方法调用时,直接报类型转换错误,虽然两者的二进制名称完全一样,但还是互相不可访问的,上述结论得到验证。

双亲模式总结

我们学了这么久的双亲和看到了双亲模式所带来的各种现象,我们总结一下双亲模式的好处,为什么JVM里面推荐使用双亲委托模式:

  1. 保证java核心库类的安全,将核心库的创建交给特定的加载器进行加载,不会因为用户自定义相同的类而使内存中存在多个版本的类,比如Object类,保证系统中只有根类加载器进行加载,更加安全
  2. (后面都是通过结论1的引申和扩展)保证java的核心类库不会被自定义的所替换
  3. 可以在JVM中创建互相隔离的命名空空间,这类技术在很多框架中得到了实际的应用。

下一讲我们换个角度,即很多SPI中,“破坏双亲”模式的存在:线程上下文加载器

发布了12 篇原创文章 · 获赞 1 · 访问量 369

猜你喜欢

转载自blog.csdn.net/weixin_39800596/article/details/103570811