插件框架IPlugin

插件框架 IPlugin

Title: 插件框架 IPlugin

URL Source: https://newlifex.com/core/plugin

Markdown Content:
业务开发中常用到插件架构实现，特别是外挂 DLL 形式的插件。我们封装了插件管理的通用代码。

源码：https://github.com/NewLifeX/X/blob/master/NewLife.Core/Model/IPlugin.cs

快速入门

星尘代理 StarAgent就支持加载插件来实现扩展功能。以下是星尘代理加载并初始化插件的代码。

Copy
// 插件管理器
var pm = _PluginManager = new PluginManager
{
Identity = "StarAgent",
Provider = this,

Log = XTrace.Log,
};
_container.AddSingleton(pm);

// 启动插件
WriteLog("启动插件[{0}]", pm.Identity);
pm.Load();
pm.Init();
foreach (var item in pm.Plugins)
{
if (item is IAgentPlugin plugin)
{
try
{
plugin.Start();
}
catch (Exception ex)
{
XTrace.WriteException(ex);
}
}
}

管理类 PluginManager 负责 Load 加载插件，支持扫描外部 DLL。紧接着 Init 通知插件执行初始化工作。最后就可以执行自己的业务操作了，星尘代理这里是 Start。

所谓插件，其实就是实现了 IPlugin 接口，或者添加了 PluginAttribute 特性，如下：

Copy
/// 星尘代理插件
public interface IAgentPlugin : IPlugin
{
/// 开始工作
public void Start();

/// 停止工作
///
public void Stop(String reason);
}

/// 星尘代理插件基类
[Plugin("StarAgent")]
public abstract class AgentPlugin : DisposeBase, IAgentPlugin
{
/// 服务提供者
public IServiceProvider? Provider { get; set; }

/// 初始化插件
///
///
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public virtual Boolean Init(String? identity, IServiceProvider provider)
{
if (identity != "StarAgent") return false;

Provider = provider;

return true;
}

/// 开始工作
public virtual void Start() { }

/// 停止工作
///
public virtual void Stop(String reason) { }
}

核心原理

通用插件实现，一般是接口或者特性。

Copy
/// 通用插件接口
/// <remarks>
/// 为了方便构建一个简单通用的插件系统，先规定如下：
/// 1，负责加载插件的宿主，在加载插件后会进行插件实例化，此时可在插件构造函数中做一些事情，但不应该开始业务处理，因为宿主的准备工作可能尚未完成
/// 2，宿主一切准备就绪后，会顺序调用插件的Init方法，并将宿主标识传入，插件通过标识区分是否自己的目标宿主。插件的Init应尽快完成。
/// 3，如果插件实现了<see cref="IDisposable"/>接口，宿主最后会清理资源。
/// </remarks>
public interface IPlugin
{
/// 初始化
/// 插件宿主标识
/// 服务提供者
/// <returns>返回初始化是否成功。如果当前宿主不是所期待的宿主，这里返回false</returns>
Boolean Init(String? identity, IServiceProvider provider);
}

/// 插件特性。用于判断某个插件实现类是否支持某个宿主
/// <remarks>实例化</remarks>
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class PluginAttribute(String identity) : Attribute
{
/// 插件宿主标识
public String Identity { get; set; } = identity;
}

插件接口很简单，只有一个 Init。因为可能有多套插件系统，为了区分开来不同类型的插件，必须指定 Identity。

服务提供者 IServiceProvider 也很重要，方便插件内部获取各种服务实现。

可以看到，我们 IPlugin 接口只有 Init 初始化，并没有销毁的接口。一般来说，我们使用 IDispose 来表示插件销毁，因此 IPlugin 无需再次定义类似方法。

为了管理插件，引入管理类 PluginManager。

Identity。指定当前插件标识，只加载该标识的插件。例如前面入门代码的 StarAgent

Provider。宿主的服务提供者，插件一般需要跟其它对象交互，这里就是纽带

Plugins。插件集合，保管着已加载插件

LoadPlugins。扫描插件，可以快速得到插件集合

Load。加载插件，扫描后并修改 Plugins 集合

Init。初始化插件，依次调用插件的 Init 接口