双亲委派机制,,这是设置出一套规范(并非强制执行和不能破坏),就类似于restful,它是一种规范,你不使用这种规范也可以,但是jdk内部,默认总体逻辑还是按照了双亲委派机制进行了类加载。
举个例子就是,“家族寻宝”--,
你问父亲:我们家族是不是有一个宝藏?
父亲答:我得先问问你爷爷
于是父亲找到你爷爷问:我们家族是不是有一个宝藏?
爷爷答答:我得先问问你爷爷
然后爷爷问你曾爷爷(目前在世的顶层人物):我们家族是不是有一个宝藏?
曾爷爷答你爷爷:有是有,地图给你,我年纪大了不过得你去找。
然后爷爷又对你爸爸说:地图给你,我年纪大了,这个宝藏就交给你去找了
你爸爸找到你说:事情要交给年轻人做,宝藏你去找吧
最后,到你这,你拿着地图去找了宝藏
我想说明的就是这层层的委托,和层层的委派,在询问宝藏的时候,只有到了曾爷爷那才知道有,但是至于谁找宝藏那其实都可以,也可以向下委托
,当然这个例子跟双亲委派有一定的出入,但是也是这样的一种关系,便于大家理解。
我们java程序写完后要编译成class文件,编译成class文件后,到我们jvm中,要使用到对象,那么在这个过程中就涉及到了class文件的加载--加载存储
我们通常说的类的加载过程:
加载->验证->准备->解析->初始化->使用->卸载,
其中,我们说的双亲委派机制就体现在:加载->验证->准备->解析 过程中
双亲委派机制的
图解如下
bootStrapClassLoader是c写的,看不到具体实现,是加载System.getProperty("sun.boot.class.path")->/jre/lib/rt.jar中的class,并缓存
剩余的加载器都是抽象类ClassLoader的子类(直接或者间接继承)
ExtClassLoader是加载System.getProperty("java.ext.dirs")->/jre/lib/ext/*.jar
AppClassLoader是加载System.getProperty("java.class.path")->当前项目的路径
通常我们自定义类加载器加载外部路径的类,比如网络上的类路径、项目之外的类路径。
虽然ExtClassLoader和AppClassLoader是父子加载器,但并不是类的继承关系
而是通过ClassLoader里的parant属性指定的父子关系。
AppClassLoader的parant=ExtClassLoader
而ExtClassLoader的parant是 null ->即表示bootStrapClassLoader,并不是真的null,只不过是因为实现方式是C;
同样我们自定义类加载器,如果没有指定父加载器,那么默认就是parant=AppClassLoader
那么为什么要采用这样一个机制呢?
我个人的理解主要是为了防止途中有人自定义类加载器,拦截JDK内部类,例如String这些,一旦被篡改,那可能造成灾难级的影响
那么双亲委派机制是怎么避免了这个问题呢?
1,所有的类加载时,先问问父加载器有没有加载,这样一级一级向上,这样去保证了String就先丢给了根加载器去加载
2,所有的类加载器除了根加载器,其他的都是直接或者间接继承了ClassLoad,最后的类加载方法是被private final修饰过了,避免我们去修改他
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
3,当我们使用自定义加载器去破坏双亲委派机制时,比如,不优先向上找父加载器,重写loadClass方法,还得去最终findClass当前String类
在时,最终会有个验证阶段,preDefineClass(被private修饰)中去校验当前类是否符合规范-安全,不允许java打头的目录,这样,java.lang.String在验证阶段就被拦截了。
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
// relies on the fact that spoofing is impossible if a class has a name
// of the form "java.*"
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
if (name != null) checkCerts(name, pd.getCodeSource())
return pd;
}
自定义类加载器,我们通常也只是重写findClass方法,这样去加载我们目标类(网路路径,或者特定目录下)
当我们重写loadClass时,就很有可能去破坏双亲委派机制,
自定义类加载器、破坏双亲委派机制、实现热加载的总结在之后再整理