zoukankan      html  css  js  c++  java
  • 自定义类加载器

    JVM默认的三个类加载器

    AppClasLoader  系统类加载器   

    ExtClassLoader  扩展类加载器

    BootstrapClassLoader  根类加载器  (由C++实现,在控制台打印出来的是null)

    他们加载的jar包所在的路径不同

    父委托机制

    1. Invoke findLoadedClass(String) to check if the class has already been loaded.

    2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.

    3. Invoke the findClass(String) method to find the class. 

     可以看出一个类的加载是通过调用类加载器的loadClass方法来完成的,第一步检查是否已经加载过此类,第二步递归调用父加载器加载,第三步调用自己的findClass加载

    自定义类加载器,打破了父委托机制

    public class MyClassLoader extends ClassLoader {
    
        private String dir = "C:\Users\Administrator\Desktop";
    
        public MyClassLoader() {
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String classPath = name.replace(".", "/");
            File classFile = new File(dir, classPath + ".class");
            if (!classFile.exists()) {
                throw new ClassNotFoundException("The class " + name + " not found under " + dir);
            }
    
            byte[] classBytes = loadClassBytes(classFile);
            if (null == classBytes || classBytes.length == 0)
                throw new ClassNotFoundException("load the class " + name + " failed");
    
            return this.defineClass(name, classBytes, 0, classBytes.length);
        }
    
        private byte[] loadClassBytes(File classFile) {
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    FileInputStream fis = new FileInputStream(classFile)) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) != -1) {
                    baos.write(buffer, 0, len);
                }
                baos.flush();
                return baos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> clazz = null;
    
            // 如果是以java开头的类,就采用系统类加载器加载
            if (name.startsWith("java.")) {
                try {
                    ClassLoader system = ClassLoader.getSystemClassLoader();
                    clazz = system.loadClass(name);
                    if (clazz != null) {
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (Exception e) {
                    // ignore
                }
            }
            // 打破了类加载的父委托机制
            // 先自己加载,自己加载不了,就委托父加载加载
            try {
                clazz = findClass(name);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            if (clazz == null && getParent() != null) {
                getParent().loadClass(name);
            }
            return clazz;
        }
    
    }
    
    
    public class Test {
    
        public static void main(String[] args) throws Exception {
    
            MyClassLoader myClassLoader = new MyClassLoader();
            Class aClass = myClassLoader.loadClass("com.irish.algorithm.MyObject");
            System.out.println(aClass.getClassLoader());
        }
    }

    类加载器的命名空间

    每一个类加载器都有自己的命名空间,由该类加载器及其所有的父加载器类构成

    类的运行时包

    由加载该类的命名空间+该类的可见包组成

    类加载的父委托机制优缺点

    优点:防止内存中出现多份同样的字节码 ,保证底层的类不会被用户写的覆盖,保证安全(即使绕过了父委托机制,与jvm底层的同名类也加载不成功)

    缺点:子加载器的命名空间对父加载器不可见

    热部署是采用类加载器实现的

    public class User {
    
        public void add() {
            System.out.println("User 1.0 版本!");
        }
    }
    public class MyClassLoader extends ClassLoader {
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
           //默认从当前类的目录下读取文件,传入的字符串以'/'开头的话,就是以classpath为目录 InputStream
    is = this.getClass().getResourceAsStream(fileName); byte [] bytes = new byte[is.available()]; is.read(bytes); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { // TODO: handle exception throw new ClassNotFoundException(); } } }
    public class Test {
    
        public static void main(String[] args) throws Exception {
            
            System.out.println("开始加载版本1.0");
            loadUser();
            Thread.sleep(10000);
            //当有类进行修改后,class文件发生了变化,重新加载该类的字节码
            System.out.println("开始加载版本2.0");
            loadUser();
            
        }
        
        
        public  static void loadUser() throws Exception {
            MyClassLoader classLoader = new MyClassLoader();
            Class<?> findClass = classLoader.findClass("com.j0620.User");
            Object obj = findClass.newInstance();
            Method method = findClass.getMethod("add");
            method.invoke(obj);
            System.out.println(obj.getClass());
            System.out.println(obj.getClass().getClassLoader());
        }
    }

     项目结构:

  • 相关阅读:
    轻松处理高于平常10倍的视频需求,还能节省60%的IT成本,蓝墨做对了什么?
    支付宝研究员王益的建议:“学好语文,才能写好代码”
    Flutter+FaaS一体化任务编排的思考与设计
    开放下载!《大促背后的前端核心业务实践》
    揭秘!信息检索技术高端玩法
    秒懂云通信:如何用阿里云语音通知服务(小白指南)
    卡顿人生,如何拯救?
    阿里云荣获可信云容器安全能力先进级认证, ACK/ACR为企业级安全护航
    飞天大数据产品价值解读— SaaS模式云数据仓库MaxCompute
    阿里产品专家:高情商的技术人,如何做沟通?
  • 原文地址:https://www.cnblogs.com/moris5013/p/10930077.html
Copyright © 2011-2022 走看看