关于加载器的命名空间,我们首先介绍何为命名空间以及它与双亲委托模式之间的关联。
命名空间定义:该类加载器(定义类加载器)和它父类加载器所加载的类构成的
先介绍几个结论,后续我们通过代码来进行说明
- 每个类加载器都有自己的命名空间,各自命名空间里的类是互相不可见的。
- 子类加载器命名空间可以看见父类加载器命名空间的类,但父类加载器空间里的类不能看到下面子类加载器所加载的类。如果访问会报ClassNotFoundEception
- 各自命名空间的类可以包名,类名(即拥有相同的二进制名称)完全一样,但是在同一个命名空间下,不会存在相同的二进制名称所代表的类
如下代码,两个自定义的加载器代表不同的命名空间(因为本身就是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里面推荐使用双亲委托模式:
- 保证java核心库类的安全,将核心库的创建交给特定的加载器进行加载,不会因为用户自定义相同的类而使内存中存在多个版本的类,比如Object类,保证系统中只有根类加载器进行加载,更加安全
- (后面都是通过结论1的引申和扩展)保证java的核心类库不会被自定义的所替换
- 可以在JVM中创建互相隔离的命名空空间,这类技术在很多框架中得到了实际的应用。
下一讲我们换个角度,即很多SPI中,“破坏双亲”模式的存在:线程上下文加载器