我来给你一个完整的自定义类加载器案例,包含多个实际场景的演示。

完整的自定义类加载器案例
基础的自定义类加载器
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* 从指定目录加载类的自定义类加载器
*/
public class FileClassLoader extends ClassLoader {
// 类文件存放的目录
private String classpath;
// 缓存已加载的类
private Map<String, Class<?>> classCache = new HashMap<>();
public FileClassLoader(String classpath) {
// 设置为系统类加载器的父加载器
super(ClassLoader.getSystemClassLoader());
this.classpath = classpath;
}
/**
* 重写 findClass 方法,实现自己的加载逻辑
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 先从缓存中获取
if (classCache.containsKey(name)) {
return classCache.get(name);
}
// 将类名转换为文件路径
String path = classpath + File.separator +
name.replace('.', File.separatorChar) + ".class";
File file = new File(path);
if (!file.exists()) {
throw new ClassNotFoundException(name);
}
try {
// 读取字节码
byte[] classData = readClassFile(file);
// 调用 defineClass 方法将字节码转换为 Class 对象
Class<?> clazz = defineClass(name, classData, 0, classData.length);
// 放入缓存
classCache.put(name, clazz);
return clazz;
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
/**
* 读取类文件的字节码
*/
private byte[] readClassFile(File file) throws IOException {
try (FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
return bos.toByteArray();
}
}
}
加密/解密类加载器
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.Key;
/**
* 支持对class文件进行加密和解密的类加载器
*/
public class EncryptedClassLoader extends ClassLoader {
private String classpath;
private String key;
public EncryptedClassLoader(String classpath, String key, ClassLoader parent) {
super(parent);
this.classpath = classpath;
this.key = key;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
// 读取加密的class文件
byte[] encryptedData = readEncryptedClass(name);
// 解密class文件
byte[] decryptedData = decrypt(encryptedData);
// 加载类
return defineClass(name, decryptedData, 0, decryptedData.length);
} catch (Exception e) {
throw new ClassNotFoundException(name, e);
}
}
/**
* 读取加密的class文件
*/
private byte[] readEncryptedClass(String className) throws IOException {
String fileName = className.replace('.', '/') + ".class";
File file = new File(classpath, fileName);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
return bos.toByteArray();
}
}
/**
* 解密class文件(使用简单的XOR加密示例)
*/
private byte[] decrypt(byte[] data) {
byte[] result = new byte[data.length];
byte[] keyBytes = key.getBytes();
for (int i = 0; i < data.length; i++) {
result[i] = (byte) (data[i] ^ keyBytes[i % keyBytes.length]);
}
return result;
}
/**
* 加密工具方法(用于生成加密的class文件)
*/
public static void encryptFile(String sourcePath, String targetPath, String key)
throws IOException {
byte[] keyBytes = key.getBytes();
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[1024];
int length;
int offset = 0;
while ((length = fis.read(buffer)) != -1) {
for (int i = 0; i < length; i++) {
buffer[i] = (byte) (buffer[i] ^ keyBytes[(offset + i) % keyBytes.length]);
}
fos.write(buffer, 0, length);
offset += length;
}
}
}
}
热部署类加载器
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 支持热部署的类加载器
*/
public class HotDeployClassLoader extends ClassLoader {
// 需要热加载的class文件路径
private String hotClasspath;
// 记录文件的修改时间
private Map<String, Long> fileTimestamps = new ConcurrentHashMap<>();
public HotDeployClassLoader(String hotClasspath, ClassLoader parent) {
super(parent);
this.hotClasspath = hotClasspath;
}
/**
* 尝试加载类(检查是否需要重新加载)
*/
public Class<?> loadClassForHotDeploy(String name) throws ClassNotFoundException {
// 检查文件是否被修改
if (isClassModified(name)) {
// 如果被修改,创建一个新的类加载器来加载
return new FileClassLoader(hotClasspath).loadClass(name);
} else {
// 没有修改,使用当前加载器加载
return loadClass(name);
}
}
/**
* 检查类文件是否被修改
*/
private boolean isClassModified(String className) {
String path = hotClasspath + File.separator +
className.replace('.', File.separatorChar) + ".class";
File file = new File(path);
if (!file.exists()) {
return false;
}
long lastModified = file.lastModified();
Long prevModified = fileTimestamps.get(className);
if (prevModified == null || prevModified != lastModified) {
fileTimestamps.put(className, lastModified);
return true;
}
return false;
}
/**
* 从Jar文件加载类
*/
public Class<?> loadClassFromJar(String jarPath, String className)
throws ClassNotFoundException, IOException {
try (JarFile jarFile = new JarFile(jarPath)) {
String entryName = className.replace('.', '/') + ".class";
JarEntry entry = jarFile.getJarEntry(entryName);
if (entry == null) {
throw new ClassNotFoundException(className);
}
try (InputStream is = jarFile.getInputStream(entry)) {
byte[] classBytes = is.readAllBytes();
return defineClass(className, classBytes, 0, classBytes.length);
}
}
}
}
网络类加载器
import java.io.*;
import java.net.*;
/**
* 从网络加载类的类加载器
*/
public class NetworkClassLoader extends ClassLoader {
private String serverUrl;
private Map<String, Class<?>> classCache = new HashMap<>();
public NetworkClassLoader(String serverUrl, ClassLoader parent) {
super(parent);
this.serverUrl = serverUrl;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 先从缓存获取
if (classCache.containsKey(name)) {
return classCache.get(name);
}
try {
// 从网络下载class文件
byte[] classData = downloadClass(name);
// 将字节码转换为Class对象
Class<?> clazz = defineClass(name, classData, 0, classData.length);
classCache.put(name, clazz);
return clazz;
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
/**
* 从服务器下载class文件
*/
private byte[] downloadClass(String className) throws IOException {
String path = className.replace('.', '/') + ".class";
URL url = new URL(serverUrl + "/" + path);
try (InputStream is = url.openStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
return bos.toByteArray();
}
}
}
使用示例和测试
import java.lang.reflect.Method;
/**
* 测试自定义类加载器
*/
public class ClassLoaderDemo {
public static void main(String[] args) throws Exception {
// 1. 创建测试类文件
createTestClassFile();
// 2. 使用文件类加载器
System.out.println("=== 文件类加载器测试 ===");
FileClassLoader fileLoader = new FileClassLoader("hot-classes");
Class<?> clazz1 = fileLoader.loadClass("com.example.Person");
Object obj1 = clazz1.getDeclaredConstructor().newInstance();
Method setNameMethod = clazz1.getMethod("setName", String.class);
setNameMethod.invoke(obj1, "张三");
Method getNameMethod = clazz1.getMethod("getName");
System.out.println("姓名: " + getNameMethod.invoke(obj1));
System.out.println("类加载器: " + clazz1.getClassLoader().getClass().getName());
// 3. 测试加密类加载器
System.out.println("\n=== 加密类加载器测试 ===");
String key = "MySecretKey2024";
// 加密原始class文件
EncryptedClassLoader.encryptFile(
"hot-classes/com/example/Person.class",
"hot-classes/com/example/Person.class.enc",
key
);
// 使用加密类加载器加载
EncryptedClassLoader encryptedLoader =
new EncryptedClassLoader("hot-classes", key,
ClassLoader.getSystemClassLoader());
try {
Class<?> clazz2 = encryptedLoader.loadClass("com.example.Person");
System.out.println("加密类加载成功: " + clazz2.getName());
} catch (ClassNotFoundException e) {
System.out.println("加密类加载失败: " + e.getMessage());
}
// 4. 测试热部署
System.out.println("\n=== 热部署测试 ===");
HotDeployClassLoader hotLoader =
new HotDeployClassLoader("hot-classes",
ClassLoader.getSystemClassLoader());
// 第一次加载
Class<?> hotClazz1 = hotLoader.loadClassForHotDeploy("com.example.Person");
System.out.println("第一次加载: " + hotClazz1.getClassLoader().getClass().getName());
// 修改class文件(模拟更新)
Thread.sleep(100);
modifyClassFile();
// 第二次加载
Class<?> hotClazz2 = hotLoader.loadClassForHotDeploy("com.example.Person");
System.out.println("第二次加载: " + hotClazz2.getClassLoader().getClass().getName());
System.out.println("两个类是同一个吗? " + (hotClazz1 == hotClazz2));
}
/**
* 创建测试类文件
*/
private static void createTestClassFile() throws IOException {
String source =
"package com.example;\n" +
"public class Person {\n" +
" private String name;\n" +
" \n" +
" public Person() {\n" +
" this.name = \"默认名字\";\n" +
" }\n" +
" \n" +
" public void setName(String name) {\n" +
" this.name = name;\n" +
" }\n" +
" \n" +
" public String getName() {\n" +
" return name;\n" +
" }\n" +
"}";
File sourceFile = new File("hot-classes/com/example/Person.java");
sourceFile.getParentFile().mkdirs();
try (java.io.FileWriter writer = new java.io.FileWriter(sourceFile)) {
writer.write(source);
}
// 编译Java文件
try {
javax.tools.JavaCompiler compiler =
javax.tools.ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null,
sourceFile.getAbsolutePath(),
"-d", "hot-classes");
if (result != 0) {
System.out.println("编译失败");
}
} catch (Exception e) {
System.out.println("编译错误: " + e.getMessage());
}
}
/**
* 修改class文件(用于测试热部署)
*/
private static void modifyClassFile() throws IOException, InterruptedException {
// 简化实现:重新编译修改过的方法
String source =
"package com.example;\n" +
"public class Person {\n" +
" private String name;\n" +
" private int age;\n" + // 新增字段
" \n" +
" public Person() {\n" +
" this.name = \"默认名字\";\n" +
" this.age = 18;\n" +
" }\n" +
" \n" +
" public void setName(String name) {\n" +
" this.name = name;\n" +
" }\n" +
" \n" +
" public String getName() {\n" +
" return name;\n" +
" }\n" +
" \n" +
" public int getAge() {\n" + // 新增方法
" return age;\n" +
" }\n" +
"}";
File sourceFile = new File("hot-classes/com/example/Person.java");
try (java.io.FileWriter writer = new java.io.FileWriter(sourceFile)) {
writer.write(source);
}
javax.tools.JavaCompiler compiler =
javax.tools.ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null,
sourceFile.getAbsolutePath(),
"-d", "hot-classes");
}
}
实际应用场景示例
/**
* Tomcat风格的Web应用类加载器
*/
public class WebAppClassLoader extends ClassLoader {
private String webAppPath; // Web应用根目录
private Map<String, byte[]> classByteCache = new HashMap<>();
public WebAppClassLoader(String webAppPath, ClassLoader parent) {
super(parent);
this.webAppPath = webAppPath;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 1. 检查是否已经加载
synchronized (getClassLoadingLock(name)) {
Class<?> clazz = findLoadedClass(name);
if (clazz == null) {
// 2. 先尝试从Web应用自己加载
try {
clazz = findClass(name);
} catch (ClassNotFoundException e) {
// 3. 如果自己的加载失败,委托给父加载器
clazz = super.loadClass(name, false);
}
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 从WEB-INF/classes下加载
String relativePath = name.replace('.', '/') + ".class";
File classFile = new File(webAppPath, "WEB-INF/classes/" + relativePath);
// 如果不存在,尝试从WEB-INF/lib的jar包中加载
if (!classFile.exists()) {
classFile = findInJars(name);
}
if (classFile == null) {
throw new ClassNotFoundException(name);
}
try {
byte[] classData = Files.readAllBytes(classFile.toPath());
return defineClass(name, classData, 0, classData.length);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
private File findInJars(String className) {
File libDir = new File(webAppPath, "WEB-INF/lib");
File[] jars = libDir.listFiles((dir, name) -> name.endsWith(".jar"));
if (jars != null) {
for (File jar : jars) {
try (JarFile jarFile = new JarFile(jar)) {
String entryName = className.replace('.', '/') + ".class";
// 检查是否存在并提取内容(简化处理)
// 实际实现时应该返回类数据
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
-
核心方法:
findClass():自定义加载逻辑的主要方法defineClass():将字节码转换为Class对象loadClass():加载入口,遵循双亲委派模型
-
特点:
- 类加载器隔离
- 支持加密/解密
- 支持热部署
- 支持从不同来源加载(文件、网络、数据库等)
-
应用场景:
- Web容器(Tomcat)
- OSGI框架
- 插件系统
- 代码热更新
- 类文件加密保护
-
注意事项:
- 注意双亲委派模型的遵循和打破
- 注意类的唯一性(同一类由不同加载器加载是不同的类)
- 注意内存泄漏问题
- 注意安全性和权限控制
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播