象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和动态编译命名空间的方法不公开,就很难做了.