# xkernel **Repository Path**: javacoo/xkernel ## Basic Information - **Project Name**: xkernel - **Description**: xkernel是一个基于java SPI思想的类加载工具包,是构建微内核系统的基础,微内核不与扩展点的具体实现产生交互,通过ExtensionLoader将扩展点与具体实现建立关联,微内核只需要知道自己暴露的扩展点和ExtensionLoader即可,扩展千变化万,内核以不变应万变。采用本工具包可快速设计一个基于微内核+插件式的扩展开发框架,不需要改动源码就可以实现扩展,解耦,实现扩展对原来的代码几乎没有侵入性,只需要添加配置就可以实现扩展,符合开闭原则。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 11 - **Created**: 2020-03-27 - **Last Updated**: 2025-01-18 ## Categories & Tags **Categories**: utils **Tags**: None ## README # xkernel微内核系统工具 #### 背景 > SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件,SPI的作用就是为这些被扩展的API寻找服务实现,从java spi的原理中可以了解到,java的spi机制有着如下的弊端: > 1. 只能遍历所有的实现,并全部实例化。 > 2. 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。 #### 软件架构 软件架构说明 ![类结构图](https://images.gitee.com/uploads/images/2020/0607/201451_164da17b_121703.png "屏幕截图.png")
![微内核系统设计简图](https://images.gitee.com/uploads/images/2020/0607/202221_d5358568_121703.png "屏幕截图.png")
![ExtensionLoader类加载过程流程图](https://images.gitee.com/uploads/images/2020/0607/202328_feb89d2e_121703.png "屏幕截图.png")
>微内核不与扩展点的具体实现产生交互,通过ExtensionLoader将扩展点与具体实现建立关联,微内核只需要知道自己暴露的扩展点和ExtensionLoader即可,扩展千变化万,内核以不变应万变。 > >l 术语说明: > >1, SPI:Service Provider Interface 。 > >2, 扩展点:被@Spi注解的 Interface 为一个扩展点。 > >3, 扩展:被@Spi注解的Interface 的实现称为这个扩展点的一个扩展。 > >l 扩展点约定: > >1, 扩展点必须是Interface类型,必须被@Spi注解,满足这两点才是一个扩展点。 > > > >l 扩展定义约定: > >1, 在META-INF/services/$扩展点接口的全类名 > >META-INF/ext/$扩展点接口的全类名 , > >META-INF/ext/internal/$扩展点接口的全类名 , > >这些路径下定义的文件名称为 $扩展点接口的全类名 , 文件中以键值对的方式配置扩展点的扩展实现。例如文件 META-INF/ext/internal/com.ximg.api.ImgHandler中定义的扩展 : > >thumbnailator=com.ximg.impl.ThumbnailatorImgHandler > >l 默认扩展: > >1, 被@Spi("abc")注解的Interface,那么这个扩展点的缺省适应扩展就是 SPI 配置文件中 key 为 "abc" 的扩展。 > >l Spi注解类,该注解作用于扩展点的接口上,表明该接口是一个扩展点,属性 value 用来指定默认适配扩展点的名称。定义如下: > >@Documented > >@Retention(RetentionPolicy.RUNTIME) > >@Target(ElementType.TYPE) > >public @interface Spi { > >​ String value() default ""; > >} > >l ExtensionLoader:扩展点实现加载器。 > >1, 扩展加载器是本方案的核心组件,它控制内部所有扩展点的初始化、加载扩展的过程。 > >2, 包含的静态属性: > >EXTENSION_LOADERS:保存了内核开放的扩展点对应的 ExtensionLoader 实例对象。 > >EXTENSION_INSTANCES:保存了扩展类型 (Class) 和扩展类型的实例对象。 > >type : 被 @SPI 注解的 Interface , 也就是扩展点。 > >cachedNames : 保存不满足装饰模式(不存在只有一个参数,并且参数是扩展点类型实例对象的构造函数)的扩展的名称。 > >cachedClasses : 保存不满足装饰模式的扩展的 Class 实例 , 扩展的名称作为 key , Class 实例作为 value。 > >cachedInstances : 保存扩展的名称和实例对象 , 扩展名称为 key , 扩展实例为 value。 > >cachedDefaultName : 扩展点上 @SPI 注解指定的缺省适配扩展。 > >cachedWrapperClasses : 满足装饰模式的扩展的 Class 实例。 > >exceptions : 保存在加载扩展点配置文件时,加载扩展点过程中抛出的异常 , key 是当前读取的扩展点配置文件的一行 , value 是抛出的异常。 > >3, 类加载过程: > >首先通过 ExtensionLoader 的 getExtensionLoader 方法获取一个 ExtensionLoader 实例,然后再通过 ExtensionLoader 的 getExtension 方法获取拓展类对象。这其中,getExtensionLoader 方法用于从缓存中获取与拓展类对应的 ExtensionLoader,若缓存未命中,则创建一个新的实例,主要有以下步骤: > >a. T getExtension(String name)方法:首先检查缓存,缓存未命中则创建拓展对象 > >b. T createExtension(String name) 方法,包含了如下的步骤: > >i. 通过 getExtensionClasses 获取所有的拓展类 > >ii. 通过反射创建拓展对象 > >c. Map> getExtensionClasses():在通过名称获取拓展类之前,首先需要根据配置文件解析出拓展项名称到拓展类的映射关系表(Map<名称, 拓展类>),之后再根据拓展项名称从映射关系表中取出相应的拓展类即可,这里也是先检查缓存,若缓存未命中,则通过 synchronized 加锁。加锁后再次检查缓存,并判空。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类。 > >d. Map> loadExtensionClasses():loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件。 > >e. Void loadDirectory(Map> extensionClasses, String dir): loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源。 > >f. Void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL): loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作。 > >g. void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException:loadClass 方法用于主要用于操作缓存,如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。 > >采用本工具包可快速设计一个基于微内核+插件式的扩展开发框架,不需要改动源码就可以实现扩展,解耦,实现扩展对原来的代码几乎没有侵入性,只需要添加配置就可以实现扩展,符合开闭原则。 > #### 安装教程 引入微内核系统核心包 ``` com.javacoo xKernel 1.0.0 ``` #### 使用说明 1,设计需要扩展的接口类,如:。 ``` /** * 数据处理 *

说明:

*
  • * * @Author DuanYong * @Since 2019/8/30 23:41 * @Version 1.0 */ public interface DataHandler { /** * 处理 *

    说明:

    *
  • * @Author DuanYong * @Since 2019/8/30 23:42 * @Version 1.0 * @Params data */ void handle(final T data); } ``` 2,在接口上添加注解@Spi,如: ``` /** * 数据处理 *

    说明:

    *
  • * * @Author DuanYong * @Since 2019/8/30 23:41 * @Version 1.0 */ @Spi("default") public interface DataHandler { /** * 处理 *

    说明:

    *
  • * @Author DuanYong * @Since 2019/8/30 23:42 * @Version 1.0 * @Params data */ void handle(final T data); } ``` 3,实现扩展接口类。 ``` /** * 抽象数据处理类 *

    说明:

    *
  • * * @Author DuanYong * @Since 2019/8/30 23:47 * @Version 1.0 */ @Slf4j public abstract class AbstractDataHandler implements DataHandler { protected ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(),Executors.defaultThreadFactory()); @Override public final void handle(final String data){ executorService.submit(()->{ //解析 T t = parser(data); if(null == t){ return; } //执行处理 doHandle(t); }); } /** * 执行处理 *

    说明:

    *
  • * @Author DuanYong * @Since 2019/8/30 23:57 * @Version 1.0 * @Params t */ protected abstract void doHandle(T t); /** * 解析数据 *

    说明:

    *
  • * @Author DuanYong * @Since 2019/8/30 23:55 * @Version 1.0 * @Params rawData 原始数据 * @Return T */ protected abstract T parser(String rawData); } ``` 4,在项目或jar包的META-INF/services/或者META-INF/ext或者META-INF/ext/internal目录下,创建一个文本文件:名称为接口的“全限定名”,内容格式为:实现名=实现类的全限定名。 ``` 文件:com.javacoo.swing.api.data.DataHandler 内容:default=com.javacoo.swing.core.data.AliPayDataHandler ``` 5,使用ExtensionLoader.getExtensionLoader(接口类型)方法,获取对应接口类型的ExtensionLoader<接口类型> 实例对象。 ``` /**数据处理服务*/ private DataHandler dataHandler; public MyNetworkDelegate(){ dataHandler = ExtensionLoader.getExtensionLoader(DataHandler.class).getDefaultExtension(); } ``` 6,使用ExtensionLoader<接口类型> 实例对象,调用getExtension(扩展点名称)方法,获取对应扩展点名称的扩展实现。 #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 码云特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)