JCreator

博客园 首页 联系 订阅 管理

1 引言

   在信息系统开发,用户业务功能变化预先不可知,故要提高系统后期的业务扩展。一般情况下用户需求发生变化,要重新编写代码,编译,生产部署包,然后再更新用户程序。这样的过程比较繁琐。

   本文讨论生成后的应用系统与外部编译的业务库实现动态绑定,应用程序在运行过程中动态绑定要实现的外部业务,当业务发生变化,也只是替换这些外部的动态库,不用重新对应用程序进行修改和编译,实现了耦合绑定。同时,业务实例对象可以在程序运行时实现实例化,达到了封装效果。并且降低了调用代码和具体实现类代码的耦合,增强灵活性和可复用性,增加了软件的可维护性。

   C#提供的反射机制,再结合自适应数据参数的传递,通过这个技术,我们可以将应用框架中的扩展点以插件式程序集的方式来动态加载、构建,从而实现可动态扩展的应用程序。

2 反射机制的基础知识

   反射是.NET中重要机制,通过反射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。.NET的应用程序结构分为应用程序域、程序集、模块、类型和成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。

   反射通常具有以下用途:① 使用Assembly定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例;② 使用Module了解如下的类似信息,如模块的程序集以及模块中的类等:③ 使用CoustructorInfo了解如下的类似信息,如构造函数的名称、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtua1)等;④ 使用MethodInfo来了解如下的类似信息,如方法的名称、返回类型、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtua1)等;⑤ 使用FieldInfo来了解如下的类似信息,如字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值;⑥ 使用EventInfo来了解如下的类似信息,如事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,并添加或移除事件处理程序:⑦ 使用PropertyInfo来了解如下的类似信息,如属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,并获取或设置属性值;⑧ 使用ParameterInfo来了解如下的类似信息,如参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。

3.总体设计思路

   插件是一种遵循一定规范的应用程序接口编写出来的程序模块。当应用程序已经部署,但业务发生了变化,这样可以通过读取插件配置信息,载入新的应用构件,实现变化的业务。

  对于应用系统的框架而言,扩展点是框架中预先定义的一些“点”。 在框架复用中应用构件的组装需要基于扩展点进行。构造性和演化性是软件的两个本质特征,作为一类重要的可复用软件制品。而基于扩展点可以组装不同的应用构件以适应领域的变化性。则体现了框架对于软件演化特征的支持[3]。

   本文涉及到几个概念,插件配置定义,接口定义,方法定义和调用参数定义和返回参数定义。在本插件平台中,配置文件描述插件配置定义,接口定义,方法定义。对于调用参数定义和返回参数定义则采用通用对象和动态对象组[4]来实现传入和返回参数。

   插件平台的实现过程如图1所示。当平台运行初始化时,通过读取XML配置信息,装载DLL,通过C#的反射机制分析DLL里的全部实现类和方法。外部构件可以在平台容器中被实例化,并执行插件点的方法。实现的算法不再是编码硬绑定。

                                                                       

                                                                                                     图1 PlugPlatform整个过程图

这样,应用程序在运行过程中动态绑定要实现的外部业务,当业务发生变化,也只是替换这些外部的动态库,不用重新对应用程序进行修改和编译,实现了耦合绑定。

4.具体实现

  PlugPlatform平台包括四个部分:① 配置文件的获取和解析;② 通用参数和动态参数组处理;③ 插件平台装载DLL并执行外部方法;④ 异常处理。

  4.1 配置文件的获取和解析

      配置文件以XML Schema为基础,分为两种类型,一种是类配置文件,主要描述关于外部DLL中的类以及方法的内容。第二种配置文件是接口配置文件,主要描述关于外部DLL中的接口以及方法的内容。

  类配置文件的XSD如图2所示。 

                                                       

                                                                                      图2 类配置文件的schema图 

    按照此XSD形成的配置XML如图3所示。 

                                                              

                                                                                      图3 类配置文件的XML树

     同理可以接口配置文件的XSD内容(如图4)和XML树(如图5)。

                                                     

                                                                                   图4 接口配置文件的schema图 

                                                                    

                                                                                       图5 接口配置文件的XML树 

      在实现将XML树状结构的数据转换为二维MethodObject哈希表,MethodObject哈希表是一key/value的键值对,其中key通常可用来快速查找,value用于存储对应于key的值。MethodObject类数据结构如下: 

