1).打破双亲委托机制的作用:主要实现不同类、类库、jar包、jdk的共存、隔离
    Tomcat打破双亲委派机制、Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行?
    我们思考一下:Tomcat是个web容器, 那么它要解决什么问题:
    1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是 独立的,保证相互隔离。
    2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程 序,那么要有10份相同的类库加载进虚拟机。
    3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的 类库和程序的类库隔离开来。
    4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中 运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。 再看看我们的问题:Tomcat 如果使用默认的双亲委派类加载机制行不行? 答案是不行的。为什么?
    第一个问题,如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认 的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。
    第二个问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性。
    第三个问题和第一个问题一样。
    第四个问题,我们想我们要怎么实现jsp文件的热加载,jsp 文件其实也就是class文 件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp 是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想 到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载 器。重新创建类加载器,重新加载jsp文件

    2). 再来一个沙箱安全机制示例,尝试打破双亲委派机制,用自定义类加载器加载我们自己实现的java.lang.String.class
    继承ClassLoader,重写loadClass

    1. package com.tuling.jvm;
    2. import java.io.FileInputStream;
    3. import java.lang.reflect.Method;
    4. public class MyClassLoaderTest {
    5. static class MyClassLoader extends ClassLoader {
    6. private String classPath;
    7. public MyClassLoader(String classPath) {
    8. this.classPath = classPath;
    9. }
    10. private byte[] loadByte(String name) throws Exception {
    11. name = name.replaceAll("\\.", "/");
    12. FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
    13. int len = fis.available();
    14. byte[] data = new byte[len];
    15. fis.read(data);
    16. fis.close();
    17. return data;
    18. }
    19. protected Class<?> findClass(String name) throws ClassNotFoundException {
    20. try {
    21. byte[] data = loadByte(name);
    22. return defineClass(name, data, 0, data.length);
    23. } catch (Exception e) {
    24. e.printStackTrace();
    25. throw new ClassNotFoundException();
    26. }
    27. }
    28. /**
    29. * 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
    30. */
    31. protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
    32. synchronized (getClassLoadingLock(name)) {
    33. // First, check if the class has already been loaded
    34. Class<?> c = findLoadedClass(name);
    35. if (c == null) {
    36. // If still not found, then invoke findClass in order
    37. // to find the class.
    38. long t1 = System.nanoTime();
    39. /*
    40. if(name.startsWith("com.tuling")){
    41. c = findClass(name);
    42. }
    43. else {
    44. c = this.getParent().loadClass(name);
    45. }*/
    46. c = findClass(name);
    47. // this is the defining class loader; record the stats
    48. sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    49. sun.misc.PerfCounter.getFindClasses().increment();
    50. }
    51. if (resolve) {
    52. resolveClass(c);
    53. }
    54. return c;
    55. }
    56. }
    57. }
    58. public static void main(String args[]) throws Exception {
    59. MyClassLoader classLoader = new MyClassLoader("D:/test");
    60. //尝试用自己改写类加载机制去加载自己写的java.lang.String.class
    61. Class clazz = classLoader.loadClass("java.lang.String");
    62. //Class clazz = classLoader.loadClass("com.tuling.myString.String");
    63. Object obj = clazz.newInstance();
    64. Method method= clazz.getDeclaredMethod("sout", null);
    65. method.invoke(obj, null);
    66. System.out.println(clazz.getClassLoader().getClass().getName());
    67. }
    68. }

    3).String.class
    image.png
    image.png

    4).结果
    1.Class clazz = classLoader.loadClass(“java.lang.String”);的结果加载失败了:
    image.png
    提示无法加载java.lang开头的包,java沙箱安全机制限制了。

    2.通过改String包路径,用Class clazz = classLoader.loadClass(“com.tuling.myString.String”);的结果成功加载了:
    image.png

    5).总结,继承ClassLoader,重写loadClass,在loadClass判断包名看是否用双亲委派机制来加载自己写的String类即可实现打破双亲委派机制来加载自己写的String类。