.Net基于RealProxy实现AOP
一、概述
关于AOP(面向切面编程)还是先讲一个日常经常碰到的场景“错误日志的记录”,一般来说我们编码的时候想记录错误日志都是用try..catch来进行捕捉和记录,慢慢的你会发现你每一个方法都得加try..catch才能记录每一个方法的错误日志。而有什么办法能做一个统一的拦截呢?做webform的时候可能会用到IHttpModule去实现,也有的会继承HttpApplication去做异常拦截。但这并不能应用在所有的项目场景中,而且这两者针对的对象也不是方法而是http请求。在面对这些场景中AOP就派上用场了。AOP在园子里也不算是一个陌生的话题了,关于AOP理论的文章也是挺多的,也有不少开源的代码,可以很好的帮组我们了解这方面的内容。这段时间自己也是在了解这方面的东西趁着周末的时间就和大家分享一下这方面的内容,代码不高深,写得不好也别吐槽!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class BaseException : IHttpModule { #region 接口实现 /// <summary> /// 初始化 /// </summary> /// <param name="context"></param> public void Init(HttpApplication context) { context.Error += new EventHandler(this.OnError); } /// <summary> /// 释放 /// </summary> public void Dispose() { } #endregion #region 事件 /// <summary> /// 错误事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnError(object sender, EventArgs e) { this.ExceptionHandle(); } #endregion #region 异常处理 /// <summary> /// 异常处理 /// </summary> protected virtual void ExceptionHandle() { //写异常日志 Exception finalEx = HttpContext.Current.Server.GetLastError(); Exception innerEx = finalEx.InnerException; while (innerEx != null) { finalEx = innerEx; innerEx = finalEx.InnerException; } ExceptionLogs.Write(finalEx);//写日志 //转到异常错误页面 HttpContext.Current.ApplicationInstance.Server.Transfer("url"); } #endregion }
二、AOP例子
就以MVC中权限验证作为一个例子引入,先定义一个特性BaseAuthorizeAttribute继承AuthorizeAttribute,中间可以定义其他属性来满足自己本身的业务,然后通过特性来进行拦截,在每个方法头添加[BaseAuthorize(IsHaveAuthorize=true)](属性只作为示例)则可以进行拦截。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class BaseAuthorizeAttribute : AuthorizeAttribute { public string typeName { get; set; } public string desc { get; set; } /// <summary> /// 定义属性 /// </summary> public bool IsHaveAuthorize { get; set; } public override void OnAuthorization(AuthorizationContext filterContext) { if (null == filterContext) { base.OnAuthorization(filterContext); return; } var method = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo;//当前方法 Debug("拦截到了方法:"); //权限判断(仅仅是示例) if (IsHaveAuthorize) filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Controller = "Account", action = "Index" })); else filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Controller = "Account", action = "Login" })); return; } void Debug(string message) { string folder = AppDomain.CurrentDomain.BaseDirectory + "TaskError/"; if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } StringBuilder sb = new StringBuilder(); DateTime dt = DateTime.Now; sb.AppendLine(dt.ToString("yyyy-MM-dd HH:mm:ss ") + message); Mutex m = new Mutex(false, "TaskDebug2016"); m.WaitOne(); File.AppendAllText(folder + "/" + dt.ToString("yyyyMMddHH") + ".debug.log", sb.ToString()); m.ReleaseMutex(); Thread.Sleep(100); } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[BaseAuthorize(IsHaveAuthorize=true)] public string HaveAuthorize() { return "有权限"; } [BaseAuthorize(IsHaveAuthorize = false)] public string NoAuthorize() { return "无权限"; } /// <summary> /// 不拦截 /// </summary> /// <returns></returns> public string NotIntercept() { return "不拦截"; }
三、RealProxy实现拦截
RealProxy 类是 abstract 代理必须从中派生的基类。
使用跨远程边界任何类型的对象的客户端实际上用于透明代理对象。 透明代理提供了实际对象驻留在客户端的空间内的视觉效果。 它通过将转发至真实对象使用远程处理基础结构对它进行的调用实现此目的。
透明代理是托管的运行时类型的类的实例本身 RealProxy。 RealProxy 实现从透明代理转发操作所需的功能的一部分。 请注意,代理对象继承关联的托管对象,如垃圾收集、 字段和方法,支持语义,并且可以扩展以形成新类。 代理具有双重特性︰ 它是作为远程对象 (透明代理),相同的类的对象,它是一个托管的对象本身。
1、新建代理
首先我们新建一个代理类AopRealProxy<T> 继承 RealProxy
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private T _target; public AopRealProxy(T target) : base(typeof(T)) { this._target = target; }
2、重写Invoke
然后我们重写RealProxy里的Invoke方法来执行我们拦截到的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public override IMessage Invoke(IMessage msg) { var methodCall = msg as IMethodCallMessage; var methodInfo = methodCall.MethodBase as MethodInfo; Before(msg); try { var result = methodInfo.Invoke(_target, methodCall.InArgs); After(msg); return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); } catch(Exception ex) { Excepting(msg); return new ReturnMessage(ex, methodCall); } } public void Before(IMessage msg) { Console.WriteLine("方法执行前"); } public void After(IMessage msg) { Console.WriteLine("方法执行后"); } public void Excepting(IMessage msg) { Console.WriteLine("异常执行"); }
3、获得代理
接着通过GetTransparentProxy来获得当前实例的透明代理 RealProxy
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static T Create<T>() { T instance = Activator.CreateInstance<T>(); AopRealProxy<T> aopRealProxy = new AopRealProxy<T>(instance); T aopTransparentProxy = (T)aopRealProxy.GetTransparentProxy(); return aopTransparentProxy; }
4、运行测试
定义接口和实现查看运行结果(实现类中必须继承MarshalByRefObject否则拦截不到对应的信息),异常拦截的时候不能加try..catch
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface IAnimal { string Eat(); string Run(); }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class AnimalRealize : MarshalByRefObject, IAnimal { public string Eat() { return "Dog Eat"; } public string Run() { return "Dog Run"; } }
这是一个简单的拦截,下面再结合另一种形式来实现。ProxyAttribute
四、ProxyAttribute特性
1、新建特性
新建特性AopProxyAttribute继承ProxyAttribute,自动实现RealProxy植入,重写ProxyAttribute中的CreateInstance获得代理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public override MarshalByRefObject CreateInstance(Type serverType) { var instance = base.CreateInstance(serverType); AopRealProxy realProxy = new AopRealProxy(serverType, instance); //事物入侵 //TransactionAopRealProxy realProxy = new TransactionAopRealProxy(serverType, instance); //realProxy.InterceptionTransaction(new TransactionRealize()); return realProxy.GetTransparentProxy() as MarshalByRefObject; }
2、新建代理
同样我们新建一个代理AopRealProxy继承RealProxy(重写Invoke和之前一样),注意构造函数部分第一次执行发现拦截的是构造函数的话先要进行初始化。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class AopRealProxy : RealProxy { private MarshalByRefObject _target; public AopRealProxy(Type type, MarshalByRefObject target) : base(type) { this._target = target; } /// <summary> /// /// </summary> /// <param name="msg"></param> /// <returns></returns> public override IMessage Invoke(IMessage msg) { IMethodCallMessage methodCall = (IMethodCallMessage)msg; IConstructionCallMessage ctr = methodCall as IConstructionCallMessage; IMessage message = null; InterceptionType interceptionType = GetInterceptionType(methodCall); //构造函数 if (ctr != null) { IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg); RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue); message = constructionReturnMessage; } //方法 else { if (interceptionType == InterceptionType.Before || interceptionType == InterceptionType.None) Console.WriteLine("开始执行"); try { var methodInfo = methodCall.MethodBase as MethodInfo; var result = methodInfo.Invoke(GetUnwrappedServer(), methodCall.InArgs); message= new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); if (interceptionType == InterceptionType.After || interceptionType == InterceptionType.None) Console.WriteLine("结束执行"); } catch (Exception ex) { message = new ReturnMessage(ex, methodCall); if (interceptionType == InterceptionType.Exception || interceptionType == InterceptionType.None) Console.WriteLine("异常执行"); } } return message; } /// <summary> /// 获取方法标签类型 /// </summary> private InterceptionType GetInterceptionType(IMethodCallMessage MethodCall) { InterceptionType type = InterceptionType.None; foreach (System.Attribute attr in MethodCall.MethodBase.GetCustomAttributes(false)) { MethodInterceptionAttribute methodInterceptionAttr = attr as MethodInterceptionAttribute; if (null !=methodInterceptionAttr) { type = methodInterceptionAttr.AdviceType; break; } } return type; } }
3、添加标签
新建测试类AopTestClass为类添加特性AopProxyAttribute实现拦截,类需要继承ContextBoundObject,到这一步只要执行类里面的方法在AopRealProxy里面都可以拦截得到对应的方法。但是方法应该在什么地方执行呢?可以在为方法定义一个特性在拦截的过程中判断方法想要在拦截前执行还是在拦截后执行又或者是异常的时候执行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 拦截位置 /// </summary> public enum InterceptionType { /// <summary> /// 默认 /// </summary> None, /// <summary> /// 拦截之前 /// </summary> Before, /// <summary> /// 拦截之后 /// </summary> After, /// <summary> /// 异常 /// </summary> Exception }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class MethodInterceptionAttribute : System.Attribute { private InterceptionType interceptionType = InterceptionType.None; public MethodInterceptionAttribute(InterceptionType interceptionType) { this.interceptionType = interceptionType; } public InterceptionType AdviceType { get { return this.interceptionType; } } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[AopProxyAttribute] public class AopTestClass : ContextBoundObject { public string content { get; set; } public AopTestClass(string content) { this.content = content; } public AopTestClass() { Console.WriteLine("初始化构造函数"); } [MethodInterception(InterceptionType.Before)] public string Before() { return "Before"; } [MethodInterception(InterceptionType.After)] public string After() { return "After"; } [MethodInterception(InterceptionType.Exception)] public string Exception(string content) { //Convert.ToInt32(content); return content; } }
4、运行测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
AopTestClass ap = new AopTestClass("aoptest"); string before = ap.Before(); Console.WriteLine(before); string after = ap.After(); Console.WriteLine(after);
通过这种方式我们只需要为我们业务类添加上ContextBoundObject和添加上对应的特性AopProxyAttribute就可以实现拦截了。是不是觉得很方便呢?
五、业务层的事务入侵
结合上面的例子,我们再通过一个例子来试验一下。在平时写代码的过程中,在数据库操作的时候,如果一次需要更新多个表我们应该怎么样做呢?sql;sql;这样?又或者是结合当前的业务写一个存储过程?当然这些都可以实现。结合Aop我们可以定义一个全局的事务来实现,在有关数据库操作方面的类加上对应的特性,这样就可以不用每次操作数据库都得声明事务。实现的方式是和上面讲的都差不多,只是在代理层增加了事务注入这一操作(对应之前的before和after),直接给出代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TransactionAopRealProxy : RealProxy { private MarshalByRefObject _target; private ITransaction transactionService = null; public TransactionAopRealProxy(Type type, MarshalByRefObject target) : base(type) { this._target = target; } /// <summary> /// /// </summary> /// <param name="msg"></param> /// <returns></returns> public override IMessage Invoke(IMessage msg) { IMethodCallMessage methodCall = (IMethodCallMessage)msg; IConstructionCallMessage ctr = methodCall as IConstructionCallMessage; IMessage message = null; //构造函数 if (ctr != null) { IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg); RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue); message = constructionReturnMessage; } //方法 else { transactionService.BeginTransaction(); try { var methodInfo = methodCall.MethodBase as MethodInfo; var result = methodInfo.Invoke(GetUnwrappedServer(), methodCall.InArgs); message = new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); transactionService.CommitTransaction(); } catch (Exception ex) { transactionService.RollbackTransaction(); message = new ReturnMessage(ex, methodCall); } } return message; } /// <summary> /// 事务入侵 /// </summary> /// <param name="transactionService"></param> public void InterceptionTransaction(ITransaction transactionService) { this.transactionService = transactionService; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 事务接口 /// </summary> public interface ITransaction { /// <summary> /// 开始事务 /// </summary> void BeginTransaction(); /// <summary> /// 提交事务 /// </summary> void CommitTransaction(); /// <summary> /// 事务回滚 /// </summary> void RollbackTransaction(); }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class TransactionRealize:ITransaction { private TransactionScope transaction = null; /// <summary> /// 开始事务 /// </summary> public void BeginTransaction() { transaction = new TransactionScope(); } /// <summary> /// 提交事务 /// </summary> public void CommitTransaction() { transaction.Complete(); transaction.Dispose(); } /// <summary> /// 回滚事务 /// 执行Dispose不提交事务 /// </summary> public void RollbackTransaction() { transaction.Dispose(); } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class AopProxyAttribute : ProxyAttribute { public override MarshalByRefObject CreateInstance(Type serverType) { var instance = base.CreateInstance(serverType); //AopRealProxy realProxy = new AopRealProxy(serverType, instance); //事物入侵 TransactionAopRealProxy realProxy = new TransactionAopRealProxy(serverType, instance); realProxy.InterceptionTransaction(new TransactionRealize()); return realProxy.GetTransparentProxy() as MarshalByRefObject; } }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[MethodInterception(InterceptionType.None)] public void deleteData() { string deleteSql = "DELETE FROM tblmessage WHERE ID='0aa080c81ce546efbb82d7a4fc5357ab'"; var result = MySqlDBManager.ExecuteNonQuery(deleteSql); string deleteSql2 = "DELETE FROM tblmessage WHERE ID1='5c02208f182045888df24fc19c7c31af'"; var result2 = MySqlDBManager.ExecuteNonQuery(deleteSql2); }
六、总结