zoukankan      html  css  js  c++  java
  • .Net插件框架的实现及分析(二)

    呵,很久之前发表了 .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,到此整个插件框架已基本完成了。下一篇将说说如何使用此框架来创建一个简单的插件  

  • 相关阅读:
    【javascript基础】之【理解JavaScript函数(函数和对象的区别和联系)】
    time
    IE6双倍边距
    【javascript基础】之【delete 运算符】
    【javascript基础】之【解剖JavaScript中的null和undefined】
    buildessential centos
    centOS restart xinetd
    g77 CentOS
    yum search package
    xos_thread
  • 原文地址:https://www.cnblogs.com/winsonet/p/2193417.html
Copyright © 2011-2022 走看看