public class MethodObject 
{
   private ClassObject classobject = null;
   private InterfaceObject interfaceobject = null;
   private DllFileObject dllfileobject = null;
   private string name = string.Empty;
   private string simplename = string.Empty;
   private string implementname = string.Empty;
 
   public string MethodName 
    {
       get { return this.name; } 
       set { this.name = value; }
    }
   public string SimpleName 
     { 
        get { return this.simplename; }
        set { this.simplename = value; } 
     }
   public string ImplementName 
    {
        get { return this.implementname; }
        set { this.implementname = value; }
    }
   public ClassObject ClassObject 
     {
        get { return this.classobject; }
        set { this.classobject = value; }
     }
  public InterfaceObject InterfaceObject 
    {
        get {return this.interfaceobject; }
        set {this.interfaceobject = value; }
    }
  public DllFileObject DLLFileObject
    { 
        get { return this.dllfileobject; } 
        set { this.dllfileobject = value; }
    }
}

 

     MethodObject哈希表中key为保证内容的唯一性而采用方法的全名。可以,这样形成的主键可以进行快速查找。value用来存储MethodObject对象。同时MethodObject对象与ClassObject对象,InterfaceObject对象和DLLFile对象都是多对一的关系,所以,一旦获得了      MethodObject对象,就可以反推出ClassObject对象,InterfaceObject对象和DLLFile对象。

                                                 

                                                                         图6  配置XML转MethodObject哈希表 

    根据XML Schema可以构建XML文档树,对XML文档树的节点进行分层遍历,然后采用递归算法,依次把XML文档树上最边上的叶子转化为方法对象哈希表,实现方式如图6所示。

  4.2通用参数和动态参数组处理

   对于外部的方法,要传入参数,同时也获得结果。这些都要用一些通用的数据结构来描述。参数必须可以支持任何类型,是一个通用性的参数。通过创建一个数据的通用类,可以保证支持任何数据类型。

 

   由于传入和传出的参数有多有少,这就要求参数组能实现随意的自动增长和减少。通过设计一个动态自增长的参数数组就可以实现。设计模型如图7表示。

                                                     

                                                                  图7 DataValueObject类和DynamicArrayObject类的设计模型 

   动态参数组的增加数组对象方法如下: 

public DynamicArrayObject addObject(DataValueObject obj) 

{
  if (Objects == null)   
    {
    Objects = new DataValueObject[1];
    Objects[0] = obj;
    return this;   
    }
  else 
    {
    DataValueObject[] objectList = new DataValueObject[Length + 1];
    for (int i = 0; i < Length; i++) 
        { 
             objectList[i] = Objects[i]; 
        }
     objectList[Length] = obj;
     Objects = objectList;
     return this;
     } 
}

   动态参数组的删除数组对象方法如下:

public DynamicArrayObject deleteObject(int idx)
{
     if (Objects == null)
     {   
        return this;
     }
     else 
     {
        if (idx >= 0 && idx < Length && Length > 1
        {
            DataValueObject[] objectList = new DataValueObject[Length - 1];
            for (int i = 0; i < idx; i++) 
            {
                objectList[i] = Objects[i];
            }
            for(int i = idx + 1; i < Length; i++) 
            {
                objectList[i - 1] = Objects[i]; 
            }
            Objects = objectList;
            return this
        }
        else 
       {
           if (idx == 0 && Length <= 1
           {
               Objects = null;
               return this
           }
           else 
           {
              return this
           }
       } 
    }
}

 

posted on 2011-10-21 23:22  JCreator  阅读(720)  评论(0)    收藏  举报