象DotNet,Java之类的语言能够进行动态代理类的创建,得益于其本身并不是直接编译成机器代码,而是编译成中间语言,在运行时才解释或动态编译成目标机器语言。这也是为什么这些概念先在Java兴起的根本原因。产生动态代理类,一般都是利用Emit命名空间的指令,但这个对IL的要求比较高,我这里利用C#提供的动态编译功能实现,
优点是直观,容易理解,不用熟悉IL指令,缺点当然是显得不怎么专业。
(网上很多利用Emit,IL指令构建动态代理类的代码)
能够动态代理(我更倾向于用装饰),一个很关键的地方就是要求你的类中需要被监视(hook)的成员至少是保护的虚方法.
//代理类工厂,简单的工厂方法.
public class MyProxyFactory
{
object _target;
private List<IMethodInterceptor> _adviceList = new List<IMethodInterceptor>();
//所谓的前置,后置等通知性类,其实就是要注入的间谍。
public void Add(IMethodInterceptor advice)
{
_adviceList.Add(advice);
}
//构造函数,传入目标对象.
public MyProxyFactory(object target)
{
_target = target;
}
// 创建并放回代理类。注意,这里是每次都创建,实际上是可以缓存的.
public object GetProxyClass()
{
StringBuilder theClassBuilder = new StringBuilder();
Type theTargetType = _target.GetType();
string theProxyClassName = "__" + theTargetType.Name + "_Proxy";
theClassBuilder.AppendLine("using System;");
theClassBuilder.AppendLine("using System.Collections.Generic;");
theClassBuilder.AppendLine("using System.Text;");
theClassBuilder.AppendLine("using System.Reflection;");
theClassBuilder.AppendLine("using System.Reflection.Emit;");
theClassBuilder.AppendLine("namespace " + theTargetType.Namespace);
theClassBuilder.AppendLine("{");
theClassBuilder.AppendLine("public class "+theProxyClassName+" : " + theTargetType.FullName + "{");
theClassBuilder.AppendLine(" private " + theTargetType.FullName + " _target;");
theClassBuilder.AppendLine(" private List<IMethodInterceptor> _adviceList = null;");
theClassBuilder.AppendLine(" public __" + theTargetType.Name + "_Proxy(" + theTargetType.FullName + " target, List<IMethodInterceptor> AdviceList)");
theClassBuilder.AppendLine(" {");
theClassBuilder.AppendLine(" _target = target;");
theClassBuilder.AppendLine(" this._adviceList = AdviceList;");
theClassBuilder.AppendLine(" }");
//获取目标类的方法,准备给代理类继承
MethodInfo[] methodinfos = theTargetType.GetMethods(BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance);
foreach (MethodInfo mi in methodinfos)
{
if (mi.IsVirtual)
{
string theRetType = mi.ReturnType.FullName;
if (mi.ReturnType == typeof(void))
{
theRetType = "void";
}
string theParams = "";
string theTypes = "";
string thePMInsts = "";
ParameterInfo[] thePs = mi.GetParameters();
for (int i = 0; i < thePs.Length; i++)
{
if (i == 0)
{
theTypes = "typeof(" + thePs[i].ParameterType.FullName + ")";
if (thePs[i].IsOut)
{
theParams = "out " + thePs[i].ParameterType.FullName + " " + thePs[i].Name;
thePMInsts = "out " + thePs[i].Name;
}
else
{
theParams = "" + thePs[i].ParameterType.FullName + " " + thePs[i].Name;
thePMInsts = "" + thePs[i].Name;
}
}
else
{
theTypes = ",typeof(" + thePs[i].ParameterType.FullName + ")";
if (thePs[i].IsOut)
{
thePMInsts = ",out " + thePs[i].Name;
theParams = ",out " + thePs[i].ParameterType.FullName + " " + thePs[i].Name;
}
else
{
thePMInsts = "," + thePs[i].Name;
theParams = "," + thePs[i].ParameterType.FullName + " " + thePs[i].Name;
}
}
}
theClassBuilder.AppendLine(" public override " + theRetType + " " + mi.Name + "(" + theParams + ")");
theClassBuilder.AppendLine(" {");
theClassBuilder.AppendLine(" if (_adviceList != null)");
theClassBuilder.AppendLine(" {");
theClassBuilder.AppendLine(" foreach (IMethodInterceptor item in _adviceList)");
theClassBuilder.AppendLine(" {");
theClassBuilder.AppendLine(" MethodInfo theMI = _target.GetType().GetMethod(\"" + mi.Name + "\", new Type[] { " + theTypes + "});");
theClassBuilder.AppendLine(" AdviseInvocation theInvocation = new AdviseInvocation(_target, theMI, new object[]{" + thePMInsts + "});");
if (mi.ReturnType == typeof(void))
{
theClassBuilder.AppendLine(" item.Invoke(theInvocation);");
}
else
{
theClassBuilder.AppendLine(" return (" + theRetType + ")(item.Invoke(theInvocation));");
}
theClassBuilder.AppendLine(" }");
theClassBuilder.AppendLine(" }");
if (mi.ReturnType == typeof(void))
{
theClassBuilder.AppendLine(" _target." + mi.Name + "(" + thePMInsts + ");");
}
else
{
theClassBuilder.AppendLine(" return (" + theRetType + ")(_target." + mi.Name + "(" + thePMInsts + "));");
}
theClassBuilder.AppendLine("}");
}
}
theClassBuilder.AppendLine("}");
theClassBuilder.AppendLine("}");
//测试观察用
Console.WriteLine(theClassBuilder.ToString());
//创建编译器实例。
CSharpCodeProvider provider = new CSharpCodeProvider();
//设置编译参数。
CompilerParameters paras = new CompilerParameters();
paras.GenerateExecutable = false;
paras.GenerateInMemory = true;
//paras.ReferencedAssemblies.Add("System.dll");
paras.ReferencedAssemblies.Add("SpringNetStudy.exe");
//paras.ReferencedAssemblies.Add("mscorlib.dll");
//paras.ReferencedAssemblies.Add("");
//编译代码。
CompilerResults result = provider.CompileAssemblyFromSource(paras, theClassBuilder.ToString());
//获取编译后的程序集。
Assembly assembly = result.CompiledAssembly;
Type theType = assembly.GetType(theTargetType.Namespace+"."+ theProxyClassName,false,true);
var obj = Activator.CreateInstance(theType, new object[] {_target,_adviceList});
return obj;
}
}
后记:有些东西看起来很神秘,但实际上也不过如此。国内没法做出好的框架,其实一个主要的原因就是编译技术不过关,没有自己的语言。如果Emit和动态编译命名空间的方法不公开,就很难做了.