zoukankan      html  css  js  c++  java
  • Java中的类加载器--Class loader

      学习一下Java中的类加载器,这个是比较底层的东西,好好学习、理解一下。

     一、类加载器的介绍

    1、类加载器:就是加载类的工具,在java程序中用到一个类,java虚拟机首先要把这个类的字节码加载到内存中来,但是通常这个类的原始信息是放在硬盘上的classpath指定的路径下,将.class文件的内容加载到硬盘上来,再对它进行一些处理,处理完的结果就是字节码,这些工作就是类加载器在做

    扩展一下一个java程序的生命周期:(这个总是记不住,无语了。。。)

    以Java为例:

    电脑是不能直接执行Java程序的,一个.java程序要想被执行,首先需要编译器将高级的.java程序文件编译成.class字节码片段,字节码经过JVM(解释器)的处理后生成电脑可以直接执

    行的机器码,至此java程序才能得以正确运行。

    2、Java虚拟机中可以安装多个类加载器,系统中默认的有三个类加载器:Bootstrap、ExtClassLoader、AppClassLoader,每个类加载器加载特定位置的类

    3、类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有一个类加载器不是java类,这个加载器就是Bootstrap类加载器。

    注意:Bootstrap类加载不是java类,它是嵌套在java虚拟机内核中的,java虚拟机内核一启动,它就已经在那里了,它是用C++语言写的一段二进制代码,它可以去加载别的类以及其他的类加载器。

     1         // sun.misc.Launcher$AppClassLoader
     2         String classLoaderName1 = ClassLoaderTest.class.getClassLoader().getClass().getName();
     3         System.out.println(classLoaderName1);
     4         
     5         // 为 null 说明System类是由Bootstrap类加载器加载的
     6         System.out.println(System.class.getClassLoader());
     7         
     8         // 循环理解 类加载器中的父子关系
     9         ClassLoader loader = ClassLoaderTest.class.getClassLoader();
    10         while(loader != null){
    11             System.out.println(loader.getClass().getName());
    12             loader = loader.getParent();
    13         }
    14         System.out.println(loader);

    4、java虚拟机中的所有类加载器采用具有父子关系的属性结构进行组织,在实例化每个类加载器对象时候,需要为其指定一个父级类加载器对象,或者默认采用系统类加载器为其父级类加载器

    5、类加载器的委托机制

    当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

    1)首先,当前线程的类加载器去加载线程中的第一个类

    2)如果类A中引用了类B,java虚拟机将使用类A的类加载器来加载类B

    3)还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器来加载某个类

    每个类加载器加载类时,又先委托给其上级类加载器

    1)当所有祖宗类加载器没有加载到类时,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是去找发起者类加载器的儿子,因为没有getChild()方法,就算是有,那有多个儿子,应该交给哪一个呢?

     二、编写自己的类加载器

     1、自定义类加载器的原理

    从jdk文档中找到的一个案例:(主要就是重写findClass()方法,加载二进制class文件,转换成字节码)

     1 //网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。示例实现如下: 
     2 
     3      class NetworkClassLoader extends ClassLoader {
     4          String host;
     5          int port;
     6 
     7          public Class findClass(String name) {
     8              byte[] b = loadClassData(name);
     9              return defineClass(name, b, 0, b.length);
    10          }
    11 
    12          private byte[] loadClassData(String name) {
    13              // load the class data from the connection
    14               . . .
    15          }
    16      }

    2、自己定义classLoader

    这个写起来真的费劲啊,听着张老师的视频讲解都感觉很费劲,这个要是自己能写出来,那真的要对class的加载机制很熟悉,看着张老师解决问题的能力,感觉自己真的还有很多东西需要学习,自己的解决问题的能力几乎是0,但是这背后肯定离不开强大的知识体系的支撑,自己的基础知识不够扎实,如何去谈解决问题,可能你都发现不了问题。

    代码拿出来,不过还有很多难点,希望以后自己能够完全理解(这个重写ClassLoader应该不难,但是如何让你写的ClassLoader来使用起来,我觉得这个是比较难的,你要知道在java中的类加载机制,树状的类加载关系,只有当父类加载不到类的时候,才会轮到你自己重写的ClassLoader来加载所需要加载的类,问题是如何让父类加载不到,而让你自己的ClassLoader加载到呢?这个是问题的所在,也是解决问题的切入点

     1 public class MyClassLoader extends ClassLoader{
     2 
     3     private String classDir;
     4     
     5     public static void main(String[] args) throws Exception{
     6         String srcPath = args[0];
     7         String destDir = args[1];
     8         
     9         String destFileName = srcPath.substring(srcPath.lastIndexOf("\")+1);
    10         String destPath = destDir + "\" + destFileName;
    11         
    12         FileInputStream fis = new FileInputStream(srcPath);
    13         FileOutputStream fos = new FileOutputStream(destPath);
    14         cypher(fis, fos);
    15         fis.close();
    16         fos.close();
    17         
    18     }
    19     
    20     private static void cypher(InputStream ips, OutputStream ops) throws Exception{
    21         int b = -1;
    22         
    23         // 读字节流读到最后为 -1
    24         while((b = ips.read()) != -1 ){
    25             // 进行异或运算 就是把二进制中的0编程1 1变成0 进行简单的加密
    26             ops.write(b ^ 0xff);
    27         }
    28     }
    29 
    30     @Override
    31     protected Class<?> findClass(String name) throws ClassNotFoundException {
    32         // 截取包名中最后的类的名称
    33         String classFileName = classDir + "\" + name.substring(name.lastIndexOf(".")+1) + ".class";
    34         System.out.println("MyClassLoader中加载的类的名称:" + classFileName);
    35         try {
    36             FileInputStream fis = new FileInputStream(classFileName);
    37             ByteArrayOutputStream bos = new ByteArrayOutputStream();
    38             cypher(fis, bos);
    39             fis.close();
    40             
    41             byte[] bytes = bos.toByteArray();
    42             return defineClass(bytes, 0, bytes.length);
    43         } catch (Exception e) {
    44             e.printStackTrace();
    45         }
    46         
    47         
    48         return super.findClass(name);
    49     }
    50     
    51     public MyClassLoader(){
    52         
    53     }
    54     
    55     public MyClassLoader(String classDir){
    56         this.classDir = classDir;
    57     }
    58 }

    写一下测试代码:

    1         //父类加载的是全路径加包名的类
    2         Class clazz = new MyClassLoader("itcastlib").loadClass("com.ssc.classLoader.ClassLoaderAttachment");
    3         // 子类继承父类的加载的是相对路径的类 只有当父类没有找到类的时候 子类才进行加载
    4         //Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
    5         Date d1 = (Date) clazz.newInstance();
    6         System.out.println(d1);

    这里的让重写的ClassLoader加载指定的类的解决办法是:我把编译好的指定的类的class文件放在 itcastlib文件夹中,这个其实是上面加密后的class文件,也就是MyClassLoader类中main函数执行之后生成在  itcastlib 文件夹中的,但是父类会去E:20181124workspaceTestincomsscclassLoaderClassLoaderAttachment.class 路径下找那个类的class文件,而当我用重写的ClassLoader加载时,会从itcastlib 文件夹下去加载该类的class文件,并且我自己的ClassLoader会执行解密方法,对class文件的字节码进行解密。否则父类加载加密的class文件的时候,会报错的,因为父类中的findClass()方法并没有我的解密方法。

    下图中的样子:

    父类加载报错:

    总结:

    这样写出来,感觉自己稍微理清楚一点了。嘻嘻嘻

    其实,在好多的框架中都会自己设计所需要的类加载器,但是我之前好像并不知道,看了一下Tomcat中的servlet的加载问题,看到了Tomcat中使用了WebappClassLoader、StandardClassLoader来加载servlet以及Tomcat内部实现的HttpServlet,这个以后可以往深里仔细去看一下

  • 相关阅读:
    在linux下Ant的环境配置
    在linux下Java的环境配置
    CSS列表逆序
    HTML元素基础学习
    第一天---HTML基础学习
    排球计分程序
    罗辑思维:怎样成为一个高手 观后感
    十八周个人作业
    本周个人作业
    个人作业
  • 原文地址:https://www.cnblogs.com/ssh-html/p/10816213.html
Copyright © 2011-2022 走看看