.Net插件框架的实现及分析(二)
2011-09-27 18:03 w i n s o n 阅读(1608) 评论(3) 编辑 收藏 举报
呵,很久之前发表了 .Net插件框架的实现及分析(一) ,只是一直没在此接上,只是在我自己的代码部落里更新了,现在也加上吧:
话接上回, 让我们来继续分析下这个插件框架如何实现吧。既然是插件,就必须得动态加载,只需将编译好的插件DLL文件放到指定的插件目录下就可以使用了,这样就有一个动态获取插件的过程,我们此例中为文章内容格式化插件,当然就不只一个格式化插件在同一时间里使用了,所以需先创建的一个集合来收集这些插件:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CoderBlog.Core
{
/// <summary>
/// 实现一个泛型插件 Provider 集合
/// </summary>
/// <typeparam name="T">集合类型.</typeparam>
public class ProviderCollector<T>
{
private List<T> list;
/// <summary>
/// 初始化新的实体
/// </summary>
public ProviderCollector()
{
list = new List<T>(3);
}
/// <summary>
/// 添加一个 Provider 到集合
/// </summary>
/// <param name="provider">需添加的 Provider.</param>
public void AddProvider(T provider)
{
lock (this)
{
list.Add(provider);
}
}
/// <summary>
/// 从集合里移除一个 Provider
/// </summary>
/// <param name="provider">需移除的 Provider</param>
public void RemoveProvider(T provider)
{
lock (this)
{
list.Remove(provider);
}
}
/// <summary>
/// 获取所有的 Providers (返回数组).
/// </summary>
public T[] AllProviders
{
get
{
lock (this)
{
return list.ToArray();
}
}
}
/// <summary>
/// 获取一个 Provider, 按类型名称搜索
/// </summary>
/// <param name="typeName">类型名称</param>
/// <returns>所找到的 Provider</returns>
public T GetProvider(string typeName)
{
lock (this)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].GetType().FullName.Equals(typeName)) return list[i];
}
return default(T);
}
}
}
}
Collectors.cs 文件
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CoderBlog.PluginFramework;
namespace CoderBlog.Core
{
/// <summary>
/// 包含了 Providers 集合的实例
/// </summary>
public static class Collectors
{
/// <summary>
/// Contains the file names of the DLLs containing each provider (provider->file).
/// </summary>
public static Dictionary<string, string> FileNames;
/// <summary>
/// 使用中的格式化插件的 Provider 集合
/// </summary>
public static ProviderCollector<IFormatterProvider> FormatterProviderCollector;
/// <summary>
/// 禁用中的格式化插件的 Provider 集合
/// </summary>
public static ProviderCollector<IFormatterProvider> DisabledFormatterProviderCollector;
/// <summary>
/// 查找一个 provider.
/// </summary>
/// <param name="typeName">Provider 类型名称</param>
/// <param name="enabled">指定此 Provider 是否开启</param>
public static IProvider FindProvider(string typeName, out bool enabled)
{
enabled = true;
IProvider prov = null;
prov = FormatterProviderCollector.GetProvider(typeName);
if (prov == null)
{
prov = DisabledFormatterProviderCollector.GetProvider(typeName);
if (prov != null) enabled = false;
}
if (prov != null) return prov;
return null;
}
/// <summary>
/// 尝试卸载一个 provider.
/// </summary>
/// <param name="typeName">provider 名称</param>
public static void TryUnload(string typeName)
{
bool enabled;
IProvider prov = FindProvider(typeName, out enabled);
FormatterProviderCollector.RemoveProvider(prov as IFormatterProvider);
DisabledFormatterProviderCollector.RemoveProvider(prov as IFormatterProvider);
}
/// <summary>
/// 尝试禁用一个 provider.
/// </summary>
/// <param name="typeName">provider 名称</param>
public static void TryDisable(string typeName)
{
IProvider prov = null;
prov = FormatterProviderCollector.GetProvider(typeName);
if (prov != null)
{
DisabledFormatterProviderCollector.AddProvider((IFormatterProvider)prov);
FormatterProviderCollector.RemoveProvider((IFormatterProvider)prov);
return;
}
}
/// <summary>
/// 尝试开启一个 provider.
/// </summary>
/// <param name="typeName">provider 名称</param>
public static void TryEnable(string typeName)
{
IProvider prov = null;
prov = DisabledFormatterProviderCollector.GetProvider(typeName);
if (prov != null)
{
FormatterProviderCollector.AddProvider((IFormatterProvider)prov);
DisabledFormatterProviderCollector.RemoveProvider((IFormatterProvider)prov);
return;
}
}
/// <summary>
/// 获取所有 providers, 包含启用和禁用的
/// </summary>
/// <returns>providers 的名称.</returns>
public static string[] GetAllProviders()
{
List<string> result = new List<string>(20);
foreach (IProvider prov in FormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
foreach (IProvider prov in DisabledFormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
return result.ToArray();
}
}
}
ProviderLoader.cs 文件
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using CoderBlog.PluginFramework;
using System.Reflection;
namespace CoderBlog.Core
{
/// <summary>
/// 加载插件的 Provider
/// </summary>
public static class ProviderLoader
{
/// <summary>
/// 列出所有插件的程序集
/// </summary>
/// <returns></returns>
public static string[] ListPluginAssemblies()
{
string pluginPath = System.Web.HttpRuntime.AppDomainAppPath + "plugins";
string[] files = Directory.GetFiles(pluginPath, "*.dll");
string[] result = new string[files.Length];
for (int i = 0; i < files.Length; i++) result[i] = Path.GetFileName(files[i]);
return result;
}
/// <summary>
/// 加载插件
/// </summary>
public static void Load()
{
string[] pluginAssemblies = ListPluginAssemblies();
//分别创建2个对象以存放开始和禁用的插件
List<IFormatterProvider> forms = new List<IFormatterProvider>();
List<IFormatterProvider> dForms = new List<IFormatterProvider>();
for (int i = 0; i < pluginAssemblies.Length; i++)
{
IFormatterProvider[] f;
LoadFrom(pluginAssemblies[i], out f);
forms.AddRange(f);
}
for (int i = 0; i < forms.Count; i++)
{
//加载并初始化插件
Initialize<IFormatterProvider>(forms[i],
Collectors.FormatterProviderCollector,
Collectors.DisabledFormatterProviderCollector);
}
}
/// <summary>
/// 初始化插件 Provider
/// </summary>
/// <typeparam name="T">当前插件接口类型</typeparam>
/// <param name="instance">插件实体</param>
/// <param name="collectorEnabled">开启中的插件集合</param>
/// <param name="collectorDisabled">禁用中的插件集合</param>
private static void Initialize<T>(T instance, ProviderCollector<T> collectorEnabled,
ProviderCollector<T> collectorDisabled) where T : class, IProvider
{
if (collectorEnabled.GetProvider(instance.GetType().FullName) != null ||
collectorDisabled.GetProvider(instance.GetType().FullName) != null)
{
//如果为空,则直接返回
return;
}
bool enabled = !IsDisabled(instance.GetType().FullName);
try
{
if (enabled)
{
//只对启用的插件进行初始化操作,并设置插件所需的配置文件,
//此方法最终由具体的插件类去实现
//以下只为演示说明,所以直接指定了配置文件路径和名称了,
//正常情况下应该再写一个 Provider 去动态获取配置文件具体路径的
instance.Init(Host.Instance, "~/plugins/config.ini");
}
}
catch
{
// 禁用插件 Provider
enabled = false;
//保存插件状态,本例为将状态保存到文本文件中
//具体实现在此也不写出来了,否则就要连带着另一个配置类 provider,太多了,呵
//SaveStatus(instance.GetType().FullName, false);
throw; // 再次抛出异常,因为不是正常执行中
}
if (enabled) collectorEnabled.AddProvider(instance);
else collectorDisabled.AddProvider(instance);
}
/// <summary>
/// 动态加载自程序集的插件
/// </summary>
/// <param name="assembly">程序集名称</param>
/// <param name="formatters">格式化插件数组,可同时加载多个</param>
public static void LoadFrom(string assembly, out IFormatterProvider[] formatters)
{
Assembly asm = null;
try
{
//asm = Assembly.LoadFile(assembly);
// 使用此方法可让DLL不会被锁住,加载完后还可以被删除
asm = Assembly.Load(LoadAssemblyFromProvider(Path.GetFileName(assembly)));
}
catch
{
formatters = new IFormatterProvider[0];
return;
}
Type[] types = null;
try
{
types = asm.GetTypes();
}
catch (ReflectionTypeLoadException)
{
formatters = new IFormatterProvider[0];
return;
}
List<IFormatterProvider> frs = new List<IFormatterProvider>();
Type[] interfaces;
for (int i = 0; i < types.Length; i++)
{
// 避免加载到抽象类,它们不能被实例化
if (types[i].IsAbstract) continue;
interfaces = types[i].GetInterfaces();
foreach (Type iface in interfaces)
{
//这里也可以添加其他插件的 proivder,加多几个 if 判断即可
if (iface == typeof(IFormatterProvider))
{
IFormatterProvider tmpf = CreateInstance<IFormatterProvider>(asm, types[i]);
if (tmpf != null)
{
frs.Add(tmpf);
Collectors.FileNames[tmpf.GetType().FullName] = assembly;
}
}
}
}
formatters = frs.ToArray();
}
/// <summary>
/// 创建一个提供者的实例
/// </summary>
/// <typeparam name="T">提供者接口类型</typeparam>
/// <param name="asm">包含指定类型的程序集</param>
/// <param name="type">要创建的实例类型</param>
/// <returns>创建的实例,或者 <c>null</c>.</returns>
private static T CreateInstance<T>(Assembly asm, Type type) where T : class, IProvider
{
T instance;
try
{
instance = asm.CreateInstance(type.ToString()) as T;
return instance;
}
catch
{
throw;
}
}
/// <summary>
/// 检测插件是否被禁用
/// </summary>
/// <param name="typeName">类型名称</param>
/// <returns></returns>
public static bool IsDisabled(string typeName)
{
//具体逻辑请自行实现啦,可从配置类里获取插件状态
return true;
}
/// <summary>
/// 从程序集加载提供者
/// </summary>
/// <param name="assemblyName">程序集文件名</param>
/// <returns></returns>
private static byte[] LoadAssemblyFromProvider(string filename)
{
if (filename == null) throw new ArgumentNullException("filename");
if (filename.Length == 0) throw new ArgumentException("filename");
if (!File.Exists(GetPluginFullPath(filename))) return null;
//此函数代码其实应该放到配置类实现,然后使用lock以保证线程安全,
//在此只为了演示,所以先放到此片了
//lock (this)
//{
try
{
return File.ReadAllBytes(GetPluginFullPath(filename));
}
catch (IOException)
{
return null;
}
//}
}
/// <summary>
/// 根据文件名获取完整的插件路径
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
private static string GetPluginFullPath(string filename)
{
//具体请自行实现,同理,此函数也应该放到配置类
return "";
}
}
}
OK,到此整个插件框架已基本完成了。下一篇将说说如何使用此框架来创建一个简单的插件