【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);              // 返回第一个找到的资源流
    }
}
目录
相关文章
|
4月前
|
运维 监控 供应链
Alibaba交易平台TMF2.0介绍
2017双11交易峰值达32.5万笔/秒,面对高并发与复杂业务需求,阿里推出TMF2.0框架,通过业务与平台分离、全链路可视化、配置化发布等创新,实现需求开发周期缩短至12天,支撑多业务快速试错与复用,构建可配置、可监控、可运维的电商技术新体系。
655 5
Alibaba交易平台TMF2.0介绍
|
4月前
|
存储 Java 编译器
【TMF】 解析器底层原理分析
该注解处理器在编译期自动扫描特定注解(如@Extension、@Business),收集标记的类或方法,生成SPI配置文件,实现服务接口与实现类的自动注册,提升开发效率与准确性。
176 2
|
存储 SQL 搜索推荐
业务系统架构实践总结
作者从2015年起至2022年,在业务平台(结算、订购、资金)、集团财务平台(应收应付、账务核算、财资、财务分析、预算)、本地生活财务平台(发票、结算、预算、核算、稽核)所经历的业务系统研发实践的一个总结。1.核心是面向复杂性业务支撑的实践经验(个人概念里的“复杂业务“,大概至少面向5类行业若干业务线且业态差异很大),文章不涉及性能、稳定性、资损防控、大数据离线研发,聚焦在线业务系统架构对多态业务的包容性、开放性、灵活性、可读性。2.文章较多强调”个人”两字,因为仅是我个人在实践上归纳总结的一些方式方法。3.实践经验主要来自两类,一类是接手旧系统,得以见识不一样的设计,文中“见过”特指。
3108 32
|
4月前
|
Dubbo Java 测试技术
【Lattice】设计原理
Lattice 是一个轻量级业务扩展调用框架,通过模块化架构实现复杂业务定制的高效管理。支持动态发现、加载与执行扩展,提供清晰的分层设计,集成 Spring、Dubbo 等主流技术,助力企业应用灵活扩展。
389 0
|
4月前
|
缓存 前端开发 Java
【lattice】 lattice-dynamic-loading 深度源码分析
lattice-dynamic-loading 模块实现插件热加载与运行时扩展,通过独立类加载器保障隔离性,集成 Spring 生态,支持动态注册 Bean 与 MVC。基于策略模式、门面模式和 SPI 机制,实现高扩展性、可维护性与热部署能力,提升系统敏捷性。(239字)
151 1
|
4月前
|
缓存 测试技术 双11
【Lattice】最佳实践
Lattice-Model 支持多租户SaaS、电商营销、ERP行业定制及微服务扩展,通过插件化实现业务隔离与动态加载,提升系统灵活性与可维护性。
209 0
|
缓存 运维 NoSQL
使用 psutil 获取硬件、网络以及进程信息
使用 psutil 获取硬件、网络以及进程信息
345 0
|
消息中间件 Kubernetes Java
MQ产品使用合集之RocketMQ发消息失败了,proxy报connect to null failed如何解决
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
2914 2
MQ产品使用合集之RocketMQ发消息失败了,proxy报connect to null failed如何解决
|
运维 Kubernetes 监控
一文读懂蓝绿发布、A/B 测试和金丝雀发布的优缺点
目前,业界已经总结出了几种常见的服务发布策略来解决版本升级过程中带来的流量有损问题。本文首先会对这些普遍的发布策略进行简单的原理解析,最后结合阿里云的云原生网关对这些发布策略进行实践。
3490 96
一文读懂蓝绿发布、A/B 测试和金丝雀发布的优缺点

热门文章

最新文章