4.3插件平台动态装载DLL并执行
PlugFramework是整个PlugPlatform平台的核心内容。主要实现检查和装载DLL文件,动态创建实例化对象,验证并执行外部方法。
图8 PlugPlatform动态装载的全过程
PlugPlatform依据配置文件可以了解装载的外部DLL文件和要求执行的类或接口方法。整个装载和的调用过程如图8说明。
PlugFramework包含有类和接口,这些类和接口之间有继承、实现、关联关系。其类图如图9所示。

图9 PlugPlatform类图
PlugFramework各个类的具体详细描述如下:
1.Plus.PlusConfig插件平台的配置信息类,可创建工厂类 类2.Plus.PlugFactory插件平台的工厂类,可以创建方法实现类 类3.Plus.IAction方法实现的接口 接口4.Plus.Framework.PlugAction方法实现的抽象祖先类 类5.Plus.Framework.ClassAction类对象的实现类 类6.Plus.Framework.InterfaceAction接口对象的实现类 类7.Plus.Framework.AssemblyManagerAssembly的管理类,生成Assembly 类8.Plus.Framework.TypeManagerType的管理类,可实现对Type、Object的生成和检查。包括动态方法的调用。 类动态调用外部方法的核心代码如下所示:public object InvokeClassMethod(String className, object[] objectArgs, String methodName, object[] methodArgs){
Object[] newArgs = new Object[methodArgs.Length];
Object thisObject = new Object();
Type type = CreateType(className);
MethodInfo[] methods = type.GetMethods();
Object instance = CreateObject(type, objectArgs);
foreach(MethodInfo m in methods)
{
if(m.Name == methodName)
{
newArgs = ConvertArgsType(m, methodArgs);
try
{
if(!m.IsStatic)
{
thisObject = m.Invoke(instance, newArgs);//非静态方法,使用类实例调用
}
else
{
thisObject = m.Invoke(null, newArgs);
}
return thisObject;
}
catch(Exception e)
{
throw new PlusException("不能动态调用方法,原因:" + e.Message, e);
}
}
}
return thisObject;
}图10表示PlugPlatform实现全过程。
下面分别对每个步骤做一个详细描述:
① 外部应用请求动态调用。
② PlusConfig类根据配置文件创建PlugFactory对象。
③ PlugFactory对象创建一个Action对象。
④ Action对象获得MethodObject对象组,逆向产生DllFileObject对象。
⑤ 根据DllFileObject对象中的DLL文件信息,Action对象通过AssemblyManager类获得Assembly对象。
⑥ Action对象使用Assembly对象创建TypeManager对象。
⑦ Action对象传递MethodObject对象给TypeManager对象。
⑧ TypeManager对象可依据MethodObject对象获得ClassObject对象。并使用ClassObject对象信息动态创建一个外部ClassObject对象的实例instance。
⑨ TypeManager对象使用instance和MethodObject对象信息调用instance的动态方法。instance把执行结果返回给Action对象。
⑩ Action对象把执行结果返回给外部应用。
图10 PlugPlatform实现的顺序图
![]()
其中Plus工厂模式采用了Factory模式。对于Assembly的生成采用了Singleton模式。
4.4 异常处理
由于应用程序中有很多不可预料的问题,本平台在很多地方都有可能出现人为错误,如找不到配置文件;配置文件的格式不对,不能解析配置文件;类或接口名称写错了,不能实例化类;方法名称写错了,不能执行方法等等。增加异常处理主要是增强其容错性,在这里就不做更多的说明。
5.应用实例
本例子程序主要有三个方面组成:XML配置文件、外部DLL文件和PlusPlatform调用代码。
5.1 XML配置文件
采用的XML配置文件有两个,一个是针对类对象的XML配置文件,一个是针对接口对象的配置文件。
其中类对象的XML配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<PlugPlatformResource>
<DllFile name="UserLibrary.dll" filepath ="/" objectType ="class">
<classobject name="UserLibrary.UserTest1" >
<Methodobject>testAction01</Methodobject>
<Methodobject>testAction02</Methodobject>
<Methodobject>testAction03</Methodobject>
</classobject>
<classobject name="UserLibrary.UserTest2" >
<Methodobject>testAction01</Methodobject>
</classobject>
</DllFile>
</PlugPlatformResource>接口对象的配置文件与类对象配置文件基本相同,只不过配置信息中由类换成了接口:
<?xml version="1.0" encoding="utf-8" ?>
<PlugPlatformResource>
<DllFile name="UserLibrary.dll" filepath ="/" objectType ="interface">
<interfaceobject name="InterfaceTest1" implement="UserLibrary.UserTest2" >
<Methodobject>testAction01</Methodobject>
</interfaceobject>
</DllFile></PlugPlatformResource>
5.2 DLL文件内容
其编译的DLL文件为UserLibrary.dll,该dll文件包括两个类和一个接口,其内部代码为:
public class UserTest1
{
public DynamicArrayObject testAction01(DynamicArrayObject outObject)
{
DynamicArrayObject thisObject = new DynamicArrayObject();
//分解DynamicArrayObject
DataValueObject do1 = null;
string ls = null;
for (int i = 0; i < outObject.Length; i++)
{
do1 = outObject.getObject(i);
ls += (String)do1.getDataValue();
}
DataValueObject do2 = new DataValueObject();
do2.setDataType(do1.getDataType()).setDataValue(ls);
//组装DynamicArrayObject,返回DynamicArrayObject
thisObject.addObject(do2);
return thisObject;
}
public DynamicArrayObject testAction02(DynamicArrayObject outObject)
{
return outObject;
}
}
public class UserTest2 : InterfaceTest1
{
public DynamicArrayObject testAction01(DynamicArrayObject outObject)
{
DynamicArrayObject thisObject = new DynamicArrayObject();
//分解DynamicArrayObject
DataValueObject do1 = null;
string ls = null;
for (int i = 0; i < outObject.Length; i++)
{
do1 = outObject.getObject(i);
ls += (String)do1.getDataValue();
}
DataValueObject do2 = new DataValueObject();
do2.setDataType(do1.getDataType()).setDataValue(ls);
//组装DynamicArrayObject,返回DynamicArrayObject
thisObject.addObject(do2);
return thisObject;
}
public DynamicArrayObject testAction02(DynamicArrayObject outObject)
{
return outObject;
}
}
public interface InterfaceTest1
{
DynamicArrayObject testAction01(DynamicArrayObject outObject);}
5.3 调用插件平台代码
调用代码也分为两类,一类是针对类对象处理的,代码如下:
另一类是针对接口对象处理,代码如下:DynamicArrayObject thisObject = new DynamicArrayObject();DataValueObject do1 = new DataValueObject();
DataValueObject do2 = new DataValueObject();
do1.setDataType("string").setDataValue("类测试:第一个对象值.");
do2.setDataType("string").setDataValue("第二个对象值.");
thisObject.addObject(do1).addObject(do2);
string dllFile = Application.StartupPath + "\DllClassFile.xml";
PlugFactory factory = PlusConfig.BuildFactory(dllFile);
IAction action = factory.CreatAction();
DynamicArrayObject outputObject = action.Execute("UserLibrary.UserTest1.testAction01", thisObject);DynamicArrayObject thisObject = new DynamicArrayObject();DataValueObject do1 = new DataValueObject();
DataValueObject do2 = new DataValueObject();
do1.setDataType("string").setDataValue("接口测试:第一个对象值.");
do2.setDataType("string").setDataValue("第二个对象值.");
thisObject.addObject(do1).addObject(do2);
string dllFile = Application.StartupPath+"\DllInterfaceFile.xml";
PlugFactory factory = PlusConfig.BuildFactory(dllFile);
IAction action = factory.CreatAction();
DynamicArrayObject outputObject = action.Execute("InterfaceTest1.testAction01", thisObject);可以对返回的DynamicArrayObject做分解查看,满足设计要求。
6.结束语
反射机制结合动态数组很好地解决了应用软件的后期维护和升级。对于应用软件的变化,可不改动任何现有的程序,只要修改XML配置文件的相应对象名称和加载新的对象即可,程序不需要任何的重编、重启和硬性改动,并保证了原应用系统的可复用性从而实现降低耦合度,实现复用的目标。
本模型在层与层之间借助类调用和接口实现,利用反射机制把调用者与实现者在编译期分离。运行期通过读配置文件动态加载实现类,并通过接口将实现者强制转型,使其为调用者所用,完成调用者和实现者的解耦。但是,这个功能并不是完全完善,对于插件平台也有很多的改进性,如果能对类和接口配置文件更加丰富,把插件平台升级为一个框架容器,该容器能把对象之间的依赖关系先行剥离,然后在适当时候由容器负责产生具体的实例再注射到调用者中,即控制权由应用代码中转到了外部容器,控制权发生了转移。即所谓的控制反转模式,这种模式在java中已经有比较成熟的框架,如Spring等。相信凭借Microsoft.Net庞大的技术框架平台,在C#上也会有这样的控制反转框架出现。
浙公网安备 33010602011771号