ESA Cabin is a lightweight class isolation container for Java applications.

Overview

Cabin

Build codecov Maven Central GitHub license

Cabin是一款Java类隔离框架,用于隔离各个模块之间、模块和业务之间使用的第三方依赖库。使用Cabin后,模块SDK将只是用自己指定 版本的三方依赖,不会被业务引入的依赖所影响,保证业务正常运行,提高业务的开发效率,减少中间件同学此类重复的支持工作。

概述

ESA Cabin用于解决模块间类冲突。要解决的问题是,当一个应用项目中使用到了多个框架或者SDK(后续称为LibModule)时, 各个LibModule之间、LibModule和应用业务代码(后续称为BizModule)之间可能存在包冲突,即“使用了三方依赖的不兼容的不同版本”。 业务遇到包冲突的时候,需要进行进行复杂的排包工作和兼容性实验,以期待找到一个能兼容各个模块的依赖版本;当LibModule使用 了不兼容的三方依赖版本发布的时候,还需要多方协调。一旦遇到依赖冲突,将严重影响开发效率和体验。

举例说明依赖冲突。假设如下场景,如果工程需要引入两个框架:FrameworkA 和 FrameworkB,其中 FrameworkA 需要依赖版本号为 1.0.0 的 DependencyC, FrameworkB 需要依赖版本号为 2.0.0 的 DependencyC,而这两个版本无法兼容(例如类名、方法名、参数有 变化或者增删):

image.png

为了彻底解决包冲突的问题,需要借助类隔离机制,将各个Framework打包为一个LibModule,使用各自的 ClassLoader 加载,业务代码 使用单独的ClassLoader加载,达到相互隔离的目的:

image.png

Quick Start

LibModule开发者

框架、中间件、SDK可以打包为特定的格式,使用Cabin容器来和业务代码进行类隔离。

打包

目前只支持maven打包。

打包插件

  • 使用cabin-module-maven-plugin插件打包 具体使用方式参考下文举例。

单module项目打包

将一个module及其依赖打包。此种打包方式需要指定classifier(例子里有说明),用户根据classifier来区分使用fatjar和普通jar。 此种打包方式会将module的所有maven依赖传递给使用者,使用者可以任意update/exclude这些依赖,不会影响module中的代码。
用esa-rpc-filter这个module举例:

...
<artifactId>esa-rpc-cluster</artifactId>
...
<dependencies>
    ...
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>io.esastack</groupId>
            <artifactId>cabin-module-maven-plugin</artifactId>
            <version>${cabin.version}</version>
            <executions>
                <execution>
                    <id>default-cli</id>
                    <goals>
                        <goal>module-repackage</goal>
                    </goals>
                    <configuration>
                        ## 一定要指定,否则会覆盖原有的标准jar包
			<classifier>cabin-module</classifier>
			## 需要导出的类、包、资源,一般使用package即可,资源一般是SPI的配置文件,Spring的xsd和spring.fatories文件等。
                        <exported>
                            <packages>
                                <package>esa.rpc.cluster</package>
                                <package>esa.commons.serialize</package>
                            </packages>
                            <resources>
                                <resource>META-INF/services/</resource>
                            </resources>
                        </exported>

                        ##imported的类是指被打包的模块需要优先从业务classpath中加载的类,比如Spring一般是使用业务的依赖;如果模块需要通过方法的参数和返回值传递三方依赖的类和对象,也建议使用import的方式从业务classpath加载这些类(例如需要传递netty的ChannelHandlerContext);提供了SPI接口的三方包,也建议从biz中import。
                        <imported>
                            <packages>
                                <package>javassist</package>
                                <package>org.springframework</package>
                            </packages>
                        </imported>
			##不影响使用的SPI,可以不import,在此处排除
 			<excludeCheckedSPIs>
			 	<excludeCheckedSPI>com.fasterxml.jackson.core.JsonFactory</excludeCheckedSPI>
                                <excludeCheckedSPI>io.grpc</excludeCheckedSPI>
                        </excludeCheckedSPIs>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

多module项目合并打包成All-In-One的FatJar

如果要将多个module项目合并打包成一个FatJar, 具体步骤如下:

  • 创建一个新module来依赖需要合并在一起的module,如xxx-cabin,在其pom中依赖此项目的所有其他module和额外需要的三方依赖;
  • 在xxx-cabin的pom中加入cabin-module-maven-plugin插件,并配置相关信息

