本文在文章底部有配套学习视频
前言
我们上面分析了tomcat通过打破双亲委派机制实现了隔离性。那我们自己要怎么实现呐。
一、如何打破双亲委派机制
那我们分析下我们这个MyClassLoaderTest这个加载类的过程:
(1)从main的classLoader.loadClass()进行加载我们指定的类。
Class clazz = classLoader.loadClass("com.kfit.jvm.User1");System.out.println(clazz.getClassLoader().getClass().getName());
(2)loadClass调用的是父类的Classload.loadClass(name,resolve):
protected Class> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}
这里的代码关注如果c=null的时候,就会使用parent的loadClass(),如果最终parent为null的话,那么就使用bootstrapclass loader。
MyClassLoaderTest的的parent会是哪个呐?
大家可以使用debug下,可以看到是ExtClassLoader:
AppClassLoader在去找父类ExtClassLoader:
这个就是我们之前讲过的双亲委派加载机制。
特别注意:在启动的时候,会先执行加载AppClassLoader去加载jvm核心的一些类库,所以debug的时候,要注意观察当前使用的是哪个类加载器,加载的哪个类。
通过上面的分析如果我们要打破双亲委派机制,那么可以重写loadClass()方法。
二、自定义加载器打破双亲委派机制
我们来看下MyClassLoaderTest2,我们通过重写loadClass()打破了双亲委派机制。
protected Class> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();//需要系统类从系统类进行加载.if("java.lang.Object".equals(name)){Class> clazz = null;ClassLoader system = getSystemClassLoader();try {c = system.loadClass(name);} catch (Exception e) {// ignore}}//从自己的类加载进行加载.if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}}
运行看下结果:
使用了我们自己定义的类加载,但是好像也不能看出打破了双亲加载呐?可以作如下测试:1)在我们的应用中复制一个com.kfit.jvm.User1的类,(1.1)然后运行,发现还是使用的:MyClassLoaderTest2$MyClassLoader(1.2)注释掉我们的上面的loadClass代码,在运行,那么结果是:Launcher$AppClassLoader
(2)尝试去加载:java.lang.String:
会报错:
java.lang.SecurityException: Prohibited package name: java.lang
上面的测试说明我们确实打破了双亲委派机制,另外还说明了一点就是对于JDK核心的类是被保护的,所在包是被禁止的。