【TMF】源码分析 1.0 LatticeClassLoader

简介: LatticeClassLoader扩展Java双亲委派模型,支持多自定义类加载器的委托加载。类加载失败后依次尝试自定义加载器,实现插件化容错;资源获取优先父加载器,支持单资源查找与多资源聚合,适用于插件系统、多租户隔离及SPI扩展,保障业务隔离与灵活扩展。

代码实现逻辑总结

核心设计模式

这是一个多委托类加载器实现,扩展了Java标准的双亲委派模型:

关键实现特性

类加载策略 (loadClass)

先遵循双亲委派模型使用父类加载器

失败后依次尝试所有自定义加载器

实现了插件化类加载的容错机制

资源获取策略 (getResource / getResourceAsStream)

单个资源查找:返回第一个匹配的资源

使用函数式编程风格优雅处理null值

资源聚合策略 (getResources)

收集所有ClassLoader中的同名资源

支持SPI机制中的多服务提供者发现

应用场景

插件系统:动态加载不同插件JAR包中的类

多租户隔离:为不同业务加载独立的类实现

SPI扩展:支持从多个类加载器发现服务提供者

这种设计确保了Lattice框架能够灵活加载来自不同来源的扩展点实现,是实现业务隔离和插件化的基础设施。


/**
 * Lattice框架的自定义类加载器,支持多个自定义ClassLoader的委托加载
 * 实现了双亲委派模型的扩展,在父类加载器加载失败后,会尝试使用自定义加载器列表进行加载
 * 
 * @author Rocky Yu
 * @since 2022/10/10
 */
public class LatticeClassLoader extends ClassLoader {

    // 自定义类加载器列表,用于实现插件化的类加载机制
    @Getter
    private final List<ClassLoader> customLoaders = Lists.newArrayList();

    /**
     * 构造函数,指定父类加载器
     * @param parent 父类加载器
     */
    public LatticeClassLoader(ClassLoader parent) {
        super(parent);
    }