下面用ESA RPC举例讲解如何打包: + esa-rpc-cabin (会传递三方依赖) ```xml ...加入需要合并打包的module和依赖 io.esastack cabin-module-maven-plugin ${cabin.version} default-cli module-repackage ### 需要导出的类、包、资源,一般使用package即可,资源一般是SPI的配置文件,Spring的xsd和spring.fatories文件等。 esa.rpc esa.commons.serialize esa.rpc.filter.consumer.Async2SyncFlter # META-INF/services/ META-INF/esa/ META-INF/esa-rpc.xsd META-INF/spring.* esa/rpc/spring/boot/autoconfigure/ESABeanConfiguration.class esa/rpc/spring/boot/autoconfigure/banner/ESAWelcomeLogoApplicationListener.class
                    #imported的类是指被打包的模块需要优先从业务classpath中加载的类,比如Spring一般是使用业务的依赖;如果模块需要通过方法的参数和返回值传递三方依赖的类和对象,也建议使用import的方式从业务classpath加载这些类(例如需要传递netty的ChannelHandlerContext);提供了SPI接口的三方包,也建议从biz中import。
                    <imported>
                        <packages>
                            <package>javassist</package>
                            <package>org.springframework</package>
                        </packages>
                    </imported>
                    ##开启检查SPI类的功能
                    <checkSPI>true</checkSPI>
		##在此处排除排除掉不必import的SPI类
		<excludeCheckedSPIs>
		 	<excludeCheckedSPI>com.fasterxml.jackson.core.JsonFactory</excludeCheckedSPI>
                            <excludeCheckedSPI>io.grpc</excludeCheckedSPI>
                    </excludeCheckedSPIs>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>
```

SPI资源文件和相关类的处理

  • 如果SDK自定义了SPI接口或者实现了其他的SPI接口,这些类都需要配置为exported(一般在exported/packages/package中已经包含,不用额外配置);
  • 如果SDK中引用了定义定实现了SPI接口的三方依赖,在启动的时候可能出现SPI接口和实现类不是一个ClassLoader加载的错误,此时建议import此三方依赖的类;
  • 可以开启checkSPI配置:会扫描所有依赖的SPI文件,如果SPI接口和实现类没被imported/exported则报错;如果确定不影响SDK使用的SPI,可以使用excludeCheckedSPIs/excludeCheckedSPI来跳过某个包或类的检查,具体的配置见上面的配置示例。

多module项目,其中任意个数的modules组合打包

当一个项目中有多个modules,业务使用方可以根据需要引用其中的一个或者一些module的情况下,按照下面的原则配置

业务开发者

引入依赖

  • 使用Cabin启动项目,应用需要在maven依赖中加入Cabin的依赖:
<dependency>
    <groupId>io.esastack</groupId>
    <artifactId>cabin-core</artifactId>
    <version>${cabin.version}</version>
</dependency>
  • 引用框架、中间件、SDK的支持类隔离的maven版本(使用Cabin插件打包的版本) 以ESA RPC(多模块打包)和ESA RPC Auth(单模块打包)为例:
<dependency>
    <groupId>esa</groupId>
    <artifactId>esa-rpc-cabin</artifactId>
    <version>${esa.rpc.version}</version>
</dependency>
<dependency>
    <groupId>esa</groupId>
    <artifactId>esa-rpc-auth</artifactId>
    <classifier>${cabin-classifier}</classifer>
    <version>${esa.rpc.version}</version>
</dependency>

开发

在main函数中添加一行代码,这行代码必须在main函数的第一行,在其前面不可以有任何代码。

public static void main(String[] args) {
    //此行代码必须添加,且必须在main函数内的第一行,
    //任何代码、包括打日志均不能在此之前。
    CabinAppBootstrap.run(args);

    //应用本来需要执行的业务代码,无论是普通应用、Spring、Springboot应用,均如此。
    ...
}

单元测试

单元测试时如果包冲突问题也可以用类隔离功能解决,目前只支持junit4.

  • 普通单元测试 在单元测试用例类上添加@Runwith注解,如下所示:
@RunWith(CabinJUnit4Runner.class)
public class UnitTest {

    @Test
    public void curatorTest() {
       //do test logic
    }
}
  • 使用spring-test进行单测 在单元测试用例类上添加@Runwith注解,如下所示:
@RunWith(CabinSpringRunner.class)
@SpringBootTest
public class UnitTest {

    @Test
    public void curatorTest() {
       //do test logic
    }
}

打包发布

zip包发布

如果用户使用java -classpath xxxApplicaion的方式启动,那么按照以前的打包方式即可。

jar包发布

如果要使用java -jar aaaApp.jar的方式启动,打包发布时需要使用Cabin的打包插件(目前只支持Maven)。

  • 添加Cabin Maven Plugin 不能再用springboot的打包插件(spring-boot-maven-plugin)打包,要用下面这个:
<plugin>
    <groupId>io.esastack</groupId>
    <artifactId>cabin-maven-plugin</artifactId>
    <version>${cabin.version}</version>
    <executions>
        <execution>
            <id>default-cli</id>
            <goals>
                <goal>repackage</goal>
            </goals>

            <configuration>
		<mainClass>应用主类全限定名</mainClass>
            </configuration>
        </execution>
    </executions>
