非自己的网站如何做二次跳转,免费建英文网站,简单的网站设计模板下载,用wordpress做个原文地址#xff1a;http://www.uml.org.cn/net/201004213.asp 一、AOP实现初步 AOP将软件系统分为两个部分#xff1a;核心关注点和横切关注点。核心关注点更多的是Domain Logic#xff0c;关注的是系统核心的业务#xff1b;而横切关注点虽与核心的业务实现无关#xff…原文地址http://www.uml.org.cn/net/201004213.asp 一、AOP实现初步 AOP将软件系统分为两个部分核心关注点和横切关注点。核心关注点更多的是Domain Logic关注的是系统核心的业务而横切关注点虽与核心的业务实现无关但它却是一种更Common的业务各个关注点离散地分布于核心业务的多处。这意味着如果不应用AOP那么这些横切关注点所代表的业务代码就会分散在系统各处导致系统中的每个模块都与这些业务具有很强的依赖性。在这里所谓横切关注点所代表的业务即为“方面(Aspect)”常见的包括权限控制、日志管理、事务处理等等。 以权限控制为例假设一个电子商务系统需要对订单管理用户进行权限判定只有系统用户才能添加、修改和删除订单那么传统的设计方法是 public class OrderManager { private ArrayList m_Orders; public OrderManager() { m_Orders new ArrayList(); } public void AddOrder(Order order) { if (permissions.Verify(Permission.ADMIN)) { m_Orders.Add(order); } } public void RemoveOrder(Order order) { if (permissions.Verify(Permission.ADMIN)) { m_Orders.Remove(order); } } } 这样的设计其缺陷是将订单管理业务与权限管理完全结合在一起耦合度高。而在一个系统中类似的权限控制会很多这些代码就好像一颗颗毒瘤一般蔓延于系统中的各处一旦需要扩展则给程序员们带来的困难是不可估量的。 让我们来观察一下订单管理业务中的权限管理。不管是添加订单还是删除订单有关权限管理的内容是完全相同的。那么为什么我们不能将这些相同的业务抽象为一个对象并将其从订单管理业务中完全剥离出来呢在传统的OO设计思想这种设想是不能实现的。因为订单管理业务作为一个类对象它封装了诸如添加、删除订单等行为。这种封装性就决定了我们不可能切入到对象内部通过获取方法消息的形式对对象行为进行监控与操作。 AOP的思想解决了这个问题之所以称为“方面(Aspect)”就是把这些对象剖开仅获取其内部相一致的逻辑并剥离出来以“方面”的形式存在。要让这些方面能够对核心业务进行控制就需要有一套获取方法消息的机制。在.Net中其中一种技术称为动态代理。 在.Net中要实现动态代理需要用到.Net Remoting中的消息机制以及.Net Framework内部提供的ContextAttribute类来自定义自己的Attribute。另外.Net还要求调用“Aspect”的核心业务类必须继承ContextBoundObject类。只有这样我们才能截取其内部传递的方法消息。以下是相关接口和类的说明。 ContextAttribute类 该类继承了Attribute类它是一个特殊的Attribute通过它可以获得对象需要的合适的执行环境即Context上下文。它还实现了IContextAttribute和IContextProperty接口。我们自定义的Attribute将从ContextAttribute类派生。 构造函数 ContextAttribute类的构造函数带有一个参数用来设置ContextAttribute的名称。 公共属性 Name只读属性。返回ContextAttribute的名称 公共方法 GetPropertiesForNewContext虚拟方法。向新的Context添加属性集合。 IsContextOK虚拟方法。查询客户Context中是否存在指定的属性。 IsNewContextOK虚拟方法。默认返回true。一个对象可能存在多个Context,使用这个方法来检查新的Context中属性是否存在冲突。 Freeze:虚拟方法。该方法用来定位被创建的Context的最后位置。 ContextBoundObject类 这个类的对象通过Attribute来指定它所在的Context凡是进入该Context的调用都可以被拦截。该类从MarshalByRefObject派生。 IMessage:定义了被传送的消息的实现。一个消息必须实现这个接口。 IMessageSink:定义了消息接收器的接口一个消息接收器必须实现这个接口。 该接口主要提供了两个方法分别进行同步和异步操作 SyncProcessMessage(IMessage msg)接口方法当消息传递的时候该方法被调用 AsyncProcessMessage(IMessage msg, IMessageSink replySink)该方法用于异步处理 下面是实现权限控制AOP的简单实现首先我们自定义一个Attribute它继承了ContextAttribute [AttributeUsage(AttributeTargets.Class)] public class AOPAttribute:ContextAttribute { public AOPAttribute() : base(AOP) { } public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg) { ctorMsg.ContextProperties.Add(new AOPProperty()); } } 在GetPropertiesForNewContext()方法中添加了AOPProperty对象它是一个上下文环境属性 public class AOPProperty : IContextProperty, IContributeObjectSink { public AOPProperty() { } #region IContributeObjectSink Members public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink) { return new AOPSink(nextSink); } #endregion #region IContextProperty Members public void Freeze(Context newContext) { } public bool IsNewContextOK(Context newCtx) { return true; } public string Name { get { return AOP; } } #endregion AOPProperty属性实现了接口IContextPropertyIContributeObjectSink。GetObjectSink()方法为IContributeObjectSink接口的方法在其实现中创建了一个IMessageSink对象AOPSink该对象实现了IMessageSink接口 public class AOPSink : IMessageSink { private IMessageSink m_NextSink; public AOPSink(IMessageSink nextSink) { m_NextSink nextSink; } public IMessageSink NextSink { get { return m_NextSink; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage call msg as IMethodCallMessage; if (call null) { return null; } IMessage retMsg null; if (call.MethodName AddOrder || call.MethodName DeleteOrder) { if (permissions.Verify(Permission.ADMIN)) { retMsg m_NextSink.SyncProcessMessage(msg); } } return retMsg; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } } 在AOPSink中最重要的是SyncProcessMessage()方法在这个方法中实现了权限控制并通过IMessage截取了需要权限控制的方法。在检验了权限之后然后再执行OrderManager的AddOrder和DeleteOrder方法。 通过AOP的实现原来的OrderManager就可以修改为 [AOP] public class OrderManager: ContextBoundObject { private ArrayList m_Orders; public OrderManager() { m_Orders new ArrayList(); } public void AddOrder(Order order) { m_Orders.Add(order); } public void RemoveOrder(Order order) { m_Orders.Remove(order); } } 在上述的OderManager类中完全消除了permissions.Verify()等有关权限的代码解除了订单管理与权限管理之间的耦合。 二、与AspectJ比较 上述的方案虽然解除了订单管理与权限管理的耦合但从SyncProcessMessage()方法可以看出它的实现具有很大的局限性。试想一下这样的应用场景在订单管理系统中用户要求对修改订单的方法增加权限验证同时要求在验证权限时允许业务经理Permission.Manager也具备管理订单的权限应该怎样做仔细思考我们会发觉以上的实现未免太过死板了。 让我们来参考一下AspectJ在java中的实现。AspectJ提供了自己的一套语法其中包括aspect、pointcut、before、after等。我们可以通过aspect定义一个“方面”如上的权限管理 private static aspect AuthorizationAspect{……} pointcut为切入点在其中定义了需要截取上下文消息的方法例如 private pointcut authorizationExecution(): execution(public void OrderManager.AddOrder(Order)) || execution(public void OrderManager.DeleteOrder(Order)) || execution(public void OrderManager.UpdateOrder(Order)); 由于权限验证是在订单管理方法执行之前完成因此在before中定义权限检查 before(): authorizationExecution() { if !(permissions.Verify(Permission.ADMIN)) { throw new UnauthorizedException(); } } 从上述AspectJ的实现中我们可以看到要定义自己的aspect是非常容易的而通过pointcut的方式可以将需要截取消息的方法集中在一起。before和after则是具体的方面执行的逻辑它们就好像Decorator模式那样对原有方法进行了一层装饰从而达到将aspect代码植入的目的。 另外AspectJ还提供了更简单的语法可以简化前面pointcut中一系列方法的列举 private pointcut authorizationExecution(): execution (public * OrderManager.*(.)) AspectJ在应用AOP领域已经非常成熟。它提供了自成一体的特有AspectJ语法并需要专门的java编译器使用起来较为复杂。那么在.Net下可否实现类似AspectJ的功能呢我想由于.Net与java在很多技术的相似性它们彼此之间在很多领域是相通的因此要达到这一目标应该是可行的。事实上开源项目中的Aspect#就与AspectJ相似。 事实上如果我们利用前面描述的动态代理机制辅以设计模式的OO设计方法直接在代码中也可以实现AspectJ中的部分AOP特性。 三、.Net中AOP的深入实现 我们先分析AspectJ中的pointcut和.Net中的SyncProcessMessage()方法。Pointcut可以添加一系列需要截取上下文的方法那么在.Net中我们也可以利用集合动态地添加方法并创建这些方法与“方面”的映射。同样的AspectJ中的before和after是“方面”的核心实现那么在.Net中我们也可以利用委托使其对应相关的方法来实现其核心逻辑。 结合动态代理的知识我们先定义两个委托分别代表before和after操作 public delegate void BeforeAOPHandle(IMethodCallMessage callMsg); public delegate void AfterAOPHandle(IMethodReturnMessage replyMsg); BeforeAOPHandle中的参数callMsg其值为要截取上下文的方法的消息AfterAOPHandle中的参数replyMsg则是该方法执行后返回的消息。 接下来定义一个抽象基类AOPSink它实现了IMessageSink接口 public abstract class AOPSink : IMessageSink { private SortedList m_BeforeHandles; private SortedList m_AfterHandles; private IMessageSink m_NextSink; } 在类AOPSink中定义了两个SortedList类型的字段m_BeforeHandles和m_AfterHandles。它们负责存放方法名与BeforeAOPHandle和AfterAOPHandle对象之间的映射。添加这些映射的职责由如下两个方法完成 protected virtual void AddBeforeAOPHandle(string methodName, BeforeAOPHandle beforeHandle) { lock (this.m_BeforeHandles) { if (!m_BeforeHandles.Contains(methodName)) { m_BeforeHandles.Add(methodName, beforeHandle); } } } protected virtual void AddAfterAOPHandle(string methodName, AfterAOPHandle afterHandle) { lock (this.m_AfterHandles) { if (!m_AfterHandles.Contains(methodName)) { m_AfterHandles.Add(methodName, afterHandle); } } } 考虑到我们要截取的方法可能会有多个因此在类AOPSink中又定义了两个抽象方法负责添加所有的映射关系 protected abstract void AddAllBeforeAOPHandles(); protected abstract void AddAllAfterAOPHandles(); 然后在构造函数中我们初始化两个SortedList对象并调用上述的两个抽象方法 public AOPSink(IMessageSink nextSink) { m_NextSink nextSink; m_BeforeHandles new SortedList(); m_AfterHandles new SortedList(); AddAllBeforeAOPHandles(); AddAllAfterAOPHandles(); } 为了能够根据方法名获得相对应的委托对象我们又定义了两个Find方法。考虑到可能会有多个用户同时调用在这两个方法中我利用lock避免了对象的争用 protected BeforeAOPHandle FindBeforeAOPHandle(string methodName) { BeforeAOPHandle beforeHandle; lock (this.m_BeforeHandles) { beforeHandle (BeforeAOPHandle)m_BeforeHandles[methodName]; } return beforeHandle; } protected AfterAOPHandle FindAfterAOPHandle(string methodName) { AfterAOPHandle afterHandle; lock (this.m_AfterHandles) { afterHandle (AfterAOPHandle)m_AfterHandles[methodName]; } return afterHandle; } 接下来是IMessageSink接口要求实现的方法和属性 public IMessageSink NextSink { get { return m_NextSink; } } public IMessage SyncProcessMessage(IMessage msg) { IMethodCallMessage call msg as IMethodCallMessage; string methodName call.MethodName.ToUpper(); BeforeAOPHandle beforeHandle FindBeforeAOPHandle(methodName); if (beforeHandle ! null) { beforeHandle(call); } IMessage retMsg m_NextSink.SyncProcessMessage(msg); IMethodReturnMessage replyMsg retMsg as IMethodReturnMessage; AfterAOPHandle afterHandle FindAfterAOPHandle(methodName); if (afterHandle ! null) { afterHandle(replyMsg); } return retMsg; } public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; } 需要注意的是SyncProcessMessage()方法。在该方法中通过FindBeforeAOPHandle()和FindAfterAOPHandle()方法找到BeforeAOPHandle和AfterAOPHandle委托对象并执行它们。即执行这两个委托对象具体指向的方法类似与AspectJ中的before和after的execution。 现在我们就可以象AspectJ那样定义自己的aspect了。如权限管理一例我们定义一个类AuthorizationAOPSink它继承了AOPSink public class AuthorizationAOPSink : AOPSink { public AuthorizationAOPSink(IMessageSink nextSink) : base(nextSink) { } } 然后在这个方法中实现before和after的逻辑。注意before和after方法应与之前定义的委托BeforeAOPHandle和AfterAOPHandle一致。不过以本例而言并不需要实现after逻辑 private void Before_Authorization(IMethodCallMessage callMsg) { if (callMsg null) { return; } if (!permissions.Verify(Permission.ADMIN)) { throw UnauthorizedException(); } } 然后我们override基类中的抽象方法AddAllBeforeAOPHandles()和AddAllAfterAOPHandles() protected override void AddAllBeforeAOPHandles() { AddBeforeAOPHandle(ADDORDER, new BeforeAOPHandle(Before_Authorization)); AddBeforeAOPHandle(DELETEORDER, new BeforeAOPHandle(Before_Authorization)); } protected override void AddAllAfterAOPHandles() { } 因为after逻辑不需要实现因此重写AddAllAfterAOPHandles()时使其为空就可以了必须重写因为该方法为抽象方法。在AOPProperty类中需要返回IMessageSink对象所以还应修改原来的AOPProperty类中的GetObjectSink方法 public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink) { return new AOPSink(nextSink); return new AuthorizationAOPSink(nextSink); } 比较一下上述的实现方案自定义的继承AOPSink类的AuthorizationAOPSink就相当于AspectJ中的aspect。而与BeforeAOPHandle和AfterAOPHandle委托对应的方法则相当于AspectJ的before和after语法。AddAllBeforeAOPHandles()和AddAllAfterAOPHandle()则相当于AspectJ的pointcut。通过引入委托的方法使得我们的AOP实现具有了AspectJ的一些特性而这些实现是不需要专门的编译器的。 很明显如果我们要求OrderManager类中新增的UpdateOrder方法也要加入权限控制那么我们可以在AddAllBeforeAOPHandles()方法中增加UpdaeOrder方法与before逻辑的映射 AddBeforeAOPHandle(UPDATEORDER, Before_Authorization); 同样的如果要对权限控制进行修改开发业务经理对订单管理的权限那么也只需要修改Before_Authorization()方法 private void Before_Authorization(IMessage callMsg) { IMethodCallMessage call callMsg as IMethodCallMessage; if (call null) { return; } if (!(permissions.Verify(Permission.ADMIN)|| permissions.Verify(Permission.MANAGER))) { throw UnauthorizedException(); } } 四、进一步完善 由于我们的委托列表m_BeforeHandles和m_AfterHandles为SortedList类型因此作为key的methodName必须是唯一的。如果系统要求添加其他权限控制的逻辑例如增加认证功能就不能再在AuthorizationAOPSink类的AddAllBeforeAOPHandles()方法中增加方法名与认证功能的before逻辑之间的映射了。 private void Before_Authentication(IMessage callMsg){……} protected override void AddAllBeforeAOPHandles() { …… AddBeforeAOPHandle(ADDORDER, new BeforeAOPHandle(Before_ Authentication)); AddBeforeAOPHandle(DELETEORDER, new BeforeAOPHandle(Before_ Authentication)); } 如果在AuthorizationAOPSink类中添加上面的代码由于新增的“ADDORDER”key与前面重复故执行程序时是找不到相应的委托Before_Authentication的。 解决的办法就是为认证功能新定义一个aspect。由于在本方案中实现AOP功能的不仅仅是实现了IMessageSink接口的AOPSink类同时该类还与Property、Attribute有关。也就是说如果我们新定义一个AuthenticationAOPSink那么还要定义与之对应的AuthenticationAOPProperty类。为便于扩展我采用了Template Method模式为所有的property定义了抽象类AOPProperty其中的抽象方法或虚方法则留待其子类来实现。 public abstract class AOPProperty : IContextProperty, IContributeObjectSink { protected abstract IMessageSink CreateSink(IMessageSink nextSink); protected virtual string GetName() { return AOP; } protected virtual void FreezeImpl(Context newContext) { return; } protected virtual bool CheckNewContext(Context newCtx) { return true; } #region IContributeObjectSink Members public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink) { return CreateSink(nextSink); } #endregion #region IContextProperty Members public void Freeze(Context newContext) { FreezeImpl(newContext); } public bool IsNewContextOK(Context newCtx) { return CheckNewContext(newCtx); } public string Name { get { return GetName(); } } #endregion } 与原来的AOPProperty类相比IContextPropertyIContributeObjectSink接口的方法与属性都没有直接实现而是在其内部调用了相关的抽象方法和虚方法。包括抽象方法CreateSink()虚方法FreezeImpl()CheckNewContext()以及GetName()。对于其子类而言需要override的主要是抽象方法CreateSink()和GetName()因为Property的Name必须是唯一的至于其他虚方法可以根据需要选择是否override。例如自定义权限控制的属性类AuthorizationAOPProperty public class AuthorizationAOPProperty :AOPProperty { protected override IMessageSink CreateSink(IMessageSink nextSink) { return new AuthorizationAOPSink(nextSink); } protected override string GetName() { return AuthorizationAOP; } } 在该类中我们override了CreateSink()方法创建了一个AuthorizationAOPSink对象。同时override了虚方法GetName返回了自己的一个名字“AuthorizationAOP”。 关于Attribute类观察其方法GetPropertiesForNewContext()其实现是在IConstructionCallMessage消息的上下文property中添加自定义property。这些property组成了一个链它是可以静态添加的。鉴于此我们可以采取两种策略 1、 所有的aspect都使用同一个Attribute。其实现如下 [AttributeUsage(AttributeTargets.Class)] public class AOPAttribute:ContextAttribute { public AOPAttribute() : base(AOP) { } public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg) { ctorMsg.ContextProperties.Add(new AuthorizationAOPProperty()); ctorMsg.ContextProperties.Add(new AuthenticationAOPProperty()); } } 在方法GetPropertiesForNewContext()中添加多个自定义Property。在添加Property时需要注意添加Property的顺序。 2、 不同的aspect使用不同的Attribute。此时可以为这些Attribute定义一个共同的抽象基类AOPAttribute [AttributeUsage(AttributeTargets.Class)] public abstract class AOPAttribute:ContextAttribute { public AOPAttribute() : base(AOP) { } public sealed override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg) { ctorMsg.ContextProperties.Add(GetAOPProperty()); } protected abstract AOPProperty GetAOPProperty(); } 注我将GetPropertiesForNewContext()方法sealed目的是不需要其子类在重写该方法。 继承AOPAttribute类的子类只需要重写GetAOPProperty()方法即可。但在为OrderManager类定义Attribute的时候需注意其顺序。如以下的顺序 [AuthorizationAOP] [AuthenticationAOP] public class OrderManager{} 此时AuthorizationAOPAttribute在前AuthenticationAOPAttribute在后。如果以Decorator的角度来看对被装饰的方法AuthorizationAOPAttribute在内AuthenticationAOPAttribute在外。 考虑到aspect的应用有的方法需要多个aspect有的则只需要单个aspect所以第二个方案更佳。 五、AOP实例 接下来我通过一个实例介绍AOP的具体实现。假定我们要设计一个计算器它能提供加法和减法功能。我们希望在计算过程中能够通过日志记录整个计算过程及其结果同时需要监测其运算性能。该例中核心业务是加法和减法而公共的业务则是日志与监测功能。根据前面对AOP的分析这两个功能应为我们整个系统需要剥离出来的“方面”。 我们已经拥有了一个AOP实现机制以及核心的类库包括AOPSink、AOPProperty、AOPAttribute三个抽象基类。现在我们分别为日志aspect和监测aspect定义相应的Sink、Property、Attribute。 首先是日志aspect LogAOPSink.cs: using System; using System.Runtime.Remoting.Messaging; using Wayfarer.AOP; namespace Wayfarer.AOPSample { /// summary /// Summary description for LogAOPSink. /// /summary public class LogAOPSink:AOPSink { public LogAOPSink(IMessageSink nextSink):base(nextSink) { } protected override void AddAllBeforeAOPHandles() { AddBeforeAOPHandle(ADD,new BeforeAOPHandle(Before_Log)); AddBeforeAOPHandle(SUBSTRACT,new BeforeAOPHandle(Before_Log)); } protected override void AddAllAfterAOPHandles() { AddAfterAOPHandle(ADD,new AfterAOPHandle(After_Log)); AddAfterAOPHandle(SUBSTRACT,new AfterAOPHandle(After_Log)); } private void Before_Log(IMethodCallMessage callMsg) { if (callMsg null) { return; } Console.WriteLine({0}({1},{2}),callMsg.MethodName,callMsg.GetArg(0),callMsg.GetArg(1)); } private void After_Log(IMethodReturnMessage replyMsg) { if (replyMsg null) { return; } Console.WriteLine(Result is {0},replyMsg.ReturnValue); } } } LogAOPProperty.cs using System; using Wayfarer.AOP; using System.Runtime.Remoting.Messaging; namespace Wayfarer.AOPSample { /// summary /// Summary description for LogAOPProperty. /// /summary public class LogAOPProperty:AOPProperty { protected override IMessageSink CreateSink(IMessageSink nextSink) { return new LogAOPSink(nextSink); } protected override string GetName() { return LogAOP; } } } LogAOPAttribute.cs: using System; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Contexts; using Wayfarer.AOP; namespace Wayfarer.AOPSample { /// summary /// Summary description for LogAOPAttribute. /// /summary [AttributeUsage(AttributeTargets.Class)] public class LogAOPAttribute:AOPAttribute { protected override AOPProperty GetAOPProperty() { return new LogAOPProperty(); } } } 然后再定义监测aspect MonitorAOPSink.cs using System; using System.Runtime.Remoting.Messaging; using Wayfarer.AOP; namespace Wayfarer.AOPSample { /// summary /// Summary description for MonitorAOPSink. /// /summary public class MonitorAOPSink:AOPSink { public MonitorAOPSink(IMessageSink nextSink):base(nextSink) { } protected override void AddAllBeforeAOPHandles() { AddBeforeAOPHandle(ADD,new BeforeAOPHandle(Before_Monitor)); AddBeforeAOPHandle(SUBSTRACT,new BeforeAOPHandle(Before_Monitor)); } protected override void AddAllAfterAOPHandles() { AddAfterAOPHandle(ADD,new AfterAOPHandle(After_Monitor)); AddAfterAOPHandle(SUBSTRACT,new AfterAOPHandle(After_Monitor)); } private void Before_Monitor(IMethodCallMessage callMsg) { if (callMsg null) { return; } Console.WriteLine(Before {0} at {1},callMsg.MethodName,DateTime.Now); } private void After_Monitor(IMethodReturnMessage replyMsg) { if (replyMsg null) { return; } Console.WriteLine(After {0} at {1},replyMsg.MethodName,DateTime.Now); } } } MonitorAOPProperty.cs using System; using Wayfarer.AOP; using System.Runtime.Remoting.Messaging; namespace Wayfarer.AOPSample { /// summary /// Summary description for MonitorAOPProperty. /// /summary public class MonitorAOPProperty:AOPProperty { public MonitorAOPProperty() { // // TODO: Add constructor logic here // } protected override IMessageSink CreateSink(IMessageSink nextSink) { return new MonitorAOPSink(nextSink); } protected override string GetName() { return MonitorAOP; } } } MonitorAOPAttribute.cs using System; using System.Runtime.Remoting.Activation; using System.Runtime.Remoting.Contexts; using Wayfarer.AOP; namespace Wayfarer.AOPSample { /// summary /// Summary description for MonitorAOPAttribute. /// /summary [AttributeUsage(AttributeTargets.Class)] public class MonitorAOPAttribute:AOPAttribute { protected override AOPProperty GetAOPProperty() { return new MonitorAOPProperty(); } } } 注意在这两个方面中各自的Property的Name必须是唯一的。 现在可以定义计算器类。 Calculator.cs using System; namespace Wayfarer.AOPSample { /// summary /// Summary description for Calculator. /// /summary [MonitorAOP] [LogAOP] public class Calculator:ContextBoundObject { public int Add(int x,int y) { return x y; } public int Substract(int x,int y) { return x - y; } } } 需要注意的是Calculator类必须继承ContextBoundObject类。 最后我们写一个控制台程序来执行Calculator Program.cs: using System; namespace Wayfarer.AOPSample { /// summary /// Summary description for Class1. /// /summary class Program { /// summary /// The main entry point for the application. /// /summary [STAThread] static void Main (string[] args) { Calculator cal new Calculator(); cal.Add(3,5); cal.Substract(3,5); Console.ReadLine(); } } } 运行结果如下 六、结论 在.Net平台下采用动态代理技术实现AOP其原理并不复杂而.Net Framework也提供了足够的技术来实现它。如果再结合好的设计模式提供一个基本的AOP框架将大大地简化开发人员处理“aspect”的工作。当然本文虽然提供了实现AOP的实例但其架构的设计还远远不能达到企业级的要求如在稳定性、可扩展性上还需经过进一步的测试与改善。例如我们可以通过配置文件的形式来配置方法与方面之间的映射。同时由于采用了动态代理在性能上还期待改进。 使用动态代理技术实现AOP对实现AOP的类有一个限制就是必须派生于ContextBoundObject类这对于单继承语言来说确实是一个比较致命的缺陷。所谓“仁者见仁智者见智”这就需要根据项目的情况做出正确的抉择了。 参考 1、 JGTM《A Taste of AOP from Solving Problems with OOP and Design Patterns》 2、 NiWalker《Attribute在.Net编程的应用》 3、板桥里人《AOP与权限控制实现》 在《在.Net中关于AOP的实现》我通过动态代理的技术基本上实现了AOP的几个技术要素包括aspectadvicepointcut。在文末我提到采用配置文件方式来获取advice和pointcut之间的映射从而使得构建aspect具有扩展性。 细细思考这个问题我发现使用delegate来构建advice似乎并非一个明智的选择。我在建立映射关系时是将要拦截的方法名和拦截需要实现的aspect逻辑建立一个对应关系而该aspect逻辑确实可以通过delegate使其指向一族方法签名与该委托完全匹配的方法。这使得advice能够抽象化以便于具体实现的扩展。然而委托其实现毕竟是面向过程的范畴虽然在.Net下delegate本身仍是一个类对象然而在创建具体的委托实例时仍然很难通过配置文件和反射技术来获得。 考虑到委托具有的接口抽象的本质也许采用接口的方式来取代委托更为可行。在之前的实现方案中我为advice定义了两个委托 public delegate void BeforeAOPHandle(IMethodCallMessage callMsg); public delegate void AfterAOPHandle(IMethodReturnMessage replyMsg); 我可以定义两个接口IBeforeAction和IAfterAction分别与这两个委托相对应 public interface IBeforeAdvice { void BeforeAdvice(IMethodCallMessage callMsg); } public interface IAfterAdvice { void AfterAdvice(IMethodReturnMessage returnMsg); } 通过定义的接口可以将Advice与Aspect分离开来这也完全符合OO思想中的“责任分离”原则。 注为什么要为Advice定义两个接口这是考虑到有些Aspect只需要提供Before或After两个逻辑之一如权限控制就只需要before Action。 那么当类库使用者要定义自己的Aspect时就可以定义具体的Advice类来实现这两个接口以及具体的Advice逻辑了。例如之前提到的日志Aspect public class LogAdvice:IAfterAdvice,IBeforeAdvice { #region IBeforeAdvice Members public void BeforeAdvice(IMethodCallMessage callMsg) { if (callMsg null) { return; } Console.WriteLine({0}({1},{2}), callMsg.MethodName, callMsg.GetArg(0), callMsg.GetArg(1)); } #endregion #region IAfterAdvice Members public void AfterAdvice(IMethodReturnMessage returnMsg) { if (returnMsg null) { return; } Console.WriteLine(Result is {0}, returnMsg.ReturnValue); } #endregion } 而在AOPSink类的派生类中添加方法名与Advice映射关系此映射关系我们即可理解为AOP的pointcut时就可以添加实现了Advice接口的类对象如 public override void AddAllBeforeAdvices() { AddBeforeAdvice(ADD,new LogAdvice()); AddBeforeAdvice(SUBSTRACT, new LogAdvice()); } public override void AddAllAfterAdvices() { AddAfterAdvice(ADD,new LogAdvice()); AddAfterAdvice(SUBSTRACT, new LogAdvice()); } 由于LogAdvice类实现了接口IBeforeAdvice和IAfterAdvice因此诸如new LogAdvice的操作均可以通过反射来创建该实例如 IBeforeAdvice beforeAdvice (IBeforeAdvice)Activator.CreateInstance(Wayfarer.AOPSample,Wayfarer.AOPSample.LogAdvice).Unwrap(); 而CreateInstance()方法的参数值是完全可以通过配置文件来配置的 aop aspect value LOG advice typebefore assemblyWayfarer.AOPSample classWayfarer.AOPSample.LogAdvice pointcutADD/pointcut pointcutSUBSTRACT/pointcut /advice advice typeafter assemblyWayfarer.AOPSample classWayfarer.AOPSample.LogAdvice pointcutADD/pointcut pointcutSUBSTRACT/pointcut /advice /aspect /aop 这无疑改善了AOP实现的扩展性。 《在.Net中关于AOP的实现》实现AOP的方案要求包含被拦截方法的类必须继承ContextBoundObject。这是一个比较大的限制。不仅如此ContextBoundObject对程序的性能也有极大的影响。我们可以做一个小测试。定义两个类其中一个类继承ContextBoundObject。它们都实现了一个累加的操作 class NormalObject { public void Sum(int n) { int sum 0; for (int i 1; i n; i) { sum i; } Console.WriteLine(The result is {0},sum); Thread.Sleep(10); } } class MarshalObject:ContextBoundObject { public void Sum(int n) { int sum 0; for (int i 1; i n; i) { sum i; } Console.WriteLine(The result is {0}, sum); Thread.Sleep(10); } } 然后执行这两个类的Sum()方法测试其性能 class Program { static void Main (string[] args) { long normalObjMs, marshalObjMs; Stopwatch watch new Stopwatch(); NormalObject no new NormalObject(); MarshalObject mo new MarshalObject(); watch.Start(); no.Sum(1000000); watch.Stop(); normalObjMs watch.ElapsedMilliseconds; watch.Reset(); watch.Start(); mo.Sum(1000000); watch.Stop(); marshalObjMs watch.ElapsedMilliseconds; watch.Reset(); Console.WriteLine(The normal object consume {0} milliseconds.,normalObjMs); Console.WriteLine(The contextbound object consume {0} milliseconds.,marshalObjMs); Console.ReadLine(); } } 得到的结果如下 从性能的差异看两者之间的差距是比较大的。如果将其应用在企业级的复杂逻辑上这种区别就非常明显了对系统带来的影响也是非常巨大的。 另外在《在.Net中关于AOP的实现》文章后有朋友发表了很多中肯的意见。其中有人提到了AOPAttribute继承ContextAttribute的问题。评论中提及微软在以后的版本中不再提供ContextAttribute。如果真是如此确有必要放弃继承ContextAttribute的形式。不过在.Net中除了ContextAttribute之外还提供有一个接口IContextAttribute该接口的定义为 public interface IContextAttribute { void GetPropertiesForNewContext(IConstructionCallMessage msg); bool IsContextOK(Context ctx, IConstructionCallMessage msg); } 此时只需要将原来的AOPAttribute实现该接口即可 public abstract class AOPAttribute:Attribute,IContextAttribute//ContextAttribute { #region IContextAttribute Members public void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg) { AOPProperty property GetAOPProperty(); property.AspectXml m_AspectXml; property.AspectXmlFlag m_AspectXmlFlag; ctorMsg.ContextProperties.Add(property); } public bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg) { return false; } #endregion } 不知道IContextAttribute似乎也会在未来的版本中被取消呢 然而从总体来看这种使用ContextBoundObject的方式是不太理想的也许它只能停留在实验室阶段或许期待微软在未来的版本中得到更好的解决 当然如果采用Castle的DynamicProxy技术可以突破必须继承CotextBoundObject的局限但随着而来的局限却是AOP拦截的方法要求必须是virtual的。坦白说这样的限制不过与前者乃“五十步笑百步”的区别而已。我还是期待有更好的解决方案。 说到AOP的几大要素在这里可以补充说说它主要包括 1、Cross-cutting concern 在OO模型中虽然大部份的类只有单一的、特定的功能但它们通常会与其他类有着共同的第二需求。例如当线程进入或离开某个方法时我们可能既要在数据访问层的类中记录日志又要在UI层的类中记录日志。虽然每个类的基本功能极然不同但用来满足第二需求的代码却基本相同。 2、Advice 它是指想要应用到现有模型的附加代码。例如在《在.Net中关于AOP的实现》的例子中是指关于打印日志的逻辑代码。 3、Point-cut 这个术语是指应用程序中的一个执行点在这个执行点上需要采用前面的cross-cutting concern。如例子中执行Add()方法时出现一个Point-cut当方法执行完毕离开方法时又出现另一个Point-cut。 4、Aspect Point-cut和advice结合在一起就叫做aspect。如例子中的Log和Monitor。在对本例的重构中我已经AOPSink更名为Aspect相应的LogAOPSink、MonitorAOPSink也更名为LogAspectMonitorAspect。 以上提到的PointCut和Advice在AOP技术中通常称为动态横切技术。与之相对应的是较少被提及的静态横切。它与动态横切的区别在于它并不修改一个给定对象的执行行为相反它允许通过引入附加的方法属性和字段来修改对象固有的结构。在很多AOP实现中将静态横切称为introduce或者mixin。 在开发应用系统时如果需要在不修改原有代码的前提下引入第三方产品和API库静态横切技术是有很大的用武之地的。从这一点来看它有点类似于设计模式中提到的Adapter模式需要达到的目标。不过看起来静态横切技术应比Adapter模式更加灵活和功能强大。 例如一个已经实现了收发邮件的类Mail。然而它并没有实现地址验证的功能。现在第三方提供了验证功能的接口IValidatable public interface IValidatable { bool ValidateAddress(); } 如果没有AOP采用设计模式的方式在不改变Mail类的前提下可以通过Adapter模式引入MailAdater继承Mail类同时实现IValidatable接口。采用introduce技术却更容易实现该功能的扩展我们只需要定义aspect注java代码使用了AspectJ import com.acme.validate.Validatable; public aspect EmailValidateAspect { declare parents: Email implements IValidatable; public boolean Email.validateAddress(){ if(this.getToAddress() ! null){ return true; }else{ return false; } } } 从上可以看到通过EmailValidateAspect方面为Email类introduce了新的方法ValidateAddress()。非常容易的就完成了Email的扩展。 我们可以比较一下如果采用Adapter模式原有的Email类是不能被显示转换为IValidatable接口的也即是说如下的代码是不可行的 Email mail new Email(); IValidatable validate ((IValidatable)mail).ValidateAddress(); 要调用ValidateAddress()方法必须通过EmailAdapter类。然而通过静态横切技术上面的代码就完全可行了。 静态横切的技术在企业应用上还需要进一步验证和测试不过遗憾的是《在.Net中关于AOP的实现》一文采用的动态代理技术是无法完成实现静态横切的目标的。转载于:https://www.cnblogs.com/wangwangfei/p/4387643.html