    /**
     * 重写类加载方法,实现多ClassLoader的委托加载策略
     * 加载顺序:
     * 1. 先使用父类加载器(遵循双亲委派模型)
     * 2. 如果父类加载器加载失败,遍历自定义加载器列表尝试加载
     * 3. 所有加载器都失败则抛出ClassNotFoundException
     * 
     * @param name 类的全限定名
     * @return 加载的Class对象
     * @throws ClassNotFoundException 如果所有加载器都无法加载该类
     */
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        try {
            // 优先使用父类加载器加载(双亲委派)
            return super.loadClass(name);
        } catch (ClassNotFoundException ex) {
            // 父类加载失败,遍历自定义加载器列表
            for (ClassLoader loader : customLoaders) {
                try {
                    // 尝试使用当前自定义加载器加载
                    return loader.loadClass(name);
                } catch (ClassNotFoundException ignored) {
                    // 当前加载器加载失败,继续尝试下一个
                }
            }
            // 所有加载器都失败,抛出异常
            throw new ClassNotFoundException(name);
        }
    }


    /**
     * 获取指定名称的资源URL
     * 查找顺序:
     * 1. 先从父类加载器查找
     * 2. 如果未找到,遍历自定义加载器列表查找,返回第一个找到的资源
     * 
     * @param name 资源名称
     * @return 资源的URL,未找到返回null
     */
    @Nullable
    @Override
    public URL getResource(String name) {
        // 优先从父类加载器获取资源
        URL url = super.getResource(name);
        if (null != url) {
            return url;
        }
        // 父类加载器未找到,使用Stream API遍历自定义加载器
        return getCustomLoaders().stream()
                .map(p -> p.getResource(name))  // 从每个加载器获取资源
                .filter(Objects::nonNull)        // 过滤掉null结果
                .findFirst().orElse(null);       // 返回第一个找到的资源
    }

    /**
     * 获取所有匹配的资源URL(支持多个同名资源)
     * 聚合策略:
     * 1. 收集父类加载器中的所有匹配资源
     * 2. 收集所有自定义加载器中的匹配资源
     * 3. 返回所有资源的聚合结果
     * 
     * @param name 资源名称
     * @return 所有匹配资源的URL枚举
     * @throws IOException 如果发生I/O错误
     */
    @Override
    public Enumeration<URL> getResources(String name) throws IOException {
        // 创建结果列表,用于聚合所有找到的资源
        List<URL> urls = Lists.newArrayList();
        
        // 先获取父类加载器中的所有资源
        Enumeration<URL> enumeration = super.getResources(name);
        while (enumeration.hasMoreElements()) {
            urls.add(enumeration.nextElement());
        }
        
        // 遍历所有自定义加载器,收集资源
        for (ClassLoader classLoader : getCustomLoaders()) {
            enumeration = classLoader.getResources(name);
            while (enumeration.hasMoreElements()) {
                urls.add(enumeration.nextElement());
            }
        }
        
        // 将List转换为Enumeration返回
        return new IteratorEnumeration<>(urls.iterator());
    }


    /**
     * 以输入流形式获取资源
     * 查找顺序:
     * 1. 先从父类加载器获取
     * 2. 如果未找到,遍历自定义加载器列表,返回第一个找到的资源流
     * 
     * @param name 资源名称
     * @return 资源的输入流,未找到返回null
     */
    @Nullable
    @Override
    public InputStream getResourceAsStream(String name) {
        // 优先从父类加载器获取资源流
        InputStream inputStream = super.getResourceAsStream(name);
        if (null != inputStream) {
            return inputStream;
        }
        // 父类加载器未找到,使用Stream API遍历自定义加载器
        return getCustomLoaders().stream()
                .map(p -> p.getResourceAsStream(name))  // 从每个加载器获取资源流
                .filter(Objects::nonNull)               // 过滤掉null结果
                .findFirst().orElse(null);              // 返回第一个找到的资源流
    }
}
目录
相关文章
|
21天前
|
运维 监控 供应链
Alibaba交易平台TMF2.0介绍
2017双11交易峰值达32.5万笔/秒,面对高并发与复杂业务需求,阿里推出TMF2.0框架,通过业务与平台分离、全链路可视化、配置化发布等创新,实现需求开发周期缩短至12天,支撑多业务快速试错与复用,构建可配置、可监控、可运维的电商技术新体系。
130 5
Alibaba交易平台TMF2.0介绍
|
20天前
|
存储 Java 编译器
【TMF】 解析器底层原理分析
该注解处理器在编译期自动扫描特定注解(如@Extension、@Business),收集标记的类或方法,生成SPI配置文件,实现服务接口与实现类的自动注册,提升开发效率与准确性。
85 2
|
20天前
|
缓存 弹性计算 人工智能
阿里云服务器g9i实例:第九代企业级云服务器,2核8G、4核16G、8核32配置优惠价格
阿里云服务器ECS通用型g9i实例,是阿里云第九代企业级高性能云服务器实例规格,适用高网络包收发场景,游戏服务器,中小型数据库系统、缓存、搜索集群,搜索推广类应用,网站和应用服务器,数据分析和计算,安全可信计算场景。目前g9i云服务器2核8G、4核16G和8核32配置都有活动,实例1个月/3个月/6个月8折,1年低至6.4折。下面是小编整理的g9i云服务器实例性能测评及优惠价格。
|
20天前
|
弹性计算
阿里云服务器最便宜多少钱一年?到底是38元?还是68元1年?
阿里云新用户可抢38元/年的轻量应用服务器(2核2G、200M带宽、40G硬盘),每日限量抢购,非新用户可选99元/年的ECS经济型实例,配置更高且续费同价。
134 8
|
20天前
|
SQL 数据可视化 关系型数据库
专为 Apache Doris 打造的可视化数据管理工具 SelectDB Studio
SelectDB Studio 提供 Desktop & Server 双版本,专注于为用户提供高效、便捷的可视化操作体验,帮助数据开发者、DBA 低门槛、高效率地对 Apache Doris 及其兼容数据库中的数据进行可视化开发和管理。
124 0
|
监控 Java 开发者
Java虚拟机(JVM)深度优化指南####
本文深入探讨了Java虚拟机(JVM)的工作原理及其性能优化策略,旨在帮助开发者通过理解JVM的内部机制来提升Java应用的运行效率。不同于传统的技术教程,本文采用案例分析与实战技巧相结合的方式,为读者揭示JVM调优的艺术。 ####
392 8
|
存储 前端开发 数据库
基于python flask 的图书管理系统,有登录界面,实现简单增删改查,可以做课程设计使用
本文介绍了一个基于Python Flask框架的图书管理系统,该系统具备登录界面,并实现了基本的增删改查功能,适合作为课程设计使用。
557 3
基于python flask 的图书管理系统,有登录界面,实现简单增删改查,可以做课程设计使用
|
Kubernetes 应用服务中间件 API
【Ingress 秘籍】集群进出流量的总管:揭秘 Kubernetes 中 Ingress 的终极奥秘!
【8月更文挑战第25天】Ingress是Kubernetes中用于管理HTTP与HTTPS流量进入集群的核心功能。作为集群内外通信的桥梁,Ingress通过定义规则将外部请求导向内部服务。本文详细介绍了Ingress的基本概念、配置方法及其实现方式。通过使用不同的Ingress控制器(如Nginx、Traefik等),用户可以根据需要选择最适合的方案。文中还提供了示例代码展示如何创建服务、部署应用及配置Ingress规则。
433 6
|
数据采集 机器学习/深度学习 算法
Python实现用PSO粒子群优化算法对KMeans聚类模型进行优化项目实战
Python实现用PSO粒子群优化算法对KMeans聚类模型进行优化项目实战
|
监控 数据采集 机器学习/深度学习
AIGC-Whisper模型
6月更文挑战第1天