核心思路是绕过默认双亲委派,在findclass中接管字节码获取与注册流程,从http、数据库blob、对象存储等任意字节流源加载单个.class字节序列,经校验后通过defineclass注册为jvm类,并需禁用敏感包、验证魔数与版本、限制大小以保障安全。

核心思路是绕过默认双亲委派,在 findClass 中接管字节码获取与注册流程,不依赖本地文件系统,而是从 HTTP、数据库 BLOB、对象存储等任意可读取字节流的源头加载类。
准备可执行的字节码流
目标不是整个 JAR,而是单个类的 .class 字节序列。来源可以是:
- HTTP:用
HttpURLConnection或OkHttpClient请求远程 URL,校验状态码为 200,设置连接/读取超时; - 数据库:通过 JDBC 查询含
BLOB或BYTEA字段的记录,调用ResultSet.getBytes()或getBinaryStream()获取流; - 对象存储(如 MinIO、OSS):使用 SDK 下载对象到
ByteArrayOutputStream,避免落盘; - 加密源:先解密再校验 SHA-256 摘要,确保字节码未被篡改。
解析并提取目标类字节码
若源是 JAR 包(ZIP 格式),需遍历条目定位指定类:
- 将类全限定名转换为内部路径格式:如
com.example.Service→com/example/Service.class; - 用
ZipInputStream逐个读取ZipEntry,匹配名称后调用entry.getInputStream(); - 用
ByteArrayOutputStream将流内容完整读入内存字节数组,不写临时文件; - 若源已是单个 .class 文件(如直传的 class blob),跳过解压,直接读取全部字节。
自定义 ClassLoader 完成加载注册
继承 ClassLoader,只重写 findClass(String name),不要动 loadClass:
- 在
findClass内完成“拉取→解析→读取→defineClass”闭环; - 调用
defineClass(name, byte[], 0, bytes.length)将字节数组注册为 JVM 可识别的Class对象; - 如需触发链接(分配静态变量内存、解析符号引用),可随后调用
resolveClass(cls); - 建议缓存已加载的
Class实例(如用ConcurrentHashMap),避免重复下载和解析。
安全与健壮性要点
动态加载远程字节码天然存在风险,必须加固:
- 禁止加载
java.*、javax.*、sun.*等敏感包路径下的类,防止覆盖核心类; - 对字节码做基础验证:检查魔数(
0xCAFEBABE)、主次版本号是否在 JVM 支持范围内; - 捕获
ClassFormatError、NoClassDefFoundError、IOException等异常,提供清晰错误上下文; - 限制单次加载最大字节数(如 5MB),防内存耗尽或恶意膨胀攻击。











