zoukankan      html  css  js  c++  java
  • 为什么JVM需要多种类加载器

    JVM的类加载器

    刚刚学习JVM的类加载机制的时候,会被教育说JVM的类加载机制需要不同的类加载器。过了很久之后忘记了。现在再复习一下。

    为什么需要多个类加载器?
    加载器作用是通过类名来获取二进制字节流。

    我们先抛开所有问题,从写程序的角度来讲一个程序应该具有什么?
    1、健壮性
    2、功能性
    3、鲁棒性
    4、效率性
    5、维护性
    6、可靠性(安全性)

    由此,我们对比JVM。JVM也是一个软件,也应该基本符合上述的几个特性。
    健壮性和功能性:JVM可以从不同的地方去加载class,比如文件系统,web,FTP等,这就要求JVM屏蔽底层的加载逻辑,只需要提供一个classloard()的接口就行了,客户端就可以加载类但是却不用管类加载器到底是怎么实现的。
    再说安全性:JVM得保证自有类不遭到破坏(比如java.lang包下的类,这个破坏的原理就是类对同包级别及其子包下的public类具有操作权限)。为了解决这个问题,使用双亲委派机制刚好可以解决。

    此外,如果我想在一个JVM中启动两个相同的应用的不同版本(不同版本值得是同样的全限定名的类,但是功能不一样),怎么办?假设只有一个类加载器,那么第二次加载该类会失败,第二个版本的应用还是使用了第一个版本的class,那么第二个应用就无法提供第二个版本类的功能了。所以得需要每个应用都有相互隔离的类加载器,否则第二个应用的类可能会覆盖第一个应用之前加载的类,从而造成一些意想不到的后果。

    每种加载器都有对应的层级来加载某些特定的类,来保证他们之间的安全性。

    自定义的类加载器。

    package com.stat;
    
    import java.util.Date;
    
    public class MyDate extends Date {
        @Override
        public String toString() {
            return "MyDate : 我的时间。";
        }
    }
    
    package com.stat;
    
    import java.io.*;
    
    /**
     * 自定义类加载器
     */
    public class MyClassLoader extends ClassLoader {
        String classDir;
    
        public MyClassLoader() {
        }
    
        public MyClassLoader(String classDir) {
            this.classDir = classDir;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String classFile = classDir + "/" + name + ".class";
            System.out.println("classFile path==" + classFile);
            try {
                //这个地方我们只是简单的读取文件流的方式来获取byte数组
                //其实可以尝试将class文件加密以后 这里解密 这样就可以保证
                //这种class文件 只有你写的classloader才能读取的了。
                //其他任何classloader都读取不了 包括系统的。
                byte[] classByte = toByteArray(classFile);
                return defineClass(classByte, 0, classByte.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    
        public static byte[] toByteArray(String filename) throws IOException, FileNotFoundException {
    
            File f = new File(filename);
            if (!f.exists()) {
                throw new FileNotFoundException(filename);
            }
    
            try (ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length())) {
                BufferedInputStream in = null;
                in = new BufferedInputStream(new FileInputStream(f));
                int bufSize = 1024;
                byte[] buffer = new byte[bufSize];
                int len = 0;
                while (-1 != (len = in.read(buffer, 0, bufSize))) {
                    bos.write(buffer, 0, len);
                }
                return bos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                throw e;
            }
        }
    }
    
    
    package com.stat;
    
    import java.util.Date;
    
    public class MyClassTest {
        public static void main(String[] args)
        {
            try {
                //注意这个路径:是在target目录下的,是编译后的路径
                Class classDate = new MyClassLoader("D:\javaworkspace\test02\test02_module01\target\classes\com\stat").loadClass("com.stat.MyDate");
                Class classDate2 = new MyClassLoader("D:\javaworkspace\test02\test02_module01\target\classes\com\stat").loadClass("MyDate");
                Date date = (Date) classDate.newInstance();
                System.out.println("date ClassLoader:"+date.getClass().getClassLoader().getClass().getName());
                System.out.println(date);
    
                Date date2 = (Date) classDate2.newInstance();
                System.out.println("date2 ClassLoader:"+date2.getClass().getClassLoader().getClass().getName());
                System.out.println(date2);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
    
        }
    
    }
    

    输出结果如下

    classFile path==D:javaworkspace	est02	est02_module01	argetclassescomstat/MyDate.class
    date ClassLoader:sun.misc.Launcher$AppClassLoader
    MyDate : 我的时间。
    date2 ClassLoader:com.stat.MyClassLoader
    MyDate : 我的时间。
    

    可以看到date 的类加载器是AppClassLoader,而date2的类加载器是自定义的

    大家可以看到classdate和classDate2 这2个类,我们在用classLoader去加载的时候传的参数唯一的不同就是前者传入了完整的包名,而后者没有。这就导致了前者的classLoader依旧是系统自带的appclassloader 而后者才是我们自定义的classloader。 原因:

    虽然对于classDate和classDate2来说,我们手动指定了她的类加载是我们自定义的myclassloader,但是根据类加载器的规则,我们能用父亲的loadclass就肯定不会用自己的,而我们系统类加载器,AppClassLoader要想loadclass成功是需要传入完整的包名的。所以classDate的构造还是传入了完整的包名,这就是为啥classDate的加载器还是AppClassLoader,但是classDate2并没有传入完整的包名,所以AppClassLoader也是找不到这个CustomDate类的,最后只能交给MyClassLoader这个最底层的,我们自定义的classloader来load

  • 相关阅读:
    css3的clip-path方法剪裁实现
    vue-cli3.0之vue.config.js的配置项(注解)
    用Canvas实现一些简单的图片滤镜
    转《图像处理之表面滤波》
    vue-axios的application/x-www-form-urlencod的post请求无法解析参数
    如何在linux中执行一个脚本
    列表、字典、元组小练习
    开发脚本自动部署及监控
    固化命令的方式、sed文本处理工具
    nginx服务、nginx反向代理和nfs共享服务
  • 原文地址:https://www.cnblogs.com/dhu121/p/14947884.html
Copyright © 2011-2022 走看看