前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >插件式开发架构综述

插件式开发架构综述

作者头像
河边一枝柳
发布2021-09-24 10:26:09
3.1K0
发布2021-09-24 10:26:09
举报
文章被收录于专栏:一个程序员的修炼之路

1. 概述

在现有软件开发中,业务越来越复杂,代码规模越来越大,依赖的人力也越来越多。为了降低系统模块内部耦合度,减少开发难度,也为了能够支持多团队的并行开发,插件式开发架构变得愈加流行,尤其是在桌面软件、移动端应用中。对于后端开发,微服务的形式也越来越流行,但是据笔者看来,微服务的很多设计思路,和插件式开发架构的设计理念也有相近之处。Eclipse, Visual Studio, VSCode等,都是插件式开发架构的典型案例。

现代软件提供插件式开发架构,一方面是服务于产品自身内部开发,另外一方面服务于市场化。借助于市场上各领域开发人员,在某一款软件(开源or商业)上进行面向新领域的开发,可以大大提高该产品的市场占有率,并衍生出一系列各领域的代理商及咨询业务。

2. 插件式开发架构要领

不管基于何种语言进行插件式开发框架的设计,有一些共同的要点需要具备。

2.1 插件运行主体

基于插件模式进行开发的软件,一般会存在一个运行主体。这个载体作为应用的主入口,并根据各类插件的配置信息,将编译或打包后的插件加载到主体环境中并执行。开发新的插件,无需调整现有运行主体的代码和二进制包。

2.2 插件的注入、配置和初始化

2.2.1 插件配置信息

配置信息即插件的描述信息,可以在代码中设置,也可以通过XML文件实现,方式不同,目的一致。

  • 插件名称
  • 插件版本号
  • 插件描述信息
  • 依赖的其他插件清单
2.2.2 插件的注入及初始化

插件的注入及初始化一般借助于继承插件基类,并实现插件框架中指定好的标准接口。通过继承插件基类,实现插件的注入;通过实现标准的初始化、启动、关闭等标准接口,实现插件的生命周期管理工作。

2.2.2.1 插件定义示例

下文代码是笔者参考开源软件Xfrogcn.PluginFactory做的说明。

https://gitee.com/WuYeCai/pluginfactory

  • 通过C#语言的Attribute特性对插件进行描述
  • 通过实现Init, StartAsync, StopAsync标准接口初始化、启动或关闭插件。这是对插件生命周期的管理。
代码语言:javascript
复制
 [Plugin(Alias = "PluginA", Description = "测试插件")]
 public class Plugin :: PluginBase, ISupportInitPlugin
 {
    public void Init(IPluginInitContext context)
    {
        //自定义初始化行为
    }
     
     public override Task StartAsync(IPluginContext context)
     {
         //自定义启动行为
         return base.StartAsync(context);
     }

     public override Task StopAsync(IPluginContext context)
     {
         //自定义关闭行为
         return base.StopAsync(context);
     }
 }

2.3. 插件通信机制

插件通信机制是一种通用概念。当各插件间协同完成一个功能时,彼此进行协调互助的一种机制。交互的形式有很多种,一种是插件对外开放自己的接口,接入到服务总线中供其他插件调用;一种是提供消息机制,插件之间通过发送消息进行事件处理。

2.3.1 基于虚拟服务总线形式的通信机制

基于虚拟服务总线形式的通信机制,每个插件都有自己的开放接口,这些接口会被注册到虚拟服务总线上,其他插件通过虚拟服务总线,获取到其他插件的接口服务。此处涉及到的内容是面向接口编程。

2.3.2 插件间消息通信

插件间消息通信属于一种开发人员可以自定义的扩展方式,插件运行主体无法定义所有的消息类型及消息的处理方法。所以用户可以通过约定消息形式以及自定义消息响应函数,实现插件间的通信。但是这样其实增强了插件之间的耦合度,不是特别推荐。笔者建议应用层插件尽量只依赖通用服务型插件及主体运行程序,业务插件保持独立。

3. C++语言下的插件开发案例

