这篇文章主要介绍整个框架中都在使用的接口,虽然设计目的是非侵入式的,但是多少还是有些接口要继承的。

先说PluginInfo,这个类基本上只是用来记录插件信息的,需要说明的也就是ConfigFilePath,这个属性对应着该插件对应的配置文件,配置文件是简单的键值对,在启动时会将该配置读出,并复制给插件的Config属性。
IPlugin接口,所有插件都要继承该接口,在初始化加载时,系统会扫描该类型,并初始化它,然后调用Initialize方法,其他方法看名字应该也差不多可以猜出含义来了。
关于其中的Message的方法将会在后面的Message中说明。

IApp接口是Appex的接口,该接口主要是Service容器,其中的Config为AppSettings的内容,LoginWindow与MainWindow是登录界面与主界面,如果系统中查找到了UIPlugin并在其中找到了登录与主界面,则会在启动时,展示登录界面(为了节省登录时间,在登录界面展示的同时,系统依然在加载剩余的DLL,一般在用户输入完用户名与密码后,差不多能够加载完成)。

* 根据上图可以看出,所有的UI控件都继承自IControl,这里所谓Control其大小大与WinForm的UserControl相似,并不是指一个单独的控件。
如果用户按下的取消键后,系统会放弃此次启动,在加载完成所有控件后,直接调用CloseSystem方法。
如果用户按下确认按钮,则会调用 权限验证模块 进行权限验证。
验证通过并且系统全部加载完成后显示主界面。
IApp中的GotoLogin与GotoMainForm主要作用就是显示相关的界面。
如果IApp中的ShowUi为False的话,不会显示任何界面,一般用于服务的启动。

上图是AppEx(IApp的实现)的使用到的相关的类,其中MethodInfoHelper的方法只有一个,就是获取当前调用方法的名称:
var st = new StackTrace(true);
// 上移两帧
StackFrame frame = st.GetFrame(frameCount);
// 获取调用函数信息
methodInfo = frame.GetMethod();
ServiceProvider类将根据PluginInfo获取IPlugin实例,同时也用于加载Assembly,具体的方法参见反射就可以了。
/// 创建插件
/// </summary>
/// <typeparam name="T">插件类型</typeparam>
/// <param name="pluginInfo">插件信息</param>
/// <param name="classType">当返回类型为接口时,使用该类型实例化</param>
/// <returns>插件</returns>
public static T CreatePlugin<T>(PluginInfo pluginInfo, String classType = null)
{
// 加载引用Dll
foreach (AssemblyName referenceDll in pluginInfo.ReferenceDlls)
{
// LoadAssembly(referenceDll.FullName);
}
// 加载项目Dll
Assembly assembly = LoadAssembly(pluginInfo.DllPath);
if (assembly == null) throw new FileLoadException();
Type type = typeof(T);
string typeName = type.IsInterface ? String.IsNullOrEmpty(classType) ? pluginInfo.TypeName : classType : type.FullName;
if (String.IsNullOrEmpty(typeName)) throw new TypeLoadException("Can not fund FullName at this type.");
Type typeFromAss = assembly.GetType(typeName);
if (typeFromAss == null) throw new TypeLoadException("Can not fund type at this assembly.");
return (T)Activator.CreateInstance(typeFromAss);
}
ServiceInterceptor类是使用动态代理是需要提供的拦截器,关于动态代理会在下一篇文章中说明。
DynamicProxy是获取动态代理的一个包装类,同样也会在下一篇文章中一起说明。
/// 加载DLL
/// </summary>
/// <param name="dllPath">Dll路径</param>
/// <returns>加载结果</returns>
public static Assembly LoadAssembly(string dllPath)
{
if (String.IsNullOrEmpty(dllPath)) return null;
try
{
string dllName = Path.GetFileName(dllPath);
if (LoadedFiles.Contains(dllName))
return
AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(
a => Path.GetFileName(a.Location) == dllName);
LoadedFiles.Add(dllName);
return Assembly.LoadFrom(dllPath);
}
catch (Exception)
{
return null;
}
}
在Appex的RegService中如果注册类型为IService类型,并且注册时,没有提供类型的情况下,会查找该类型的接口类型,例如 IServiceDemo:IService,当调用RegService(serviceDemo)时(serviceDemo为实现了IServiceDemo接口的类型的实例),注册的类型是IServiceDemo。
在GetService时,如果为客户端程序则会返回动态代理类型,如果是服务端,则会直接返回注册的实例。
AppEx总体来说还是比较简单的,其中用到了反射和动态代理,相信对于有过经验的设计人员对这两点应该也都很熟悉,动态代理是AOP的基础,反射更应该是家常便饭了。
因为个人能力有限,文章中应该有不少的问题,还请大家多多指正。
浙公网安备 33010602011771号