.Net基于RealProxy实现AOP
一、概述
关于AOP(面向切面编程)还是先讲一个日常经常碰到的场景“错误日志的记录”,一般来说我们编码的时候想记录错误日志都是用try..catch来进行捕捉和记录,慢慢的你会发现你每一个方法都得加try..catch才能记录每一个方法的错误日志。而有什么办法能做一个统一的拦截呢?做webform的时候可能会用到IHttpModule去实现,也有的会继承HttpApplication去做异常拦截。但这并不能应用在所有的项目场景中,而且这两者针对的对象也不是方法而是http请求。在面对这些场景中AOP就派上用场了。AOP在园子里也不算是一个陌生的话题了,关于AOP理论的文章也是挺多的,也有不少开源的代码,可以很好的帮组我们了解这方面的内容。这段时间自己也是在了解这方面的东西趁着周末的时间就和大家分享一下这方面的内容,代码不高深,写得不好也别吐槽!
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 }
HttpModule拦截日志
二、AOP例子
就以MVC中权限验证作为一个例子引入,先定义一个特性BaseAuthorizeAttribute继承AuthorizeAttribute,中间可以定义其他属性来满足自己本身的业务,然后通过特性来进行拦截,在每个方法头添加[BaseAuthorize(IsHaveAuthorize=true)](属性只作为示例)则可以进行拦截。
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();
}
}
BaseAuthorizeAttribute
[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
private T _target;
public AopRealProxy(T target) : base(typeof(T))
{
this._target = target;
}
2、重写Invoke
然后我们重写RealProxy里的Invoke方法来执行我们拦截到的方法
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, , 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
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
public interface IAnimal
{
string Eat(); string Run();
}
接口
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获得代理
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和之前一样),注意构造函数部分第一次执行发现拦截的是构造函数的话先要进行初始化。
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, , 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里面都可以拦截得到对应的方法。但是方法应该在什么地方执行呢?可以在为方法定义一个特性在拦截的过程中判断方法想要在拦截前执行还是在拦截后执行又或者是异常的时候执行。
/// <summary>
/// 拦截位置
/// </summary>
public enum InterceptionType
{
/// <summary>
/// 默认
/// </summary>
None,
/// <summary>
/// 拦截之前
/// </summary>
Before,
/// <summary>
/// 拦截之后
/// </summary>
After,
/// <summary>
/// 异常
/// </summary>
Exception
}
特性枚举
public class MethodInterceptionAttribute : System.Attribute
{
private InterceptionType interceptionType = InterceptionType.None;
public MethodInterceptionAttribute(InterceptionType interceptionType)
{
this.interceptionType = interceptionType;
} public InterceptionType AdviceType
{
get { return this.interceptionType; }
}
}
方法特性
[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、运行测试
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),直接给出代码。
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, , 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;
}
}
新建代理
/// <summary>
/// 事务接口
/// </summary>
public interface ITransaction
{
/// <summary>
/// 开始事务
/// </summary>
void BeginTransaction(); /// <summary>
/// 提交事务
/// </summary>
void CommitTransaction(); /// <summary>
/// 事务回滚
/// </summary>
void RollbackTransaction();
}
定义事务接口
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();
}
}
事务实现
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;
}
}
获得代理
[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);
}
代码测试
六、总结
.Net基于RealProxy实现AOP的更多相关文章
- .Net中的RealProxy实现AOP
序言 这个AOP要从我们公司的一个事故说起,前段时间公司的系统突然在乌云中出现,数据被泄露的一览无余,乌云上显示是SQL注入攻击.呵,多么贴近生活的一个露洞,可谓是人尽皆知啊.然而却华丽丽的给拉我们一 ...
- 基于Schema的AOP 配置使用详解
原文地址:http://jinnianshilongnian.iteye.com/blog/1418598 基于Schema的AOP从Spring2.0之后通过"aop"命名空间来 ...
- 开涛spring3(6.3) - AOP 之 6.3 基于Schema的AOP
6.3 基于Schema的AOP 基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面.切入点及声明通知. 在Spring配置文件中,所以AOP相关定义必须放在<a ...
- spring aop 基于schema的aop
AOP的基本概念: 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行.方法调用.字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP ...
- Spring_Spring与AOP_AspectJ基于注解的AOP实现
一.AspectJ.Spring与AOP的关系 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Cl ...
- spring声明式事务管理方式( 基于tx和aop名字空间的xml配置+@Transactional注解)
1. 声明式事务管理分类 声明式事务管理也有两种常用的方式, 一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解. 显然基于注解的方式更简单易用,更清爽. ...
- spring3: 基于Schema的AOP
6.3 基于Schema的AOP 基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面.切入点及声明通知. 在Spring配置文件中,所以AOP相关定义必须放在<a ...
- 基于XML的AOP配置
创建spring的配置文件并导入约束 此处要导入aop的约束 <?xml version="1.0" encoding="UTF-8"?> < ...
- 利用基于@AspectJ的AOP实现权限控制
一. AOP与@AspectJ AOP 是 Aspect Oriented Programming 的缩写,意思是面向方面的编程.我们在系统开发中可以提取出很多共性的东西作为一个 Aspect,可以理 ...
随机推荐
- Spring IOC 之Bean定义的继承
一个Bean的定义可以包含大量的配置信息,包括构造器参数.属性值以及容器规范信息,比如初始化方法.静态工厂方法名字等等.一子bean的定义可以从父bean的定义中继承配置数据信息.子bean定义可以覆 ...
- CSS学习笔记之元素分类
在讲解CSS布局之前,我们需要提前知道一些知识,在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div> ...
- 宿主机跟Vmware中的linux使用host-only,bridge 方式通信
声明:我的机器在本文中叫 宿机,vmware中的linux就叫linux 本文已经实现了通过host-only模式 宿机 ping通 linux,但是在host-only模式下,linux 没有pin ...
- Winform无边框窗体(FormBorderStyle属性设为None)自定义移动
为了界面的好看,有时候需要将窗体FormBorderStyle属性设为None,这样就可以根据自己的喜欢来设计界面.但这样窗体无法进行移动的.而且默认的窗体(FormBorderStyle=Sizab ...
- VS2012下systemC配置
一.编译System库 1.下载SystemC library source code 到http://www.systemc.org注册会员账号后,即可下载SystemC ...
- test maekdown 2
Package Control Messages Markdown Preview: Sublime Text 2/3 Markdown Preview ======================= ...
- c++ virtual function 虚函数面试题
下面的代码输出什么? #include<iostream> using namespace std; class A { public: virtual void foo() { cout ...
- Hadoop能力测试图谱
一张图测试你的Hadoop能力-Hadoop能力测试图谱 1.引言 看到一张图,关于Hadoop技术框架的图,基本上涉及到Hadoop当前应用的主要领域,感觉可以作为测试Hadoop开发人员当前能力和 ...
- MongoDB学习(翻译5)
C#驱动序列化文档对象 介绍 本文档基于C#官方驱动1.8版本. 本节C#驱动教程谈论C#类到BSON对象的序列化和反序列化.序列化是映射一个对象到可保存到MongoDB库中BSON对象的过程,反序列 ...
- Mahout之(三)相似性度量
User CF 和 Item CF 都依赖于相似度的计算,因为只有通过衡量用户之间或物品之间的相似度,才能找到用户的“邻居”,才能完成推荐.上文简单的介绍了相似性的计算,但不完全,下面就对常用的相似度 ...