笔者曾基于某国外通用产品,进行领域化定制。该产品基于C++/MFC开发,并提供SDK包和样例工程,辅助用户基于该产品的插件开发。每个插件最终会编译为一个DLL,拷贝到产品指定目录下,就会被加载并执行。整体模式如下图所示。

  • 支持插件注入接口 通过继承插件中指定的基类并实现指定接口,达到插件注册及初始化的效果。
  • 支持用于注入菜单及子菜单的接口 通过菜单注入接口,开发人员可以在菜单中提供插件功能入口。用户可以通过该入口启用该插件。
  • 支持事件分发(已定义标准事件清单),插件可以接收事件,并开发自定义的事件处理程序 用户在实际设计过程中针对模型、针对工程目录、针对绘图区域的操作,会分发到所有的插件中,类似于广播。开发人员根据实际需要去实现或者不实现。
  • 提供接口,获取当前上下文信息 提供访问接口,用户获取用户当前选中的模型对象、选中的目录、选中的文件等信息。
  • 提供接口,用于所有业务对象的访问 提供访问接口,可以去创建、删除模型,创建、删除文件,创建删除工程管理目录等文件。
  • 插件以DLL的形式存在,进程内加载运行

4. JAVA体系下的插件开发案例

校园时代曾和同学一起开发过一款桌面端设计软件,就是基于Eclipse的RCP技术。基于插件的架构、扩展点等概念,依旧印象深刻。其中开发的基石Eclipse,就是基于OSGI规范进行开发。

4.1 OSGI简介

OSGi是基于java语言实现的开发期和运行期模块化技术。它的核心部分是一个框架,其中定义了应用程序的生命周期模式和服务注册。OSGI框架定义了大量的OSGI通用服务:日志、配置管理,XML解析等通用服务。

4.2 OSGI整体架构

4.2.1 模块层

模块层可以理解为开发出来的各类插件,一般以bundle的形式出现。一个完整的系统功能,往往由不同的模块插件进行配合完成。模块之间通过约定好的接口为外部提供服务。也确定了每个模块的边界,并进行封装。

模块层中bundle的特点:

  • bundle 以 jar 包形式存在的模块化物理单元,包含了代码,资源文件和元数据(模块描述信息)。
  • bundle 是开发、部署 OSGi 应用的基本单元。
  • bundle 的核心是 META-NF 目录下的 MANIFEST.MF 文件。
  • bundle 定义了内部包的对外可见性。
  • 每个 bundle 都有单独的类加载器。
4.2.2 生命周期层

管理bundle的创建,销毁。Bundle内部需要实现相关的接口,配合生命周期层的工作。

4.3.3 服务层

服务层可以理解为一个服务中心,每个插件将自己可以对外提供的功能通过服务层进行发布,并给其他插件提供了服务发现的方式及服务访问。

4.3 Eclipse插件示例

Eclipse插件示例,采用Eclipse中自带的Hello World案例进行说明。

插件的配置信息如下:

代码语言:javascript
复制
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: PluginDemo
Bundle-SymbolicName: PluginDemo
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: plugindemo.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-16
Automatic-Module-Name: PluginDemo
Import-Package: org.osgi.framework;version="1.3.0"
Bundle-ActivationPolicy: lazy

一个简单的插件示例代码如下:

代码语言:javascript
复制
package plugindemo;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

  @Override
  public void start(BundleContext context) throws Exception {
    System.out.println("Hello World!!");
  }
  
  @Override
  public void stop(BundleContext context) throws Exception {
    System.out.println("Goodbye World!!");
  }
}

采用Eclipse OSGI模式进行运行,结果输入如下。

代码语言:javascript
复制
WARNING: Using incubator modules: jdk.incubator.vector, jdk.incubator.foreign
Hello World!!
osgi> 22:48:00.337 [Worker-0: Loading available Gradle versions] INFO org.eclipse.buildship.core.internal.util.gradle.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
!SESSION 2021-08-31 22:47:57.492 -----------------------------------------------

5. 总结

本文简单说明了插件开发架构中的一些基本原则,以及采用不同语言开发的软件平台中的插件开发模式。需要注意的是,实际插件架构中,根据产品的不同、面向的领域不同、采用的编程语言不同,会有很多不一样的设计,需要结合具体情况进行设计。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个程序员的修炼之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 概述
  • 2. 插件式开发架构要领
    • 2.1 插件运行主体
      • 2.2 插件的注入、配置和初始化
        • 2.2.1 插件配置信息
        • 2.2.2 插件的注入及初始化
      • 2.3. 插件通信机制
        • 2.3.1 基于虚拟服务总线形式的通信机制
        • 2.3.2 插件间消息通信
    • 3. C++语言下的插件开发案例
    • 4. JAVA体系下的插件开发案例
      • 4.1 OSGI简介
        • 4.2 OSGI整体架构
          • 4.2.1 模块层
          • 4.2.2 生命周期层
          • 4.3.3 服务层
        • 4.3 Eclipse插件示例
        • 5. 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档