浅谈C#关于AOP编程的学习总结
难得在这样一个节日里给写出一篇博客,却没有佳人相约,没办法,这就是一个程(dan)序(shen)猿(gou)的真实生活情景,每天除了coding还是coding。唉..污染各位看官的眼了。好吧,进入正题,最近做的项目各种验证,异常控制,特别的麻烦,刚好前几天听到一个同事说起AOP 然后就想学习下应用到项目中去,自己也是在网上找了很多的资料,之后发现 网上的大部分资料的内容几乎都是如出一撤,于是自己就整理了一下再加上这几天的学习总结到的知识写到这篇文章中,首先带大家了解一下AOP,AOP(面向切面编程)在Java中应该算比较常见的,.net呢很少有人谈起吧,AOP主要用于做权限控制,日志,事物,拦截和记录(本篇总结中我不会说太多有关事务的处理,这不是重点),我觉得AOP最大的不同其实是,在不增加代码的基础上,还增加新的功能,
在简单的业务逻辑方法中我们可能是这样写的
方法()
{
逻辑处理.......
逻辑处理.......
逻辑处理.......
}
要是复杂的业务逻辑呢,可能会这样写
方法()
{
try(异常处理.......)
{
If(权限判断.....)
记录日志......
逻辑处理.......
逻辑处理.......
逻辑处理.......
}
catch(Exception e)
{
throwe;
记录异常......
}
}
这样代码看起来就会很乱。
如果利用AOP对这些记录做处理的话,应该是这样
[判断权限,记录日志,事务,异常处理]
方法()
{
逻辑处理.......
逻辑处理.......
逻辑处理.......
}
这样看起来是不是就清晰很多了。
好了,关于AOP的作用和分析呢就写这么多,
下面开始一个简单的AOP的创作过程吧
先把我整个项目的文件列表贴出来,大家只要看第一个和第四个项目就可以了,第二个和第三个只是我写过放弃的项目 没删掉而已
首先创建一个控制台的项目 AOPconsole,然后紧接着创建一个名称是NewAop的类库
类库创建完成后
我们在newaop中创建第一个接口文件 CommonDef.cs文件,文件中定义两个接口IAopOperator
和IAopProxyFactory 代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting.Messaging; namespace NewAop { /// <summary> /// IAopOperator AOP操作符接口,包括前处理和后处理 /// </summary> public interface IAopOperator { void PreProcess(IMessage requestMsg); void PostProcess(IMessage requestMsg, IMessage Respond); } /// <summary> /// IAopProxyFactory 用于创建特定的Aop代理的实例,IAopProxyFactory的作用是使AopProxyAttribute独立于具体的AOP代理类。 /// </summary> public interface IAopProxyFactory { AopProxyBase CreateAopProxyInstance(MarshalByRefObject obj, Type type); } }
接着我们创建一个比较重要的类MethodAopSwitcherAttribute.cs,这个类中的主要做自己定义的一些参数,比如事务 日志和执行的判断条件,或者传参都是可以的,这里是与网上其他的资料有些不同的地方,所以要注意细看,需要注意的几点:1.该类中的属性和字段,可以自己定义,我们利用这些字段去存储目标方法要带过来的一些参数或者其他的记录,来做权限或者日志的判断,2.重写该类的构造函数,方便在使用的时候直接写入参数,不用重新赋值(很省事对不对)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NewAop { /// <summary> /// MethodAopSwitcherAttribute 用于决定一个被AopProxyAttribute修饰的class的某个特定方法是否启用截获 。 /// 创建原因:绝大多数时候我们只希望对某个类的一部分Method而不是所有Method使用截获。 /// 使用方法:如果一个方法没有使用MethodAopSwitcherAttribute特性或使用MethodAopSwitcherAttribute(false)修饰, /// 都不会对其进行截获。只对使用了MethodAopSwitcherAttribute(true)启用截获。 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class MethodAopSwitcherAttribute : Attribute { private int useAspect = 0; //记录类型 private string userlog = ""; //记录详细信息 public MethodAopSwitcherAttribute(int useAop,string log) { this.useAspect = useAop; this.userlog = log; } public int UseAspect { get { return this.useAspect; } } public string Userlog { get { return this.userlog; } } } }
接着新建一个AopProxyBase.cs的抽象类文件,这个类中主要用于实现要做的日志,事物权限等等的判断和目标方法的前后处理,该类需要注意的是,必须继承RealProxy接口,同时需要引入命名空间。同时要写接口中的Invoke方法。同时 在Invoke的方法中定义了两个变量useAspect 和uselog ,这里的变量是用来记录上一个类中定义的一些属性值的,主要是为了方便调用,还有,在该类中可以看到有两个抽象的方法PreProcess和PostProcess这个先不急到后面会细说,需要注意的是这段代码RemotingServices.ExecuteMessage(this.target, call);这里是运行目标方法,并作返回值。这个时候可能你会觉得,如我不运行这段代码直接返回一个NULL是不是就可以让目标方法不运行了,其实你这样写可能你编译正常,但在你运行的时候你会发现程序抛出异常了,其实这是一个重点,也许你可以用异常处理包裹你的目标方法中的代码,但这样会不会发现太不明智了,所以我们就需要使用 return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg);这段代码来做返回值了,这样写既可以不让你的返回值报错,也可以保证你的目标方法中的代码没有被执行。好了这个类就说到这里 下面贴代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting; using System.Runtime.Remoting.Proxies; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Services; using System.Runtime.Remoting.Activation; namespace NewAop { /// <summary> /// AopProxyBase 抽象类 所有自定义AOP代理类都从此类派生,覆写IAopOperator接口,实现具体的前/后处理 。 /// </summary> public abstract class AopProxyBase : RealProxy, IAopOperator { private readonly MarshalByRefObject target; //默认透明代理 #region IAopOperator 成员(两个方法 一个在执行之前调用,另一个在执行之后调用,具体实现代码写在AopControlProxy类中) public abstract void PreProcess(IMessage requestMsg); //方法执行的预处理逻辑 public abstract void PostProcess(IMessage requestMsg, IMessage Respond); //方法执行结束的处理逻辑 #endregion public AopProxyBase(MarshalByRefObject obj, Type type) : base(type) { this.target = obj; } #region Invoke 重写基方法 public override IMessage Invoke(IMessage msg) { int useAspect = 0; string uselog = ""; IMethodCallMessage call = (IMethodCallMessage)msg; //查询目标方法是否使用了启用AOP的MethodAopSwitcherAttribute foreach (Attribute attr in call.MethodBase.GetCustomAttributes(false)) { MethodAopSwitcherAttribute mehodAopAttr = attr as MethodAopSwitcherAttribute; if (mehodAopAttr != null) { useAspect = mehodAopAttr.UseAspect; uselog = mehodAopAttr.Userlog; break; //if (mehodAopAttr.UseAspect==1) //{ // useAspect = 1; // break; //} } } if (useAspect == 2) { this.PreProcess(msg); //执行方法之前的操作 } //如果触发的是构造函数,此时target的构建还未开始 IConstructionCallMessage ctor = call as IConstructionCallMessage; if (ctor != null) { //获取最底层的默认真实代理 RealProxy default_proxy = RemotingServices.GetRealProxy(this.target); default_proxy.InitializeServerObject(ctor); MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy(); //自定义的透明代理 this return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp); } if (useAspect ==2) { #region 若不想运行目标方法可以执行该代码,如果直接return null会导致异常发生 IMethodCallMessage callMsg = msg as IMethodCallMessage; return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg); #endregion //#region 调用目标方法代码 //IMethodMessage result_msg; //result_msg = RemotingServices.ExecuteMessage(this.target, call); //return result_msg; //#endregion } else { #region 调用目标方法代码 IMethodMessage result_msg; result_msg = RemotingServices.ExecuteMessage(this.target, call); //IMethodReturnMessage result_msg = RemotingServices.ExecuteMessage(this.target, call); #endregion if (useAspect == 1) { this.PostProcess(msg, result_msg); //执行方法结束后的操作 } return result_msg; } } #endregion } }
接着我们创建一个新的文件AopProxyAttribute.cs,这里于其他资料中的没有区别,不详细说明
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting; using System.Runtime.Remoting.Proxies; namespace NewAop { /// <summary> /// AopProxyAttribute /// AOP代理特性,如果一个类想实现具体的AOP,只要实现AopProxyBase和IAopProxyFactory,然后加上该特性即可。 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AopProxyAttribute : ProxyAttribute { private IAopProxyFactory proxyFactory = null; public AopProxyAttribute(Type factoryType) { this.proxyFactory = (IAopProxyFactory)Activator.CreateInstance(factoryType); } #region 创建实例 /// <summary> /// 获得目标对象的自定义透明代理 /// </summary> public override MarshalByRefObject CreateInstance(Type serverType)//serverType是被AopProxyAttribute修饰的类 { //未初始化的实例的默认透明代理 MarshalByRefObject target = base.CreateInstance(serverType); //得到位初始化的实例(ctor未执行) object[] args = { target, serverType }; //AopProxyBase rp = (AopProxyBase)Activator.CreateInstance(this.realProxyType ,args) ; //Activator.CreateInstance在调用ctor时通过了代理,所以此处将会失败 //得到自定义的真实代理 AopProxyBase rp = this.proxyFactory.CreateAopProxyInstance(target, serverType);//new AopControlProxy(target ,serverType) ; return (MarshalByRefObject)rp.GetTransparentProxy(); } #endregion } }
接着创建AopControlProxyFactory.cs创建文件
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NewAop { public class AopControlProxyFactory : IAopProxyFactory { #region IAopProxyFactory 成员 public AopProxyBase CreateAopProxyInstance(MarshalByRefObject obj, Type type) { return new AopControlProxy(obj, type); } #endregion } }
接着最关键的地方到了,创建AopControlProxy.cs文件,该类继承AopProxyBase类可以看到该类中我们实现了PreProcess和PostProcess两个方法,两个方法对应AopProxyBase类中的两个抽象方法,分别用于做目标方法执行之前和之后的操作记录,当然这里你可以继续定义新的方法,如果要实现调用那就必须要在AopProxyBase类中也有对应的抽象方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Remoting.Messaging; namespace NewAop { public class AopControlProxy:AopProxyBase { public AopControlProxy(MarshalByRefObject obj, Type type) : base(obj, type) //指定调用基类中的构造函数 { } public override void PreProcess(IMessage requestMsg) { Console.WriteLine("目标方法运行开始之前"); return; } public override void PostProcess(IMessage requestMsg, IMessage Respond) { Console.WriteLine("目标方法运行结束之后"); } } }
到此 一个用于方法代理的AOP算是完成了,那我们要怎么使用呢,暂时我只是在一个控制台系统中做了这AOP的使用实例,废话不多说,代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NewAop; namespace AOPconsole { //[AopProxyAttribute(typeof(AopControlProxyFactory))] //将自己委托给AOP代理AopControlProxy class Program { static void Main(string[] args) { //try //{ Exameplec epc = new Exameplec("添加代理的方法"); epc.say_hello(); Console.WriteLine(""); Console.WriteLine("--------------------------这是分隔符--------------------------------"); Exameplec epcs = new Exameplec("未添加代理的方法"); epcs.sayByeBye(); Console.WriteLine("--------------------------这是分隔符--------------------------------"); //} //catch //{ // Console.WriteLine("报错了"); //} Console.ReadLine(); } } //如果在类上添加该代码 会导致没有添加属性的代码也会被带入Invoke中 //导致其他方法执行IMethodCallMessage callMsg = msg as IMethodCallMessage; return new ReturnMessage(callMsg.InArgs, null, 0, null, callMsg); //最终导致其他方法的代码无法运行 [AopProxyAttribute(typeof(AopControlProxyFactory))] //将自己委托给AOP代理AopControlProxy,(最好不要添加该代码) public class Exameplec : ContextBoundObject//放到特定的上下文中,该上下文外部才会得到该对象的透明代理 { private string name; public Exameplec(string a) { this.name = a; } [MethodAopSwitcherAttribute(2,"参数")] public void say_hello() { Console.WriteLine( name); } public void sayByeBye() { Console.WriteLine( name); } } }
可以看到,我在program中添加了一个新的方法Exameplec 这里只是为了方便观看,如果要对方法进行AOP代理 那么需要在方法上加入[MethodAopSwitcherAttribute(2,"参数")]这当中的两个参数就是在MethodAopSwitcherAttribute.cs中定义的两个属性,方便传参,可以定义更多的属性,比如你可以吧session当做参数传入,尽量避免在AOP中进行一些服务端状态或者会话的获取。
一个完整的AOP案例算是完成了,总算这两天的学习不是白费功夫啊,也感谢我在网上看到的各位大神给出的共享资料。
其实我对AOP还不是完全的了解,如果各位看客有什么疑问或者建议,还希望各位留言,你们的没一个建议可能对我都是一次帮助,希望大家可以互相学习到更多的知识。