静态代理和动态代理方式分别实现AOP拦截功能
摘要:面向对象的思想强调"一切皆是对象",在面向对象的程序中我们使用真实概念的模型思考问题,使得整个软件系统开发可以像搭建房屋一样有条不紊。然而面向对象也并非完美无缺的,它更注重于对象层次结构方面的东西,对于如何更好的管理对象行为内部结构,还存在着些许不足。那么我们如何使这个问题的得到更完美的解决呢?答案就是AOP。
主要内容:
- AOP简述
- 利用动态代理实现AOP
- 总结
一、AOP简述
上面我们说了一些关于AOP所要解决的问题以及这样做的好处,下面我们主要看一下如何实现AOP。
AOP的实现一般分为:动态代理(DynamicProxy)和静态织入(StaticWeave)两种方式。这里我们主要说的是如何利用Emit来自己实现动态代理方式的AOP(关于Emit的知识大家可以看一下我的另一篇博客反射发出--Emit),以此来帮助大家更深入的理解AOP。
在开始动态代理之前假设我们有这样一个类,它是负责处理我们的业务逻辑的。
先看对应接口和相应的类
interface IBusinessLogic { void ShowMessage(string msg); int Calculate(); } public class MyBusinessLogic : IBusinessLogic { public virtual void ShowMessage(string msg) { Console.WriteLine(msg); } public virtual int Calculate() { int sum = 0; for (int i = 1; i <= 100; ++i) { sum += +i; } Console.WriteLine(sum); return sum; } }
现在我们需要给所有的核心业务添加日志记录功能,此时大概会有下面几种选择:
1.在ShowMessage和Calculate内部都添加上记录日志的代码 。
public class MyBusinessLogic : IBusinessLogic { public virtual void ShowMessage(string msg) { Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.MyBusinessLogic ShowMessage is called!"); Console.WriteLine(msg); Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.MyBusinessLogic ShowMessage has finished!"); } public virtual int Calculate() { Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.Calculate ShowMessage is called!"); int sum = 0; for (int i = 1; i <= 100; ++i) { sum += +i; } Console.WriteLine(sum); Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.Calculate ShowMessage has finished!"); return sum; } }
2.重新写一个类继承于MyBusinessLogic并对其中的方法重写。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DanymicProxy { class MyBusinessLogicDecoration : MyBusinessLogic { public override void ShowMessage(string msg) { Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.MyBusinessLogic ShowMessage is called!"); base.ShowMessage(msg); Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.MyBusinessLogic ShowMessage has finished!"); } public override int Calculate() { Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.Calculate ShowMessage is called!"); int t = base.Calculate(); Console.WriteLine(DateTime.Now.ToString() + ":DanymicProxy.Calculate ShowMessage has finished!"); return t; } } }
3.写一个静态代理类。
首先看一下代理中用到的拦截器接口
然后编写一个操作日志的拦截器
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DanymicProxy { public interface IInterceptor { object Invoke(object obj, string methodName, object[] parameters); } public class LogInterceptor : IInterceptor { public object Invoke(object obj, string methodName, object[] parameters) { Console.WriteLine(DateTime.Now.ToString() + ":" + obj.ToString() + "'s " + methodName + " is called!"); object rst = obj.GetType().GetMethod(methodName).Invoke(obj, parameters); Console.WriteLine(DateTime.Now.ToString() + ":" + obj.ToString() + "'s " + methodName + " has finished!"); return rst; } } }
再看静态代理类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DanymicProxy; namespace DanymicProxy { class MyBusinessLogicProxy { private IInterceptor _interceptor = null; public MyBusinessLogicProxy(IInterceptor interceptor) { _interceptor = interceptor; } public virtual void ShowMessage(string msg) { _interceptor.Invoke(new MyBusinessLogic(), "ShowMessage", newobject[] { msg }); } public virtual int Calculate() { return Convert.ToInt32(_interceptor.Invoke(new MyBusinessLogic(), "Calculate", null)); } } }
接着我们可以使用下面的代码做测试
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DanymicProxy { class Program { static void Main(string[] args) { (new MyBusinessLogicProxy(new LogInterceptor())).ShowMessage("Hello World!"); (new MyBusinessLogicProxy(new LogInterceptor())).Calculate(); ; } } }
4.使用动态代理
相对于前两种方法,第三种方法应该算是比较好的方法了,可是做起来比较麻烦。不仅必须给每个业务类都重新写一个包含日志处理的代理类,而且如果我有其他类似日志处理的模块(例如权限模块)需要添加还要再写包含其他功能的代理类。这样做起来不仅工作量大,而且再维护起来也十分麻烦。怎么办?方法就是使用动态代理。
我们下面要实现的动态代理,其运行机制和上面说的静态代理可以说是完全相同的(事实上我们编写Emit的时候就是仿照静态代理类来写的),只不过重复写代理类的工作交给程序来做了而已。
具体过程:首先根据泛型类型创建名字为"泛型+Proxy"的类;在类中声明一个IInterceptor型的私有变量_interceptor赋值为null;接着创建构造函数,其参数为IInterceptor类型;然后我们遍历泛型中的方法,创建名称、返回类型、参数均与泛型方法一致的方法,然后在方法中使用interceptor的Invoke方法调用对应的方法(参数分别为泛型对象、方法名和参数);最后实例化此类,并将其返回。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Reflection.Emit; namespace DanymicProxy { public class Proxywhere T : class { public static T CreateProxy(IInterceptor interceptor) { //构建程序集 AssemblyName aName = new AssemblyName("Cmj.DotNet"); AppDomain aDomain = AppDomain.CurrentDomain;//应用程序域,这里设为当前域 AssemblyBuilder aBuidler = aDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave); //定义模块 ModuleBuilder mBuidler = aBuidler.DefineDynamicModule("MyModule", "Cmj.DotNet.dll"); //创建类型(其实就是一个类) StringBuilder sbClassName = new StringBuilder("Cmj.DotNet."); sbClassName.Append(typeof(T).Name); sbClassName.Append("Proxy"); TypeBuilder tBuidler = mBuidler.DefineType(sbClassName.ToString(), TypeAttributes.Public | TypeAttributes.Class, typeof(T));//继承于T //--实现类--// //定义私有字段 FieldBuilder fbInterceptor = tBuidler.DefineField("_interceptor", typeof(IInterceptor), FieldAttributes.Private); //为私有变量赋值 fbInterceptor.SetConstant(null); //定义构造函数 ConstructorBuilder ctstBuilder = tBuidler.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(IInterceptor) }); ILGenerator ctstGenerator = ctstBuilder.GetILGenerator(); ctstGenerator.Emit(OpCodes.Ldarg_0); ctstGenerator.Emit(OpCodes.Ldarg_1); ctstGenerator.Emit(OpCodes.Stfld, fbInterceptor);//给字段赋值 ctstGenerator.Emit(OpCodes.Ret); //定义方法 MethodInfo[] methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public); Type retType = null; Type[] paramsType = null; ParameterInfo[] parameters = null; MethodBuilder mtdBuidler = null; ILGenerator mtdGenerator = null; foreach (MethodInfo method in methods) { if (method.Name != "ToString" && method.Name != "Equals" && method.Name != "GetHashCode" && method.Name != "GetType") { //获得返回值类型 retType = method.ReturnType; //获得参数类型 parameters = method.GetParameters(); paramsType = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; ++i) { paramsType[i] = parameters[i].ParameterType; } //实现方法体 mtdBuidler = tBuidler.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, retType, paramsType); mtdGenerator = mtdBuidler.GetILGenerator(); mtdGenerator.Emit(OpCodes.Ldarg_0); mtdGenerator.Emit(OpCodes.Ldfld, fbInterceptor); mtdGenerator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(new Type[0])); mtdGenerator.Emit(OpCodes.Ldstr, method.Name); if (paramsType.Length == 0) { mtdGenerator.Emit(OpCodes.Ldnull); } else { LocalBuilder paras = mtdGenerator.DeclareLocal(typeof(object[])); mtdGenerator.Emit(OpCodes.Ldc_I4, paramsType.Length); mtdGenerator.Emit(OpCodes.Newarr, typeof(object)); mtdGenerator.Emit(OpCodes.Stloc, paras); for (var j = 0; j < paramsType.Length; j++) { mtdGenerator.Emit(OpCodes.Ldloc, paras); mtdGenerator.Emit(OpCodes.Ldc_I4, j); mtdGenerator.Emit(OpCodes.Ldarg, j + 1); mtdGenerator.Emit(OpCodes.Stelem_Ref); } mtdGenerator.Emit(OpCodes.Ldloc, paras); } mtdGenerator.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("Invoke")); if (retType == typeof(void)) { mtdGenerator.Emit(OpCodes.Pop); } mtdGenerator.Emit(OpCodes.Ret); } } //创建引用、调用方法 Type proxyType = tBuidler.CreateType(); aBuidler.Save("Cmj.DotNet.dll"); object proxy = Activator.CreateInstance(proxyType, newobject[]{ interceptor});//实例化 return proxy as T; } } }
最后我们测试一下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DanymicProxy { class Program { static void Main(string[] args) { Proxy.CreateProxy(new LogInterceptor()).ShowMessage("Hello World!"); Proxy .CreateProxy(new LogInterceptor()).Calculate(); } } }
运行效果
三、总结:
上面简单的介绍了如何用动态代理的方式实现AOP,主要是帮助大家理解动态代理AOP的大致思路。在实际开发中我们可能更多时候会选择一些AOP的工具(例如Castle中的Aspect#、Spring AOP、AspectDNG等),这些内容(包括静态织入方式实现AOP)我们今后有机会再一块学习。
本作品采用知识共享署名 2.5 中国大陆许可协议进行许可,欢迎转载,演绎或用于商业目的。但转载请注明来自崔江涛(KenshinCui),并包含相关链接。
出处:https://www.cnblogs.com/kenshincui/archive/2011/01/01/1923954.html