【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);              // 返回第一个找到的资源流
    }
}
目录
相关文章
|
1天前
|
数据采集 人工智能 自然语言处理
3分钟采集134篇AI文章!深度解析如何通过云无影AgentBay实现25倍并发 + LlamaIndex智能推荐
结合阿里云无影 AgentBay 云端并发采集与 LlamaIndex 智能分析,3分钟高效抓取134篇 AI Agent 文章,实现 AI 推荐、智能问答与知识沉淀,打造从数据获取到价值提炼的完整闭环。
295 90
|
9天前
|
机器人 API 调度
基于 DMS Dify+Notebook+Airflow 实现 Agent 的一站式开发
本文提出“DMS Dify + Notebook + Airflow”三位一体架构,解决 Dify 在代码执行与定时调度上的局限。通过 Notebook 扩展 Python 环境,Airflow实现任务调度,构建可扩展、可运维的企业级智能 Agent 系统,提升大模型应用的工程化能力。
|
人工智能 前端开发 API
前端接入通义千问(Qwen)API:5 分钟实现你的 AI 问答助手
本文介绍如何在5分钟内通过前端接入通义千问(Qwen)API,快速打造一个AI问答助手。涵盖API配置、界面设计、流式响应、历史管理、错误重试等核心功能,并提供安全与性能优化建议,助你轻松集成智能对话能力到前端应用中。
730 154
|
15天前
|
人工智能 数据可视化 Java
Spring AI Alibaba、Dify、LangGraph 与 LangChain 综合对比分析报告
本报告对比Spring AI Alibaba、Dify、LangGraph与LangChain四大AI开发框架,涵盖架构、性能、生态及适用场景。数据截至2025年10月,基于公开资料分析,实际发展可能随技术演进调整。
978 152
|
2天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
2天前
|
数据采集 人工智能 搜索推荐
别再“调教”ChatGPT了!用Qwen2.5打造24小时在线数字分身
在AI时代,专属“数字分身”正从科幻走向现实。依托Qwen2.5-14B大模型、LoRA微调技术及LLaMA-Factory Online平台,仅需四步即可打造会说话、懂风格、能办事的个性化AI助手,让每个人拥有自己的“贾维斯”。
202 152