</plugin>
  • 使用java -jar启动进程 应用打包后会在target目录下生成*.jar的文件,只要使用java -jar运行此文件即可。
You might also like...

MapDB provides concurrent Maps, Sets and Queues backed by disk storage or off-heap-memory. It is a fast and easy to use embedded Java database engine.

MapDB: database engine MapDB combines embedded database engine and Java collections. It is free under Apache 2 license. MapDB is flexible and can be u

Dec 30, 2022

MariaDB Embedded in Java JAR

What? MariaDB4j is a Java (!) "launcher" for MariaDB (the "backward compatible, drop-in replacement of the MySQL(R) Database Server", see FAQ and Wiki

Jan 4, 2023

Unified Queries for Java

Querydsl Querydsl is a framework which enables the construction of type-safe SQL-like queries for multiple backends including JPA, MongoDB and SQL in

Dec 31, 2022

requery - modern SQL based query & persistence for Java / Kotlin / Android

requery - modern SQL based query & persistence for Java / Kotlin / Android

A light but powerful object mapping and SQL generator for Java/Kotlin/Android with RxJava and Java 8 support. Easily map to or create databases, perfo

Jan 5, 2023

Speedment is a Stream ORM Java Toolkit and Runtime

Speedment is a Stream ORM Java Toolkit and Runtime

Java Stream ORM Speedment is an open source Java Stream ORM toolkit and runtime. The toolkit analyzes the metadata of an existing SQL database and aut

Dec 21, 2022

A blazingly small and sane redis java client

A blazingly small and sane redis java client

Jedis Jedis is a blazingly small and sane Redis java client. Jedis was conceived to be EASY to use. Jedis is fully compatible with redis 2.8.x, 3.x.x

Jan 8, 2023

jOOQ is the best way to write SQL in Java

jOOQ's reason for being - compared to JPA Java and SQL have come a long way. SQL is an "ancient", yet established and well-understood technology. Java

Jan 4, 2023

MapDB provides concurrent Maps, Sets and Queues backed by disk storage or off-heap-memory. It is a fast and easy to use embedded Java database engine.

MapDB: database engine MapDB combines embedded database engine and Java collections. It is free under Apache 2 license. MapDB is flexible and can be u

Jan 1, 2023

jdbi is designed to provide convenient tabular data access in Java; including templated SQL, parameterized and strongly typed queries, and Streams integration

The Jdbi library provides convenient, idiomatic access to relational databases in Java. Jdbi is built on top of JDBC. If your database has a JDBC driv

Dec 27, 2022
Owner
ESA Stack
Elastic Service Architecture
ESA Stack
A lightweight and performant Java ORM alternative.

LightORM A lightweight and performant Java ORM alternative. LightORM has annotation processors so that all the integration code with the database is g

Jailson Pereira 14 Nov 22, 2022
ObjectBox is a superfast lightweight database for objects

ObjectBox Java (Kotlin, Android) ObjectBox is a superfast object-oriented database with strong relation support. ObjectBox is embedded into your Andro

ObjectBox 4.1k Dec 30, 2022
The application is a PoC that helps in identifying modern bankers, potentially malicious and remote controlling applications abusing Android AccessibilityService.

Motivation Project aims to help in: identifying keyloggers and events hijacking malicious applications such as Anubis/TeaBot, identifying a "fake bank

Krzysztof Pranczk 33 Dec 9, 2022
Hi, Spring fans! In this installment, we'll look at how to build tenancy-aware JDBC applications

Multitenant JDBC You'll need to spin up two separate PostgreSQL instances. Put this script into a file called postgres.sh: #!/usr/bin/env bash NAME=${

Spring Tips 19 Nov 7, 2022
H2 is an embeddable RDBMS written in Java.

Welcome to H2, the Java SQL database. The main features of H2 are: Very fast, open source, JDBC API Embedded and server modes; disk-based or in-memory

H2 Database Engine 3.6k Jan 5, 2023
A blazingly small and sane redis java client

Jedis Jedis is a blazingly small and sane Redis java client. Jedis was conceived to be EASY to use. Jedis is fully compatible with redis 2.8.x, 3.x.x

Redis 10.8k Dec 31, 2022
Elasticsearch Java Rest Client.

JEST Jest is a Java HTTP Rest client for ElasticSearch. ElasticSearch is an Open Source (Apache 2), Distributed, RESTful, Search Engine built on top o

Searchly 2.1k Jan 1, 2023
Java binding for etcd

jetcd: Java binding for etcd TravisCI: CircleCI: A simple Java client library for the awesome etcd Uses the Apache HttpAsyncClient to implement watche

Justin Santa Barbara 134 Jan 26, 2022
LINQ-style queries for Java 8

JINQ: Easy Database Queries for Java 8 Jinq provides developers an easy and natural way to write database queries in Java. You can treat database data

Ming Iu 641 Dec 28, 2022