👉 一个简单的基于ClassLoader用于依赖隔离的容器实现。
# 在Java中依赖主要是Jar。
依赖容器自然涉及下面的问题:
- 可以从多处加载类,并分配不同的
ClassLoader ClassLoader之间有继承关系
ClassLoader的继承关系会是一个树- 类加载会在上下级
ClassLoader之间有委托关系,如:- 是否允许在上级
ClassLoader中查找类。
即是否 委托。 - 允许在上级
ClassLoader查找哪些类/包。
即可以配置 委托 的粒度。 - 只允许在某级
ClassLoader查找哪些类/包,会忽略这级ClassLoader的下级ClassLoader中这些类/包,不允许子ClassLoader。
即必须 委托。
- 是否允许在上级
完备委托关系可以先分析只有父子2层ClassLoader间委托关系的情况。
某个类的加载在两层父子ClassLoader间委托关系,按是否加载排列组合一共有4种情况:
- 父不加载,子不加载【00】
可以用来显式禁止某些类的加载。
实际应用中应该 很少 会用到。 - 父不加载,子加载【01】
子自理,父里即使包含了相同的类也不会污染子。实际场景:- 用来
Tomcat容器自用Lib,不会影响到的Web应用。
- 用来
- 父加载,子不加载【10】
一定使用的父的类。实际场景:Tomcat容器的ServletAPI,不允许被Web应用改写。
- 父加载,子加载【11】
两者可以加载的情况下,按谁优先分成2个Case:- 父优先。【
Parent-Child】
即是Java缺省的委托策略,代理模式(Delegation Mode)。
这个委托策略可以保证Java核心库的类型优先加载,Java核心库的类的加载工作由引导类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。 - 子优先。【
Child-Parent】
这种委托关系比较复杂,有引起类版本混乱的风险!:bomb:
实际应用中应该 避免 这种委托关系。 🙅
- 父优先。【
ℹ️
关于子优先【Child-Parent】类版本混乱的风险的细节原因看了后面参考资料就清楚了,这里只说一个简单例子:
子里有类Wheel;父里有类Car、Wheel;类Car引用了类Wheel。
子加载类Car里通过实际是父来加载(子中没有这个类),返回的Car所引用Wheel是用Car的ClassLoader即父来加载。
子中直接使用Wheel时,由子来加载(子里有Wheel类)。
结果Car引用的类Wheel和子直接使用的类Wheel的ClassLoader不同,即类型不兼容,看起来正确的赋值会抛出的ClassCastException!
上面【11】的情况分成2个子Case,合起来一共有5种情况。
委托关系可以统一描述成:
NoneChild-OnlyParent-OnlyParent-ChildChild-Parent
按上面说明的2层委托关系约定,嵌套推广即可得到 包含 任意层ClassLoader的完备委托关系。:sparkles:
父子ClassLoader由于树状的单继承关系,委托关系比较单一。
要实现复杂的代理关系,兄弟ClassLoader之间代理可以简化。
举个场景,多个中间件如RPC、Message等,要需要把部分类代理给应用ClassLoader,如果就使用父子代理,结果会是这样:
System ClassLoader
|
V
RPC ClassLoader
|
V
Message ClassLoader
|
V
App ClassLoader上面问题是,RPC ClassLoader和Message ClassLoader之间的父子关系不符合实际关系(RPC ClassLoader和Message ClassLoader之间并不需要父子代理)。
更合理些的代理关系是:
System ClassLoader
/ | \
/ | \
V V V
RPC ClassLoader -> App ClassLoader <- Message ClassLoader即RPC ClassLoader和Message ClassLoader作为App ClassLoader的兄弟ClassLoader并代理。
- 加载本地类目录或
Jar文件 - 加载本地有类目录或
Jar文件的目录 - 加载网络上的类
这个功能应该很少使用 😜 ,为了功能完整而说明。 - 加密类工具/加载加密的类
这个功能应该很少使用 😝 ,为了功能完整而说明。
- 在一个
JVM中部署多个应用,但应用依赖不互相影响。
这样是提高 系统利用率 的一种方式。 - 把平台级的二方库从应用中隔离出来,由架构部门统一升级。这样做的原因是:
- 平台级二方库如果有
Bug影响面广,有统一的升级的需求。 - 平台级二方库升级使用面广,升级困难。
- 平台级二方库如果有
ℹ️
上面的部署方式中,依赖容器的引入对于应用的开发应该是 透明 的。
- 给出类加载委托情况的完备说明
- 给出类加载委托规则的规范描述
- 给出类加载委托规则的规范描述的自己的一个描述格式
对于这个项目会优先使用Properties来描述,简单够用。 - 说明
Java的ClassLoader的用途和限制 - 给出
ClassLoader使用和实现的原则 ClassLoader使用和实现容易出错的地方- 整理出使用了
ClassLoader的常见框架 - 说明这些框架中
ClassLoader的实现方法及其使用契约 - 给出
ClassLoader实现方法及其使用契约的最佳实践
Web容器集成- 实现
OSGi规范
这个也可以用来验证实现是否面向编程友好
jboss-modules
JBoss Modules is a standalone implementation of a modular (non-hierarchical) class loading and execution environment for Java.sofa-jarslink
Jarslink is a sofa ark plugin used to manage multi-application deployment
- The Java Language Specification的第12章 Execution和The Java Virtual Machine Specification的第5章 Loading, Linking, and Initializing
详细介绍了Java类的加载、链接和初始化。
不同Java版本的语言和JVM规范在http://docs.oracle.com/javase/specs/ - Multithreaded Custom Class Loaders in Java SE 7,
JDK7修复了ClassLoader的死锁问题。
这个问题在JDK 7之前的版本中一直存在。平时使用中确实不容易碰到,但在线上应用复杂和高压力场景中有实际观察到过。
这篇文档给出问题的原因及其修复方法。 Java命令行选项-verbose:class可以在加载类时显示相关信息。
完整Java命令行选项参见: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html- Java API doc - ClassLoader
- Understanding Extension Class Loading
SunJDK用于启动应用的Laucher实现类。
注:sun.misc.Launcher类没有在JDK附带的src.zip中,因为是JDK具体实现部分(厂商相关),可以在JDK源码中找到这个Java类。
Sun的JDK的源码下载在这里,厂商相关Java类在目录jdk/src/share/classes下。Laucher类中包含了Java除BootstrapClassloader(是用本地代码C/C++来实现)外另2个BuildinClassLoader类的实现:
- IBM DeveloperWorks - 深入探讨Java类加载器。一篇非常不错的
ClassLoader的介绍文章,并且对比介绍了JavaSPI的类加载策略。包含JDBC和JAXP为代表的2种方式。Tomcat的类加载策略。OSGi的类加载策略。
- The basics of Java class loaders
1996年(Java 1的年代)的一篇老文章,其中描述功能现在可以通过Java 2提供的java.net.URLClassLoader方便的完成。
但实现复杂自定义ClassLoader的流程是一样的,文章给出了实现自定义ClassLoader- 要覆盖的
ClassLoader关键方法 - 要遵循的基本约定
- 要注意的安全问题
- 要覆盖的
- IBM DeveloperWorks - Understanding the Java ClassLoader
- IBM DeveloperWorks - Java programming dynamics, Part 1: Java classes and class loading
中文版在:Java编程的动态性,第 1 部分: 类和类装入
Java programming dynamics series,这个系列的中文版Java编程的动态性 - onjava.com - Internals of Java Class Loading
- IBM DeveloperWorks - Demystifying class loading problems series
- IBM Java Diagnostics Guide - Class loading
- Getting Started with Javassist - Class Loader
- Sheng Liang and Gilad Bracha, "Dynamic Class Loading in the Java Virtual Machine"
ACM OOPSLA'98, pp.36-44, 1998. - Wikipedia - Java Classloader
- Class.forName() vs ClassLoader.loadClass() - which to use for dynamic loading?
- Understanding WebLogic Server Application Classloading
- The Apache Tomcat 5.5 Servlet/JSP Container - Class Loader HOW-TO:详细介绍了
Tomcat5.5中的类加载器机制。 - OSGi Service Platform Core Specification
- IBM DeveloperWorks - Java安全模型介绍
- IBM DeveloperWorks - Java 授权内幕
更多内容参见:IBM DeveloperWorks - Java安全专题 - Java Security - Chapter 3. Java Class Loaders
- Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception
- How to fix the dreaded "java.lang.OutOfMemoryError: PermGen space" exception (classloader leaks)
- IBM DeveloperWorks - Diagnosis of Java class loader memory leaks
- Reloading Java Classes 201: How do ClassLoader leaks happen?
- Classloader-Related Memory Issues