前面我们已经实现了IPlugin接口的插件自动加载。 但是程序使用中,可以不仅仅只有一个接口,而可能是多个接口并存,不能每个接口都手动实现代码类!怎么办?
解决方案是:泛型+EMIT。 使用泛型把自动加载和插件接口分开,使用EMIT自动生成代理类。
如4项目组成:自动加载+接口定义+接口实现+测试程序,如下图:
我们使用的方式上,仍没有什么改变:
private void FormMain_Load(object sender, EventArgs e)
{
//这里加上了泛型参数 IPlugin表示要求自动生成这个接口的代理类
var inst = PluginManager<IPlugin>.Instance;
inst.PluginChanged += OnPluginChanged;
}
void OnPluginChanged(object sender, PluginManagerEventArgs<IPlugin> e)
{
if (e.ChangeType == PluginChangeType.Created)
{
// 这里初始化插件,提供服务
e.PluginInstance.Run(DateTime.Now.ToString());
}
}
当然,最主要的项目就是"PluginLoader"
思路仍是使用RemotePluginLoader加载插件,但此类不再实现IPlugin,而是使用EMIT生成一个插件代理类,该代理类从MarshalByRefObject 继承以支持AppDomain间的通信。
public class RemotePluginLoader<T> : RemoteTypeLoader where T :class
{
protected Type pluginType;
public T Plugin { get; private set; }
protected override System.Reflection.Assembly LoadAssembly(string assemblyPath)
{
var ass = base.LoadAssembly(assemblyPath);
//查找插件
Type typePlugin = typeof(T);
this.pluginType = ass.GetTypes().Where(t => typePlugin.IsAssignableFrom(t)).FirstOrDefault();
//生成代理插件类,并从MarshalByRefObject继承以支持AppDomain通信
this.Plugin = InterfaceProxyBuilder<T>.CreateProxy((T)System.Activator.CreateInstance(pluginType), typeof(MarshalByRefObject));
return ass;
}
/// <summary>
///直接在远程Domain中,执行方法。要求支持序列化
/// </summary>
/// <param name="action">执行的匿名方法</param>
public void Execute(Action action)
{
action();
}
/// <summary>
///直接在远程Domain中,执行函数。要求支持序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fun"></param>
/// <returns></returns>
public T Execute<T>(Func<T> fun)
{
return fun();
}
在调用方,基于插件更新时,我们要求统一更新引用,也有生成一个代理类。 所不同的是,该代理类每次都要访问函数,从而每次都可以检查插件的情况并进行控制:
public class PluginCallerProxy<T> where T : class
{
private T _targetPlugin;
private T _proxyPlugin;
private PluginLoader<T> _pluginLoader;
private System.Threading.ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
/// <summary>
///构造一个代理类,保存在 ProxyPlugin 属性中
/// </summary>
/// <param name="loader"></param>
public PluginCallerProxy(PluginLoader<T> loader)
{
this.PluginLoader = loader;
this.TargetPlugin = loader.Plugin;
//从函数生成代理类
this._proxyPlugin = InterfaceProxyBuilder<T>.CreateFuncProxy(() => this.TargetPlugin, typeof(object));
}
/// <summary>
///供使用的代理
/// </summary>
public T ProxyPlugin
{
get { return _proxyPlugin; }
}
#region实现
internal PluginLoader<T> PluginLoader
{
get
{
return _pluginLoader;
}
set
{
_pluginLoader = value;
this.TargetPlugin = _pluginLoader == null ? null : _pluginLoader.Plugin;
}
}
internal T TargetPlugin
{
get
{
locker.EnterReadLock();
try
{
if (_targetPlugin == null)
{
throw new PluginException("插件已经卸载");
}
return _targetPlugin;
}
finally
{
locker.ExitReadLock();
}
}
set
{
locker.EnterWriteLock();
try
{
_targetPlugin = value;
}
finally
{
locker.ExitWriteLock();
}
}
}
#endregion
}
最终得到新的插件管理类,代码如下:
public class PluginManager<T> where T : class
{
#region实现
#region字段
private static PluginManager<T> _instance = null;
private FileSystemWatcher pluginWatcher;
private Timer timerProcess = null;
private ConcurrentDictionary<string, FileSystemEventArgs> changedPlugins = new ConcurrentDictionary<string, FileSystemEventArgs>();
private ConcurrentDictionary<string, PluginCallerProxy<T>> plugins = new ConcurrentDictionary<string, PluginCallerProxy<T>>();
#endregion
static PluginManager()
{
if (_instance == null)
{
lock (typeof(PluginManager<T>))
{
if (_instance == null)
_instance = new PluginManager<T>();
}
}
}
private PluginManager()
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins");
//监控
this.pluginWatcher = new FileSystemWatcher(path, "*.dll");
this.pluginWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;
this.pluginWatcher.Changed += OnPluginChanged;
this.pluginWatcher.Created += OnPluginChanged;
this.pluginWatcher.Deleted += OnPluginChanged;
this.pluginWatcher.Renamed += OnPluginRenamed;
pluginWatcher.EnableRaisingEvents = true;
timerProcess = new Timer(new TimerCallback(e => this.ProcessChangedPlugin()));
//加载所有
Directory.GetFiles(path, "*.dll").ToList().ForEach(file =>
{
FileInfo fi = new FileInfo(file);
this.changedPlugins[fi.Name] = new FileSystemEventArgs(WatcherChangeTypes.Created, fi.DirectoryName, fi.Name);
});
this.timerProcess.Change(10, -1);
}
void OnPluginRenamed(object sender, RenamedEventArgs e)
{
//重命名,理解为去掉原来的,增加新命名的
FileInfo old = new FileInfo(e.OldFullPath);
this.changedPlugins[old.Name] = new FileSystemEventArgs(WatcherChangeTypes.Deleted, old.DirectoryName, old.Name);
FileInfo n = new FileInfo(e.FullPath);
this.changedPlugins[n.Name] = new FileSystemEventArgs(WatcherChangeTypes.Created, n.DirectoryName, n.Name);
//1秒后再处理
this.timerProcess.Change(1000, -1);
}
void OnPluginChanged(object sender, FileSystemEventArgs e)
{
Debug.Print(e.Name + e.ChangeType);
//记录变更
this.changedPlugins[e.Name] = e;
//1秒后再处理
this.timerProcess.Change(1000, -1);
}
protected void ProcessChangedPlugin()
{
#region处理插件变化
foreach (var kv in this.changedPlugins)
{
FileSystemEventArgs e;
if (changedPlugins.TryRemove(kv.Key, out e))
{
Debug.Print(e.Name + "=>" + e.ChangeType);
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
{
//加载
var loader = new PluginLoader<T>(e.Name);
var proxy = new PluginCallerProxy<T>(loader);
plugins.TryAdd(e.Name, proxy);
OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Created, proxy.ProxyPlugin));
}
break;
case WatcherChangeTypes.Deleted:
{
PluginCallerProxy<T> proxy;
if (plugins.TryRemove(e.Name, out proxy))
{
OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Deleted, proxy.ProxyPlugin));
var loader = proxy.PluginLoader;
proxy.PluginLoader = null;
loader.Unload();
}
}
break;
case WatcherChangeTypes.Changed:
{
PluginCallerProxy<T> proxy;
if (plugins.TryGetValue(e.Name, out proxy))
{
OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Deleted, proxy.ProxyPlugin));
var loader = proxy.PluginLoader;
loader.Unload();
loader = new PluginLoader<T>(e.Name);
proxy.PluginLoader = loader;
OnPluginChange(new PluginManagerEventArgs<T>(e.Name, PluginChangeType.Created, proxy.ProxyPlugin));
}
}
break;
}
}
}
#endregion
}
protected virtual void OnPluginChange(PluginManagerEventArgs<T> e)
{
if (PluginChanged != null)
{
PluginChanged(this, e);
}
}
#endregion
#region公共
public static PluginManager<T> Instance
{
get
{
return _instance;
}
}
public event PluginChangeHandle<T> PluginChanged;
#endregion
}
附:
-
EMIT代理生成
/// <summary>
///从接口 TPlugin 生成代理类
/// </summary>
/// <typeparam name="TPlugin">要求是一个接口</typeparam>
public static class InterfaceProxyBuilder<TPlugin> where TPlugin : class
{
private static ConcurrentDictionary<Type, Type> proxies = new ConcurrentDictionary<Type, Type>();
private static void CheckTargetType(Type targetType)
{
if (!targetType.IsInterface)
{
throw new NotImplementedException("T 必须为接口");
}
}
/// <summary>
///生成接口实例对像的代理
/// </summary>
/// <typeparam name="TObject"></typeparam>
/// <param name="target"></param>
/// <returns></returns>
public static TPlugin CreateProxy<TObject>(TObject target, Type baseType) where TObject : TPlugin
{
Type targetType =typeof(TPlugin);
CheckTargetType(targetType);
var proxyType = proxies.GetOrAdd(targetType, t =>
{
return CreateProxyType(t, baseType);
});
return System.Activator.CreateInstance(proxyType, target) as TPlugin;
}
/// <summary>
///生成一个接口的代理类,并从baseType继承
/// </summary>
/// <param name="targetType">接口类型</param>
/// <param name="baseType">基类</param>
/// <returns>代理类</returns>
public static Type CreateProxyType(Type targetType, Type baseType)
{
Debug.Assert(targetType.IsInterface,"必须是接口");
string typeName = string.Concat(targetType.FullName, "_Proxy");
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assemblyBuilder.DefineDynamicModule("DynamicProxy", "DynamicAssembly.dll");
//声明类
var typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, baseType, new Type[] { targetType }); //
//实现类
//字段
var fieldBuilder = typeBuilder.DefineField("target", targetType, FieldAttributes.Private);
fieldBuilder.SetConstant(null);
//构造函数
var cnstBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { targetType });
{
var il = cnstBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fieldBuilder);
il.Emit(OpCodes.Ret);
}
//属性
Dictionary<MethodInfo, MethodBuilder> map = new Dictionary<MethodInfo, MethodBuilder>();
//方法代理
var methods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
methods.ToList().ForEach(mi =>
{
var retType = mi.ReturnType;
var parmTypes = mi.GetParameters().ToList().ConvertAll(pi => pi.ParameterType).ToArray();
MethodAttributes methodAttrs = mi.Attributes & (~MethodAttributes.Abstract);
var methodBuilder = typeBuilder.DefineMethod(mi.Name, methodAttrs, CallingConventions.Standard, mi.ReturnType, parmTypes);
//方法体
{
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fieldBuilder);
if (parmTypes.Length > 0)
{
for (int i = 1; i <= parmTypes.Length; ++i)
{
switch (i)
{
case 0:
il.Emit(OpCodes.Ldarg_0);
break;
case 1:
il.Emit(OpCodes.Ldarg_1);
break;
case 2:
il.Emit(OpCodes.Ldarg_2);
break;
case 3:
il.Emit(OpCodes.Ldarg_3);
break;
default:
il.Emit(OpCodes.Ldarg_S, i);
break;
}
}
}
il.Emit(OpCodes.Callvirt, mi);
if (mi.ReturnType == typeof(void))
{
il.Emit(OpCodes.Pop);
}
il.Emit(OpCodes.Ret);
}
map.Add(mi, methodBuilder);
});
var props = targetType.GetProperties();
props.ToList().ForEach(pi =>
{
var propBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.HasDefault, pi.ReflectedType, null);
if (pi.CanRead)
{
var mi = pi.GetGetMethod();
Debug.Assert(map.ContainsKey(mi));
var builder = map[mi];
propBuilder.SetGetMethod(builder);
}
if (pi.CanWrite)
{
var mi = pi.GetSetMethod();
Debug.Assert(map.ContainsKey(mi));
var builder = map[mi];
propBuilder.SetSetMethod(builder);
}
});
var ret = typeBuilder.CreateType();
#if DEBUG
assemblyBuilder.Save(string.Concat(typeName, "_", DateTime.Now.ToString("yyyyMMddHHmmss") , ".dll"));
#endif
return ret;
}
public static TPlugin CreateFuncProxy(Func<TPlugin> targetFunc, Type baseType)
{
Type targetType = typeof(TPlugin);
CheckTargetType(targetType);
var proxyType = proxies.GetOrAdd(typeof(Func<TPlugin>), t =>
{
return CreateFuncProxyType(t, baseType);
});
return System.Activator.CreateInstance(proxyType, targetFunc) as TPlugin;
}
/// <summary>
///生成一个接口工厂的代理类,并从baseType继承
/// </summary>
/// <param name="targetType">接口工厂类型要求必须为 Func<TPlugin></param>
/// <param name="baseType">基类</param>
/// <returns>代理类</returns>
public static Type CreateFuncProxyType(Type targetType, Type baseType)
{
var miInvoke = targetType.GetMethod("Invoke");
Debug.Assert(miInvoke != null, "Func<T>.Invoke 必须有");
Type interfaceType = miInvoke.ReturnType;
string typeName = string.Concat(interfaceType.FullName, "_FuncProxy");
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assemblyBuilder.DefineDynamicModule("DynamicProxy", "DynamicAssembly.dll");
//声明类
var typeBuilder = module.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, baseType, new Type[] { interfaceType }); //
//实现类
//字段
var fieldBuilder = typeBuilder.DefineField("targetFunc", targetType, FieldAttributes.Private);
fieldBuilder.SetConstant(null);
//构造函数
var cnstBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { targetType });
{
var il = cnstBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fieldBuilder);
il.Emit(OpCodes.Ret);
}
//属性
Dictionary<MethodInfo, MethodBuilder> map = new Dictionary<MethodInfo, MethodBuilder>();
//方法代理
var methods = interfaceType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
methods.ToList().ForEach(mi =>
{
var retType = mi.ReturnType;
var parmTypes = mi.GetParameters().ToList().ConvertAll(pi => pi.ParameterType).ToArray();
MethodAttributes methodAttrs = mi.Attributes & (~MethodAttributes.Abstract);
var methodBuilder = typeBuilder.DefineMethod(mi.Name, methodAttrs, CallingConventions.Standard, mi.ReturnType, parmTypes);
//方法体
{
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fieldBuilder);
il.Emit(OpCodes.Callvirt, miInvoke);
if (parmTypes.Length > 0)
{
for (int i = 1; i <= parmTypes.Length; ++i)
{
switch (i)
{
case 0:
il.Emit(OpCodes.Ldarg_0);
break;
case 1:
il.Emit(OpCodes.Ldarg_1);
break;
case 2:
il.Emit(OpCodes.Ldarg_2);
break;
case 3:
il.Emit(OpCodes.Ldarg_3);
break;
default:
il.Emit(OpCodes.Ldarg_S, i);
break;
}
}
}
il.Emit(OpCodes.Callvirt, mi);
if (mi.ReturnType == typeof(void))
{
il.Emit(OpCodes.Pop);
}
il.Emit(OpCodes.Ret);
}
map.Add(mi, methodBuilder);
});
var props = interfaceType.GetProperties();
props.ToList().ForEach(pi =>
{
var propBuilder = typeBuilder.DefineProperty(pi.Name, PropertyAttributes.HasDefault, pi.ReflectedType, null);
if (pi.CanRead)
{
var mi = pi.GetGetMethod();
Debug.Assert(map.ContainsKey(mi));
var builder = map[mi];
propBuilder.SetGetMethod(builder);
}
if (pi.CanWrite)
{
var mi = pi.GetSetMethod();
Debug.Assert(map.ContainsKey(mi));
var builder = map[mi];
propBuilder.SetSetMethod(builder);
}
});
var ret = typeBuilder.CreateType();
#if DEBUG
assemblyBuilder.Save(string.Concat(typeName, "_Func_", DateTime.Now.ToString("yyyyMMddHHmmss"), ".dll"));
#endif
return ret;
}
}
2. 项目类关系图: