🍡 a simple java dependency-isolation container via class loader.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
land-core
land-main-runner
land-test-helper
src/main/resources
.gitignore
.travis.yml
LICENSE
README.md
pom.xml

README.md

Land

Build Status Coverage Status GitHub issues
Dependency Status License

🏁 概述

👉 一个简单的基于ClassLoader用于依赖隔离的容器实现。
# 在Java中依赖主要是Jar

依赖容器自然涉及下面的问题:

  • 可以从多处加载类,并分配不同的ClassLoader
  • ClassLoader之间有继承关系
    ClassLoader的继承关系会是一个树
  • 类加载会在上下级ClassLoader之间有委托关系,如:
    • 是否允许在上级ClassLoader中查找类。
      即是否 委托
    • 允许在上级ClassLoader查找哪些类/包。
      即可以配置 委托 的粒度。
    • 只允许在某级ClassLoader查找哪些类/包,会忽略这级ClassLoader的下级ClassLoader中这些类/包,不允许子ClassLoader
      即必须 委托

🔧 功能

1. ClassLoader委托关系的完备配置

父子ClassLoader委托

完备委托关系可以先分析只有父子2层ClassLoader间委托关系的情况。

某个类的加载在两层父子ClassLoader间委托关系,按是否加载排列组合一共有4种情况:

  • 父不加载,子不加载【00】
    可以用来显式禁止某些类的加载。
    实际应用中应该 很少 会用到。
  • 父不加载,子加载【01】
    子自理,父里即使包含了相同的类也不会污染子。实际场景:
    • 用来Tomcat容器自用Lib,不会影响到的Web应用。
  • 父加载,子不加载【10】
    一定使用的父的类。实际场景:
    • Tomcat容器的Servlet API,不允许被Web应用改写。
  • 父加载,子加载【11】
    两者可以加载的情况下,按谁优先分成2个Case:
    • 父优先。【Parent-Child
      即是Java缺省的委托策略,代理模式(Delegation Mode)。
      这个委托策略可以保证Java核心库的类型优先加载,Java核心库的类的加载工作由引导类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。
    • 子优先。【Child-Parent
      这种委托关系比较复杂,有引起类版本混乱的风险!💣
      实际应用中应该 避免 这种委托关系。 🙅

ℹ️
关于子优先【Child-Parent】类版本混乱的风险的细节原因看了后面参考资料就清楚了,这里只说一个简单例子:
子里有类Wheel;父里有类CarWheel;类Car引用了类Wheel
子加载类Car里通过实际是父来加载(子中没有这个类),返回的Car所引用Wheel是用CarClassLoader即父来加载。
子中直接使用Wheel时,由子来加载(子里有Wheel类)。
结果Car引用的类Wheel和子直接使用的类WheelClassLoader不同,即类型不兼容,看起来正确的赋值会抛出的ClassCastException

上面【11】的情况分成2个子Case,合起来一共有5种情况。

委托关系可以统一描述成:

  1. None
  2. Child-Only
  3. Parent-Only
  4. Parent-Child
  5. Child-Parent

按上面说明的2层委托关系约定,嵌套推广即可得到 包含 任意层ClassLoader的完备委托关系。

兄弟ClassLoader委托

父子ClassLoader由于树状的单继承关系,委托关系比较单一。 要实现复杂的代理关系,兄弟ClassLoader之间代理可以简化。

举个场景,多个中间件如RPCMessage等,要需要把部分类代理给应用ClassLoader,如果就使用父子代理,结果会是这样:

System ClassLoader
    |
    V
RPC ClassLoader
    |
    V
Message ClassLoader
    |
    V
App ClassLoader

上面问题是,RPC ClassLoaderMessage ClassLoader之间的父子关系不符合实际关系(RPC ClassLoaderMessage ClassLoader之间并不需要父子代理)。

更合理些的代理关系是:

                  System ClassLoader
                /         |         \
              /           |          \
            V             V           V
RPC ClassLoader -> App ClassLoader <- Message ClassLoader

RPC ClassLoaderMessage ClassLoader作为App ClassLoader的兄弟ClassLoader并代理。

2. 常用类加载方式

  • 加载本地类目录或Jar文件
  • 加载本地有类目录或Jar文件的目录
  • 加载网络上的类
    这个功能应该很少使用 😜 ,为了功能完整而说明。
  • 加密类工具/加载加密的类
    这个功能应该很少使用 😝 ,为了功能完整而说明。

🎨 使用场景

  1. 在一个JVM中部署多个应用,但应用依赖不互相影响。
    这样是提高 系统利用率 的一种方式。
  2. 把平台级的二方库从应用中隔离出来,由架构部门统一升级。这样做的原因是:
    • 平台级二方库如果有Bug影响面广,有统一的升级的需求。
    • 平台级二方库升级使用面广,升级困难。

ℹ️
上面的部署方式中,依赖容器的引入对于应用的开发应该是 透明 的。

🍺 目标

  • 给出类加载委托情况的完备说明
  • 给出类加载委托规则的规范描述
  • 给出类加载委托规则的规范描述的自己的一个描述格式
    对于这个项目会优先使用Properties来描述,简单够用。
  • 说明JavaClassLoader的用途和限制
  • 给出ClassLoader使用和实现的原则
  • ClassLoader使用和实现容易出错的地方
  • 整理出使用了ClassLoader的常见框架
  • 说明这些框架中ClassLoader的实现方法及其使用契约
  • 给出ClassLoader实现方法及其使用契约的最佳实践

📢 进阶目标

  • Web容器集成
  • 实现OSGi规范
    这个也可以用来验证实现是否面向编程友好

📚 相关资料

🔬 官方资料

ClassLoader

Permission

🔖 二手资料

ClassLoader

ClassLoader实际应用

安全

ClassLoader Memory Leak