深入理解Java类加载器(3)
2024-08-26 15:17:34
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();
}
} }
最新文章
- webform开发基础
- 组合数取模Lucas定理及快速幂取模
- Python基础之【第三篇】
- 网站中集成jquery.imgareaselect实现图片的本地预览和选择截取
- Cannot spawn... TortoisePlink
- 石子归并问题(nyoj737)
- Web发布 未能加载文件或程序集“”或它的某一个依赖项。系统找不到指定的...
- Git教程之分支管理之一
- 照片教你eclipse通过使用gradle 打包Android
- AngularJS入门第一课
- Yii2 给iOS App写推送的接口
- [洛谷P1196][NOI2002]银河英雄传说 - 带偏移量的并查集(1)
- 作为小白,如何学习Web前端开发?
- FFmpeg 学习(六):FFmpeg 核心模块 libavformat 与 libavcodec 分析
- GitHubPopular运行记录
- JAVA—API和SPI概念
- js data日期初始化的5种方法
- Linux中配置别名
- TP5.1:request请求对象(使用四种方式获取)
- 虹软人脸识别SDK的接入方法
热门文章
- R语言数据结构二
- 20155217 2016-2017-2 《Java程序设计》第9周学习总结
- WPF 主题
- extern ";C"; 的用意
- 9 ORM-高阶补充(未完成)
- 【CQOI2017】小Q的棋盘
- 洛谷P2973 [USACO10HOL]赶小猪
- python基础—字典
- Flutter - >; Android dependency &#39;com.android.support:support-v4&#39; has different version for the compile (26.1.0) and runtime (27.1.1) classpath.
- javaweb学习5——JSP