代码改变世界

.Net插件框架的实现及分析(二)

2011-09-27 18:03  w i n s o n  阅读(1553)  评论(3编辑  收藏  举报

 

 

呵,很久之前发表了 .Net插件框架的实现及分析(一) ,只是一直没在此接上,只是在我自己的代码部落里更新了,现在也加上吧:

 

话接上回, 让我们来继续分析下这个插件框架如何实现吧。既然是插件,就必须得动态加载,只需将编译好的插件DLL文件放到指定的插件目录下就可以使用了,这样就有一个动态获取插件的过程,我们此例中为文章内容格式化插件,当然就不只一个格式化插件在同一时间里使用了,所以需先创建的一个集合来收集这些插件:

 

ProviderCollector.cs 文件:
using System;
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);
            }
        }
    }


然后还要创建一个管理插件 Provider 集合实例的类:

Collectors.cs 文件

using System; 

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<stringstring> 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 != nullreturn 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();
        }
    }
}

 

 

 接下来就可以开始正式编写加载插件 Provider 的代码了,创建一个 ProviderLoader 类:

 

 ProviderLoader.cs 文件

using System;
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 == nullthrow new ArgumentNullException("filename");
            if (filename.Length == 0throw 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,到此整个插件框架已基本完成了。下一篇将说说如何使用此框架来创建一个简单的插件