zoukankan      html  css  js  c++  java
  • 深入理解Java类加载器(3)

    5.2 网络类加载器

      下面将通过一个网络类加载器来说明如何通过类加载器来实现组件的动态更新。即基本的场景是:Java 字节代码(.class)文件存放在服务器上,客户端通过网络的方式获取字节代码并执行。当有版本更新的时候,只需要替换掉服务器上保存的文件即可。通过类加载器可以比较简单的实现这种需求。
      类 NetworkClassLoader负责通过网络下载Java类字节代码并定义出Java类。它的实现与FileSystemClassLoader类似。

    package classloader;  
      
    import java.io.ByteArrayOutputStream;  
    import java.io.InputStream;  
    import java.net.URL;  
      
    public class NetworkClassLoader extends ClassLoader {  
      
        private String rootUrl;  
      
        public NetworkClassLoader(String rootUrl) {  
            // 指定URL  
            this.rootUrl = rootUrl;  
        }  
      
        // 获取类的字节码  
        @Override  
        protected Class<?> findClass(String name) throws ClassNotFoundException {  
            byte[] classData = getClassData(name);  
            if (classData == null) {  
                throw new ClassNotFoundException();  
            } else {  
                return defineClass(name, classData, 0, classData.length);  
            }  
        }  
      
        private byte[] getClassData(String className) {  
            // 从网络上读取的类的字节  
            String path = classNameToPath(className);  
            try {  
                URL url = new URL(path);  
                InputStream ins = url.openStream();  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                int bufferSize = 4096;  
                byte[] buffer = new byte[bufferSize];  
                int bytesNumRead = 0;  
                // 读取类文件的字节  
                while ((bytesNumRead = ins.read(buffer)) != -1) {  
                    baos.write(buffer, 0, bytesNumRead);  
                }  
                return baos.toByteArray();  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return null;  
        }  
      
        private String classNameToPath(String className) {  
            // 得到类文件的URL  
            return rootUrl + "/"  
                    + className.replace('.', '/') + ".class";  
        }  
    }  

    在通过NetworkClassLoader加载了某个版本的类之后,一般有两种做法来使用它。第一种做法是使用Java反射API。另外一种做法是使用接口。需要注意的是,并不能直接在客户端代码中引用从服务器上下载的类,因为客户端代码的类加载器找不到这些类。使用Java反射API可以直接调用Java类的方法。而使用接口的做法则是把接口的类放在客户端中,从服务器上加载实现此接口的不同版本的类在客户端通过相同的接口来使用这些实现类。我们使用接口的方式。示例如下:

      客户端接口:

    package classloader;  
      
    public interface Versioned {  
      
        String getVersion();  
    }  
    package classloader;  
      
    public interface ICalculator extends Versioned {  
      
        String calculate(String expression);  
    }  

    网络上的不同版本的类:

    package com.example;  
      
    import classloader.ICalculator;  
      
    public class CalculatorBasic implements ICalculator {  
      
        @Override  
        public String calculate(String expression) {  
            return expression;  
        }  
      
        @Override  
        public String getVersion() {  
            return "1.0";  
        }  
      
    }  
    package com.example;  
      
    import classloader.ICalculator;  
      
    public class CalculatorAdvanced implements ICalculator {  
      
        @Override  
        public String calculate(String expression) {  
            return "Result is " + expression;  
        }  
      
        @Override  
        public String getVersion() {  
            return "2.0";  
        }  
      
    }  

    在客户端加载网络上的类的过程:

    package classloader;  
      
    public class CalculatorTest {  
      
        public static void main(String[] args) {  
            String url = "http://localhost:8080/ClassloaderTest/classes";  
            NetworkClassLoader ncl = new NetworkClassLoader(url);  
            String basicClassName = "com.example.CalculatorBasic";  
            String advancedClassName = "com.example.CalculatorAdvanced";  
            try {  
                Class<?> clazz = ncl.loadClass(basicClassName);  // 加载一个版本的类  
                ICalculator calculator = (ICalculator) clazz.newInstance();  // 创建对象  
                System.out.println(calculator.getVersion());  
                clazz = ncl.loadClass(advancedClassName);  // 加载另一个版本的类  
                calculator = (ICalculator) clazz.newInstance();  
                System.out.println(calculator.getVersion());  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
      
    }  
  • 相关阅读:
    使用SQLite做本地数据缓存的思考
    毕业后第一次跳槽面试的点滴记录
    Nancy基于JwtBearer认证的使用与实现
    谈谈Nancy中让人又爱又恨的Diagnostics【上篇】
    CentOS 7.x 防火墙开放端口相关用法记录
    浅析如何在Nancy中使用Swagger生成API文档
    浅析如何在Nancy中生成API文档
    初探CSRF在ASP.NET Core中的处理方式
    微信小程序支付简单小结与梳理
    浅析Content Negotation在Nancy的实现和使用
  • 原文地址:https://www.cnblogs.com/xiarongjin/p/8310277.html
Copyright © 2011-2022 走看看