Land is a simple Java™ dependency-isolation container via class loader.
APACHE-2.0 License
👉 一个简单的基于ClassLoader
用于依赖隔离的容器实现。
# 在Java
中依赖主要是Jar
。
依赖容器自然涉及下面的问题:
ClassLoader
ClassLoader
之间有继承关系ClassLoader
的继承关系会是一个树ClassLoader
之间有委托关系,如:
ClassLoader
中查找类。ClassLoader
查找哪些类/包。ClassLoader
查找哪些类/包,会忽略这级ClassLoader
的下级ClassLoader
中这些类/包,不允许子ClassLoader
。ClassLoader
委托关系的完备配置ClassLoader
委托完备委托关系可以先分析只有父子2层ClassLoader
间委托关系的情况。
某个类的加载在两层父子ClassLoader
间委托关系,按是否加载排列组合一共有4种情况:
Tomcat
容器自用Lib
,不会影响到的Web
应用。Tomcat
容器的Servlet
API
,不允许被Web
应用改写。Parent-Child
】Java
缺省的委托策略,代理模式(Delegation Mode
)。Java
核心库的类型优先加载,Java
核心库的类的加载工作由引导类加载器来统一完成,保证了Java
应用所使用的都是同一个版本的Java
核心库的类,是互相兼容的。Child-Parent
】ℹ️ 关于子优先【Child-Parent】类版本混乱的风险的细节原因看了后面参考资料就清楚了,这里只说一个简单例子: 子里有类
Wheel
;父里有类Car
、Wheel
;类Car
引用了类Wheel
。 子加载类Car
里通过实际是父来加载(子中没有这个类),返回的Car
所引用Wheel
是用Car
的ClassLoader
即父来加载。 子中直接使用Wheel
时,由子来加载(子里有Wheel
类)。 结果Car
引用的类Wheel
和子直接使用的类Wheel
的ClassLoader
不同,即类型不兼容,看起来正确的赋值会抛出的ClassCastException
!
上面【11】的情况分成2个子Case,合起来一共有5种情况。
委托关系可以统一描述成:
None
Child-Only
Parent-Only
Parent-Child
Child-Parent
按上面说明的2层委托关系约定,嵌套推广即可得到 包含 任意层ClassLoader
的完备委托关系。✨
ClassLoader
委托父子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
sofa-jarslink
Java
类的加载、链接和初始化。Java
版本的语言和JVM
规范在http://docs.oracle.com/javase/specs/
JDK
7修复了ClassLoader
的死锁问题。JDK 7
之前的版本中一直存在。平时使用中确实不容易碰到,但在线上应用复杂和高压力场景中有实际观察到过。Java
命令行选项-verbose:class
可以在加载类时显示相关信息。Java
命令行选项参见: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html
Sun
JDK
用于启动应用的Laucher
实现类。sun.misc.Launcher
类没有在JDK
附带的src.zip
中,因为是JDK
具体实现部分(厂商相关),可以在JDK
源码中找到这个Java
类。Sun
的JDK
的源码下载在这里,厂商相关Java
类在目录jdk/src/share/classes
下。Laucher
类中包含了Java
除BootstrapClassloader
(是用本地代码C/C++
来实现)外另2个Buildin
ClassLoader
类的实现:
ClassLoader
的介绍文章,并且对比介绍了
Java
SPI
的类加载策略。包含JDBC
和JAXP
为代表的2种方式。Tomcat
的类加载策略。OSGi
的类加载策略。1996
年(Java 1
的年代)的一篇老文章,其中描述功能现在可以通过Java 2
提供的java.net.URLClassLoader
方便的完成。ClassLoader
的流程是一样的,文章给出了实现自定义ClassLoader
ClassLoader
关键方法Tomcat
5.5中的类加载器机制。