在JAVA中实现动态接口代理的方法有很多,但显然在.NET中相似的方法似乎挺难找的 当然这不是说你无法自己写出这方面的东西 它的确不会是个太难的东西 只是会相对麻烦些
对于动态接口代理对我们而言它具备有多大的意义?实际上从我角度上看它的意义并不大 很多时候它或许只会是个嚼头 一个花瓶具象解决不了任何问题
那么思考一下动态接口代理是如何实现,我们就用JAVA语言中的“InvocationHandler & Proxy”的实现方式为概念去思考一下好了
那么姑且先看看下面的JAVA代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface IFoo {
void say();
}
class FooHandler implements InvocationHandler {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
if (("say").equals(arg1.getName())) {
System.out.println("hello world!");
}
return null;
}
}
public abstract class Program {
public static void main(String[] args) throws InterruptedException {
InvocationHandler handler = new FooHandler();
Class<?> clazz = IFoo.class;
IFoo foo = (IFoo) Proxy.newProxyInstance(clazz.getClassLoader(),
new Class[] { clazz }, handler);
foo.say();
}
}
这是JAVA中很形象的一种动态接口代理的方法、从它的动态代码编写结构上我们很容易看明白一个东西即“
InvocationHandler
”是具象成员的执行体、它主要针对于友元函数
如上图所示,Object只是一个花瓶的具象它不做任何事它只有一件事把调用的任务“桥接”到“InvocationHandler”再由它处理流程(但此时它却又更高级的权限它可以绝对是否处理此调用,但显然你同样可以在具象内做到 但代码可能会变得不是很好看)
那么知道了它的思想我们要如何去实现动态代理接口的工具类呢,那么有必要弄明白既然既然动态生成的具象它所有的友元函数都需要调用“InvocationHandler”来完成 那么显然你肯定需要把“InvocationHandler”的引用传入对象
1、构造(Constructor)在这里定义构造时提供一个“InvocationHandler”类型的参数输入 这肯定是很重要的地方当然你也可以定义一个公共属性或字段在输入引用但显然不是太好
private static void CreateConstructor(TypeBuilder tb, FieldBuilder fb)
{
Type[] args = new Type[] { typeof(InvocationHandler) };
ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, args);
ILGenerator il = ctor.GetILGenerator();
//
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, fb);
il.Emit(OpCodes.Ret);
}
2、后台字段(Blank field)它主要保存对“
InvocationHandler”的一个地址引用,在接口里面调用一个方法或属性时那么就会去使用这个引用 所以这个字段的重要性是很高 当然既然是后台字段肯定是不公开私有的字段
private static FieldBuilder CreateField(TypeBuilder tb)
{
return tb.DefineField("handler", typeof(InvocationHandler), FIELD_ATTRIBUTES);
}
3、桥接函数体(Bridge function)这显然是花瓶具象内部最麻烦的一个,它需要把调用的参数一个一个的加入固定数组然后再把一些关键信息压入堆栈在桥接给
“
InvocationHandler
”进行处理、但是有些函数内部是没有返回值的那么你还需要对这种函数进行处理 否则会出现一些故障 有返回值的但是桥接给“InvocationHandler”处理 但如果被代理的函数具备返回值 那么不可避免你需要去进行一次转换 “InvocationHandler”返回的是一个Object类型
1、定义固态数组
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldc_I4, args.Length);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc_0);
2、把函数调用参数放入数组
for (int i = 0; i < args.Length; i++)
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldarg, (1 + i));
il.Emit(OpCodes.Box, args[i].ParameterType);
il.Emit(OpCodes.Stelem_Ref);
}
3、桥接到“
InvocationHandler”中进行处理
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fb);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, met.MetadataToken);
il.Emit(OpCodes.Ldstr, met.Name);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Call, typeof(InvocationHandler).GetMethod("InvokeMember", BindingFlags.Instance | BindingFlags.Public));
4、对“InvocationHandler”的返回值进行处理
if (met.ReturnType == typeof(void))
{
il.Emit(OpCodes.Pop);
}
else
{
il.Emit(OpCodes.Unbox_Any, met.ReturnType);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Ldloc_1);
}
il.Emit(OpCodes.Ret);
没时间说的太详细了 今天就先这样把 下面是一个例子与JAVA的使用上并没有太大的一个区别但是显然我这篇文档并不是再告诉你如何去使用 这显然没有意义、有时间的话在完善这篇文章的内容吧
源代码与示例代码的下载地址:http://pan.baidu.com/s/1qYatYSs
namespace STDLOGIC_SERVER.AOP.Proxy
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
public static class Program
{
public interface IFoo
{
void Say();
}
public class FooInvocationHandler : InvocationHandler
{
object InvocationHandler.InvokeMember(object obj, int rid, string name, params object[] args)
{
MethodBase met = typeof(IFoo).Module.ResolveMethod(rid);
if (met.Name == "Say")
{
Console.WriteLine("hello world!");
}
return null;
}
}
static void Main()
{
IFoo foo = InterfaceProxy.New<IFoo>(new FooInvocationHandler());
foo.Say();
Console.ReadKey(false);
}
}
}