我设想的可扩展结构(插件) (三)

 

背景

第一部分见:我设想的可扩展结构(插件) ()

第二部份见:我设想的可扩展结构(插件) ()

本文将对此插件结构的设计和关键代码作一个介绍。

 

设计

使用Rose画的设计图(去掉了一些辅助类)
The Package Diagram :


The Plug-in Interface Class Diagram:


The Plug-in Manager Class Diagram:

 

对关键类的说明

1.IPlugin──任何插件需要实现的接口。放在单独的Project中,以便于其它Project引用。Execute方法执行动作,Dispose方法则终止执行且释放资源。

2.IAppContext──插件以此来了解其运行环境。PluginManager将此类型的对象传递给IPluginExecute方法。

3.PluginAssemblyAttribute──描述插件程序集的属性,比如此插件所从属的模块,插件类的名字。

4.PluginDependencyAttribute──描述插件类的属性,指明所依赖的其它插件。

5.PluginManager──插件管理类,继承MarshalByRefObject。负责加载,执行,停止和删除插件。为每个模块的插件创建一个AppDomain,以按照模块来隔离插件。

6.PluginDescriptor──插件的包装类,负责将按照插件之间的依赖关系进行排序,以便于PluginManager按照正确的顺序加载插件。

7.PluginSecurityManager──插件安全管理类。负责检查插件的合法性,设置插件的执行权限。

8.PluginProvider──插件提供者,继承MarshalByRefObject类。负责提供需要加载的插件的Assembly的列表。具体实现中可以读取配置找到插件,也可以自动发现插件。原本我设计了一个工厂,但由于我只是采用自动查找插件的方式,所以为了简化设计,就把这个工厂去掉了。PluginProvider创建一个临时的AppDomain来加载插件目录下的程序集,并读取其属性以检验此程序集中是否包含插件;返回包含插件的程序集的列表,最后卸载此临时AppDomain

9.DirectoryMonitor──监控部署插件的目录,一旦有新增加的插件,或者有新版本插件被部署后,就能立刻通知PluginManager进行处理。具体实现中通常使用代理了处理了。此类继承FileSystemWatcher类,使用中需要注意一些问题──同一个事件可能重复触发;一个文件拷贝动作会触发多个事件。

10.        SoftwareDelivery──这个子系统负责进行系统部署,远程将插件部署到指定的各个客户端的某目录下。

11.        还有一些辅助类,比如读取Attribute的类,还有一些包装类,在图中没有画出。

  

代码

这里只对关键的代码作一个说明。

1.                文件监控:

   public delegate void AddPluginDelegate(string assemblyString);
    
public delegate void ChangePluginDelegate(string assemblyString);
    
public class DirectoryMonitor : FileSystemWatcher
    
{
        
private AddPluginDelegate addPluginDelegate;
        
private ChangePluginDelegate changePluginDelegate;
        
public DirectoryMonitor(string monitorDirectory, AddPluginDelegate addPluginDelegate, ChangePluginDelegate changePluginDelegate)
        
{
            
this.addPluginDelegate = addPluginDelegate;
            
this.changePluginDelegate = changePluginDelegate;

            
this.Path=monitorDirectory;
            
this.Filter="*.dll";
            
this.IncludeSubdirectories=true;
            
this.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Attributes;
            
this.Changed += new FileSystemEventHandler(fsWatcher_Changed);
            
this.Created += new FileSystemEventHandler(fsWatcher_Created);
        }

//move the files to another directory  
// in fsWatcher_Changed and fsWatcher_Created method 
    }

 

2.                寻找插件:

 

public sealed class PluginProvider : MarshalByRefObject
    
{
        
public PluginProvider()
        
{
        }

        
public PluginProvider(string shadowCopyDir)
        
{
            
this.shadowCopyDir=shadowCopyDir;
        }

        
private string shadowCopyDir;
        
public PluginAssemblyDescriptor[] GetPluginAssemblies()
        
{
            
string[] assemblies=null;
            PluginAssemblyDescriptor[] ret
=null;
            AppDomainSetup setupInfo
=new AppDomainSetup();
            setupInfo.ApplicationBase
=AppDomain.CurrentDomain.BaseDirectory+"\\"+shadowCopyDir;
            AppDomain tempDomain
=AppDomain.CreateDomain("",null,setupInfo);
            PluginProvider afpp
=null;
            
try
            
{
                afpp
=(PluginProvider)tempDomain.CreateInstanceFromAndUnwrap(
                    
"XXX.dll","XXX.PluginProvider");
                DirectoryInfo pluginShadowDir
=new DirectoryInfo(tempDomain.BaseDirectory);
                FileInfo[] assemblyFiles
=pluginShadowDir.GetFiles("*.dll");
                
if(assemblyFiles==null || assemblyFiles.Length==0)
                    
return null;

                assemblies
=new string[assemblyFiles.Length];
                
int i=0;
                
foreach(FileInfo assemblyFile in assemblyFiles)
                
{
                    assemblies[i
++]=assemblyFile.Name.Replace(assemblyFile.Extension,"");
                }

                PluginAssemblyDescriptor[] temp
=new PluginAssemblyDescriptor[assemblies.Length];
                
int count=0;
                
for(i=0;i<assemblies.Length;i++)
                
{
                    PluginAssemblyDescriptor pad
=(PluginAssemblyDescriptor)afpp.LoadAssembly(assemblies[i]);
                    
if(pad!=null)
                        temp[count
++]=pad;
                }

                
if(count!=0)
                
{
                    ret
=new PluginAssemblyDescriptor[count];
                    
for(i=0;i<count;i++)
                    
{
                        ret[i]
=new PluginAssemblyDescriptor();
                        ret[i].AssemblyString
=temp[i].GetAssemblyString();
                        ret[i].PluginAssemblyAttribute
=temp[i].GetPluginAssemblyAttribute();
                    }

                }

            }

            
catch(Exception e)
            
{
                
//do something
            }

            
finally
            
{
                AppDomain.Unload(tempDomain);
            }

            
return ret;
        }


        
public PluginAssemblyDescriptor GetPluginAssembly(string assemblyString)
        
{
            
//get one a special assembly
        }

        
private PluginAssemblyDescriptor LoadAssembly(string assemblyString)
        
{
            Assembly assembly
=AppDomain.CurrentDomain.Load(assemblyString);
            AssemblyAttributeReader aar
=new AssemblyAttributeReader(assembly);
            PluginAssemblyAttribute paa
=aar.GetPluginAssemblyAttribute();
            
if(paa==null)
                
return null;
            PluginAssemblyDescriptor pad
=new PluginAssemblyDescriptor(assemblyString,paa);
            
return pad;
        }

    }

3.                加载并且执行插件:

和上面寻找插件的过程类似,需要为每个模块创建应用程序域。PluginManager在各个应用程序域中创建PluginManager的实例,加载插件,创建插件的实例,并且调用Execute方法。说白了,就是PluginManager在新的应用程序域中再创建一个自己,这些PluginManager对象管理本域中的插件。代码太长,在这里就不贴出了。

   

后记

这个插件结构已经完成了,可能和大家常见的UI中的插件有些不同。我的这个插件结构只是适合于我在我设想的可扩展结构(插件) ()提到的场合,而不是一个通用的插件结构库。希望大家积极提出批评意见!


 今天是个特殊的日子(7月7日),发此文亦当纪念。

posted @ 2005-07-07 10:18  风满袖  阅读(4074)  评论(16编辑  收藏  举报