之前自己在写一个IOC小轮子的时候,临时想加一个动态代理拦截功能,考虑到实用性方面,使用了Emit动态生成的方式代替RealProxy加反射的实现,网上查找过不少版本,但是都存在一些缺陷,所以决定自己实现一个。
首先了解一下动态代理的原理,在编码过程中,如果对原有代码不想做改动,且对操作前操作后加入一些迭代代码,我们会使用静态代理,也就是新建一个类,持有原实现类的引用,写一个同名方法并在其中调用,大概的编码形式如下:
代码语言:javascript复制public class MovieProxy
{
private Movie _beproxy = new Movie();
public override void Test()
{
Console.WriteLine("Before");
_beproxy.Test();
Console.WriteLine("After");
}
}
更灵活一步的实现方式可以改造为继承基类,或者实现接口
代码语言:javascript复制 public class MovieProxy : Movie
{
public override void Test()
{
Console.WriteLine("Before");
base.Test();
Console.WriteLine("After");
}
}
这样实现也随之带来一个问题,如果我有很多类或者一个类中需要代理的方法很多,编码就会做很多重复的操作,所以我们需要通过动态代理进行自动生成,先看一下实现后的硬编码代码
继承:
代码语言:javascript复制public class MovieProxy : Movie
{
private MoveIntercept _interceptor = new MoveIntercept();
private Movie _beproxy = new Movie();
public override void Test()
{
object[] parameters = new object[0];
this._interceptor.Before(this, "Test", parameters);
base.Test();
object result = null;
this._interceptor.After(this, "Test", result);
}
public override T Test1<T>()
{
object[] parameters = new object[0];
this._interceptor.Before(this, "Test1", parameters);
object obj = base.Test1<T>();
this._interceptor.After(this, "Test1", obj);
return (T)((object)obj);
}
}
实现接口
代码语言:javascript复制public class MySQLCatchProxy : ICatch
{
private DefaultIntercept _interceptor = new DefaultIntercept();
private MySQLCatch _beproxy = new MySQLCatch();
public override void Catch(string text)
{
object[] parameters = new object[]
{
text
};
this._interceptor.Before(this._beproxy, "Catch", parameters);
this._beproxy.Catch(text);
object result = null;
this._interceptor.After(this._beproxy, "Catch", result);
}
public override void UnCatch()
{
object[] parameters = new object[0];
this._interceptor.Before(this._beproxy, "UnCatch", parameters);
this._beproxy.UnCatch();
object result = null;
this._interceptor.After(this._beproxy, "UnCatch", result);
}
public override string SaveCatch()
{
object[] parameters = new object[0];
this._interceptor.Before(this._beproxy, "SaveCatch", parameters);
object obj = this._beproxy.SaveCatch();
this._interceptor.After(this._beproxy, "SaveCatch", obj);
return (string)obj;
}
}
通过反编译动态生成的实现类可以看出,我们要实现的是对需要代理的方法执行前,执行后进行拦截,获取执行对象,方法名,参数等有效信息
对于生成的需要保存的类我们可以如下定义
代码语言:javascript复制 string nameOfAssembly = ImpType.Name "ProxyAssembly";
string nameOfModule = ImpType.Name "ProxyModule";
string nameOfType = ImpType.Name "Proxy";
var assemblyName = new AssemblyName(nameOfAssembly);
ModuleBuilder moduleBuilder = null;
AssemblyBuilder assemblyBuilder = null;
assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = assemblyBuilder.DefineDynamicModule(nameOfModule, nameOfType ".dll");
如果我们只需要用这个类的瞬时定义,定义如下:
代码语言:javascript复制 var assembly = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
moduleBuilder = assembly.DefineDynamicModule(nameOfModule);
继承方式声明TypeBuilder:
代码语言:javascript复制 typeBuilder = moduleBuilder.DefineType(
nameOfType, TypeAttributes.Public, ImpType);
实现接口方式声明TypeBuilder
代码语言:javascript复制 typeBuilder = moduleBuilder.DefineType(
nameOfType, TypeAttributes.Public, null, new[] { InterfaceType });
如果是继承方式,ImpType与InterfaceType类型一致,声明完这个类,接下来我们就需要对这个类的内容进行完善,先上代码
代码语言:javascript复制 private static void InjectInterceptor(TypeBuilder typeBuilder, Type ImpType, Type InterceptType, bool inheritMode, bool isInterceptAllMethod)
{
// ---- 变量定义 ----
var constructorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard, null);
var ilOfCtor = constructorBuilder.GetILGenerator();
//---- 拦截类对象定义 ----
//声明
var fieldInterceptor = typeBuilder.DefineField(
"_interceptor", InterceptType, FieldAttributes.Private);
//赋值
ilOfCtor.Emit(OpCodes.Ldarg_0);
ilOfCtor.Emit(OpCodes.Newobj, InterceptType.GetConstructor(new Type[0]));
ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);
//---- 实现类对象定义 ----
//声明
var fieldBeProxy = typeBuilder.DefineField(
"_beproxy", ImpType, FieldAttributes.Private);
//赋值
ilOfCtor.Emit(OpCodes.Ldarg_0);
ilOfCtor.Emit(OpCodes.Newobj, ImpType.GetConstructor(new Type[0]));
ilOfCtor.Emit(OpCodes.Stfld, fieldBeProxy);
ilOfCtor.Emit(OpCodes.Ret);
// ---- 定义类中的方法 ----
var methodsOfType = ImpType.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(a => a.IsVirtual).ToArray();
string[] ignoreMethodName = new[] { "GetType", "ToString", "GetHashCode", "Equals" };
for (var i = 0; i < methodsOfType.Length; i )
{
bool IsMethodIntercept = false;
var method = methodsOfType[i];
//---- 过滤Object基类方法,如果是继承,基类中的属性get,set方法也要过滤 ----
if (ignoreMethodName.Contains(method.Name) || method.Name.StartsWith("set_") || method.Name.StartsWith("get_"))
{
continue;
}
// ---- 判断方法是否需要拦截 ----
if (isInterceptAllMethod)
{
if (method.GetCustomAttribute(typeof(IgnoreInterceptAttibute)) == null)
{
IsMethodIntercept = true;
}
}
else
{
if (method.GetCustomAttribute(typeof(InterceptAttibute)) != null)
{
IsMethodIntercept = true;
}
}
var methodParameterTypes =
method.GetParameters().Select(p => p.ParameterType).ToArray();
// ---- 定义方法名与参数 ----
var methodBuilder = typeBuilder.DefineMethod(
method.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
CallingConventions.Standard,
method.ReturnType,
methodParameterTypes);
//如果是泛型方法
if (method.IsGenericMethod)
{
//获取所有泛型参数类型定义
Type[] Args = method.GetGenericArguments();
List<string> GenericArgNames = new List<string>();
for (int j = 0; j < Args.Length; j )
{
GenericArgNames.Add(Args[j].Name);
}
//代理类方法生成泛型参数定义
GenericTypeParameterBuilder[] DGP = methodBuilder.DefineGenericParameters(GenericArgNames.ToArray());
//泛型参数约束设置
for (int j = 0; j < DGP.Length; j )
{
//泛型参数继承约束
DGP[j].SetBaseTypeConstraint(Args[j].BaseType);
//泛型参数完成接口约束
DGP[j].SetInterfaceConstraints(Args[j].GetInterfaces());
}
}
var ilOfMethod = methodBuilder.GetILGenerator();
var methodresult = ilOfMethod.DeclareLocal(typeof(object)); //instance of result
// ---- before ----
if (IsMethodIntercept)
{
var parameters = ilOfMethod.DeclareLocal(typeof(object[]));
ilOfMethod.Emit(OpCodes.Ldc_I4, methodParameterTypes.Length);
ilOfMethod.Emit(OpCodes.Newarr, typeof(object));
ilOfMethod.Emit(OpCodes.Stloc, parameters);
for (var j = 0; j < methodParameterTypes.Length; j )
{
ilOfMethod.Emit(OpCodes.Ldloc, parameters);
ilOfMethod.Emit(OpCodes.Ldc_I4, j);
ilOfMethod.Emit(OpCodes.Ldarg, j 1);
ilOfMethod.Emit(OpCodes.Stelem_Ref);
}
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);
//拦截方法参数赋值
if (inheritMode)
{
//继承传递代理类本身
ilOfMethod.Emit(OpCodes.Ldarg_0);
}
else
{
//接口传递实现类
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
}
ilOfMethod.Emit(OpCodes.Ldstr, method.Name);
ilOfMethod.Emit(OpCodes.Ldloc, parameters);
//调用拦截类中的Before方法
ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("Before"));
}
// ---- call ----
定义实现类局部变量
//var localimpobj = ilOfMethod.DeclareLocal(ImpType);
new一个实现类的对象
//ilOfMethod.Emit(OpCodes.Newobj, ImpType.GetConstructor(new Type[0]));
局部变量赋值
//ilOfMethod.Emit(OpCodes.Stloc, localimpobj);
局部变量出栈,等待调用
//ilOfMethod.Emit(OpCodes.Ldloc, localimpobj);
if (inheritMode)
{
//继承方法调用父类的方法
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Call, ImpType.GetMethod(method.Name));
}
else
{
//接口方法调用实现类的方法
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
for (var j = 0; j < methodParameterTypes.Length; j )
{
ilOfMethod.Emit(OpCodes.Ldarg, j 1);
}
//调用方法
ilOfMethod.Emit(OpCodes.Callvirt, ImpType.GetMethod(method.Name));
}
// ---- after ----
if (IsMethodIntercept)
{
if (method.ReturnType == typeof(void))
{
ilOfMethod.Emit(OpCodes.Ldnull);
}
else
{
ilOfMethod.Emit(OpCodes.Box, method.ReturnType);
}
ilOfMethod.Emit(OpCodes.Stloc, methodresult);
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldInterceptor);
//拦截方法参数赋值
if (inheritMode)
{
//继承传递代理类本身
ilOfMethod.Emit(OpCodes.Ldarg_0);
}
else
{
//接口传递实现类
ilOfMethod.Emit(OpCodes.Ldarg_0);
ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);
}
ilOfMethod.Emit(OpCodes.Ldstr, method.Name);
ilOfMethod.Emit(OpCodes.Ldloc, methodresult);
ilOfMethod.Emit(OpCodes.Callvirt, InterceptType.GetMethod("After"));
//调用完After方法后,将实现类的方法返回值的临时变量再次压栈用作代理方法的返回
ilOfMethod.Emit(OpCodes.Ldloc, methodresult);
}
// pop the stack if return void
if (method.ReturnType == typeof(void))
{
ilOfMethod.Emit(OpCodes.Pop);
}
else
{
if (method.ReturnType.IsValueType)
{
ilOfMethod.Emit(OpCodes.Unbox_Any, method.ReturnType);
}
else
{
ilOfMethod.Emit(OpCodes.Castclass, method.ReturnType);
}
}
// complete
ilOfMethod.Emit(OpCodes.Ret);
}
}
代码可以有点长,我们慢慢分析
1.我们首先通过
代码语言:javascript复制 ilOfCtor.Emit(OpCodes.Ldarg_0);
ilOfCtor.Emit(OpCodes.Newobj, InterceptType.GetConstructor(new Type[0]));
ilOfCtor.Emit(OpCodes.Stfld, fieldInterceptor);
这种方式,定义全局变量,也就是生成类中的代理类,和实现类对象
代码语言:javascript复制private DefaultIntercept _interceptor = new DefaultIntercept();
private MySQLCatch _beproxy = new MySQLCatch();
2.定义方法,我们获取类中的方法会包含GetType,ToString,GetHashCode,Equals这四个Object类中的方法,需要过滤,如果是继承方法是,属性的set,get方法也要去除,如果方法是泛型方法,我们需要对泛型的数量,名称,泛型约束进行同步
代码语言:javascript复制 //如果是泛型方法
if (method.IsGenericMethod)
{
//获取所有泛型参数类型定义
Type[] Args = method.GetGenericArguments();
List<string> GenericArgNames = new List<string>();
for (int j = 0; j < Args.Length; j )
{
GenericArgNames.Add(Args[j].Name);
}
//代理类方法生成泛型参数定义
GenericTypeParameterBuilder[] DGP = methodBuilder.DefineGenericParameters(GenericArgNames.ToArray());
//泛型参数约束设置
for (int j = 0; j < DGP.Length; j )
{
//泛型参数继承约束
DGP[j].SetBaseTypeConstraint(Args[j].BaseType);
//泛型参数完成接口约束
DGP[j].SetInterfaceConstraints(Args[j].GetInterfaces());
}
}
3.方法声明完毕后,我们需要进行方法的内部实现,实现顺序为,Before——Invoke——After,我这里的代码加了一些判断,是结合Attribue使用,有两种方式,如果是全局拦截,则方法有IgnoreInterceptAttibute标记的不拦截,如果是自定义拦截,则拦截有InterceptAttibute标记的方法,使用更加方便灵活,在调用Before和After方法时,对于传入的引用对象需要进行区分,如果是继承方法,通过 ilOfMethod.Emit(OpCodes.Ldarg_0);传入this,即子类本身,如果是实现接口方法,通过 ilOfMethod.Emit(OpCodes.Ldarg_0);ilOfMethod.Emit(OpCodes.Ldfld, fieldBeProxy);加载声明的接口实现,对于有返回值的结果,我们需要对Invoke执行的结果出栈 ilOfMethod.Emit(OpCodes.Stloc, methodresult);,但是动态代理类本身也要返回值,所以调用完After方法后需要通过ilOfMethod.Emit(OpCodes.Ldloc, methodresult);对返回值再次入栈,需要注意的是,继承方式拦截,父类方法一定要声明为虚方法,才能被子类重写
大概实现过程已经分析完毕了,对于Emit最重要的还是OpCodes的理解和方法调用与栈之间的联系,如果有什么不完善的地方,希望大家提出意见,相互交流,共同进步!
完整源码地址:FastIOC: 轻量级IOC容器 中的DynamictProxy类