自从上次《网站性能优化之应用程序缓存-中篇》得到不少园友的支持和鼓励,并且提出了不错的思路来实现我们中篇中提到的缓存策略,那么我将会结合.NET 本身内置
的AOP的方式来跟大伙讲述和共同分析下缓存策略的简单实现,那么我们通过配置文件或者自定义配置文件来完成缓存策略的配置。本文将会给出相关的核心代码,大家也可以通
过示例代码来集成到自己的项目中,完成AOP方式的应用。简单来说我们的设计的思路是这样的。
通过上面的图形化的描述,大家应该对AOP植入的方式来完成缓存服务的集成有了简单的映像,那么下面我们会详细的分析这个具体的实现过程。
本章将主要讲解缓存中的过期策略的具体底层支撑实现,通过AOP的动态注入的方式来监测对象中的具体的事件方法,怎么样在项目中解决这样的问题,这就是本篇要讲述
的内容。主要还是通过XML的缓存配置文件来配置缓存的具体的过期依赖特性和具体的缓存对象。如果可以的话,我们其实可以考虑将缓存对象通过缓冲池来进行统一的管理,这
样就可以通过引入缓冲池替代现有的这样的缓存服务,当然缓冲池内部提供对缓存对象的生命周期的管理,我们这里可以通过一种辅助的机制来完成缓存服务。
1、上章回顾。
2、摘要。
3、本章大纲。
4、具体策略分析。
5、具体策略方案。
6、本章总结。
7、下篇预告。
我们这里主要是分析动态注入AOP与静态植入的简单区别吧:我们先要知道动态注入和静态植入是什么概念,才能更好的理解他们之间的区别和如何更好的平衡到具体的
应用中。我们这里认为大家都懂AOP是什么概念,是解决什么样的问题的,所以我这里不再详细描述,只是给出应用中可能的切面。
我们本篇将会只是讲述缓存相关的AOP实现,不过其他的方面的例子也是举一反三,可以用同样的思路来做,当然如果你有更好的思路来实现,那么请告诉我,谢谢!
下面我们来分析下动态AOP与静态AOP几个具体的实现方式:
目前比较流行的几类框架中都或多或少的使用到了AOP的优势,我们来简单的分析下静态植入与动态植入的区别:
1、动态注入一般有几种方式,根据业界提供的方式是,通过Emit的形式,为某个方法通过透明的方式新增新的行为,这里就会用到动态代理的技术。
我们通过拦截执行的方法调用,通过Emit动态的将方法重新输出,将我们想要执行的代码植入到方法中,然后完成调用,一般都是在创建对象实例的时候,即完成代码的植入。
或者是通过.NET自带的代理的方式来实现,通过监测消息的执行,来在执行方法调用的同时来执行另外的一段代码。
2、静态植入,一般是在编译器,需要编译器内部的支持,然后在编译器就把想要执行的代码通过编译器将注入代码生成在目标方法中。
对比静态植入与动态植入,静态植入的效率更高,不过不灵活,动态注入效率地下,但是更灵活。
上面也给出的简单的分析,下面我们还是结合代码来说说吧,如何来操作:
既然我们要注入到具体的方法中,那么我们通过标记的形式来标记某个方法是要执行注入的目标方法。我们这里定义这个特性的抽象类。
public abstract class DecoratorAttributeBase : Attribute { /// /// AOP注入机制 /// /// public abstract void Intercept(object target); }
我们对于具体的缓存操作,我们定义TimeOutAttribute 个特性,当执行某个方法的时候,我们即刷新缓存。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class TimeOutAttribute : DecoratorAttributeBase { public override void Intercept(object target) { MethodCallMessageWrapper caller = (MethodCallMessageWrapper)target; if (caller.ArgCount == 0) return; //重新设置缓存。 } }这里我没有给出具体的缓存过期的代码,如果大家想知道具体的操作缓存的代码,那么大家请参考上篇中的《网站性能优化之应用程序缓存-中篇》中的操作缓存的类,那么我们再来看看具体的配置文件中的格式吧:
<cacheModels>//缓存的对象列表 <cacheModel id="CacheLRU_Sys_UserRole" implementation="LRU" >//缓存的对象 <flushInterval minutes="2" />//设置过期策略 <flushOnExecute statement="Insert_Sys_UserRole"/>//刷新的动作 <flushOnExecute statement="Update_Sys_UserRole"/> <flushOnExecute statement="Delete_Sys_UserRole"/> <property name="CacheSize" value="100"/>//设置缓存对象的大小 </cacheModel> </cacheModels> 配置具体的缓存刷新动作 <flushOnExecute statement="Insert_Sys_UserRole"/>//刷新的动作 <flushOnExecute statement="Update_Sys_UserRole"/> <flushOnExecute statement="Delete_Sys_UserRole"/>
下面我们给出具体的通用代理类实现:
public class CustomProxy : RealProxy, IDisposable where T : MarshalByRefObject { /// 构造过程中把Proxy需要操作的内容与实际目标对象实例Attach到一起。 public CustomProxy(T target) : base(target.GetType()) { AttachServer(target); } /// 析构过程则借助proxy和目标对象实例的Attach,便于GC回收。 public void Dispose() { DetachServer(); } public static T Create(T target) { if (target == null) throw new ArgumentNullException("target"); return (T)(new CustomProxy(target).GetTransparentProxy()); } /// 实际执行的拦截,并根据装饰属性进行定制处理。 public override IMessage Invoke(IMessage msg) { MethodCallMessageWrapper caller = new MethodCallMessageWrapper((IMethodCallMessage)msg); // 提取实际宿主对象 MethodInfo method = (MethodInfo)caller.MethodBase; T target = (T)GetUnwrappedServer(); DecoratorAttributeBase[] attributes = (DecoratorAttributeBase[]) method.GetCustomAttributes(typeof(DecoratorAttributeBase), true); object ret = method.Invoke(target, caller.Args);if (attributes.Length > 0)foreach (DecoratorAttributeBase attribute in attributes) attribute.Intercept(caller);// 拦截处理后,继续回到宿主对象的调用过程 return new ReturnMessage(ret, caller.Args, caller.ArgCount, caller.LogicalCallContext, caller); } }我们这里定义一个基类:
public abstract class Base : MarshalByRefObject { //定义相关方法,可以为子类提供相关的其他服务 }我们来看看具体的实体代码:
class Book : Base { private string name; private string title; [TimeOut()] public void SetBookInfo(object name, object title) { this.name = (string)name; this.title = (string)title; } }
具体的测试代码如下:
Book book= CustomProxy.Create(new Book()); book.SetBookInfo("joe", "manager"); // 成功 try { book.SetBookInfo(20, "manager"); } catch (Exception exception) { // 因第一个参数类型异常被拦截后抛出异常 //Assert.AreEqual("0", exception.Message); } try { book.SetBookInfo("", "manager"); } catch (Exception exception) { // 因name为空被拦截后抛出异常 //Assert.AreEqual("string is null or empty", exception.Message); }
这样我们就在我们的应用程序中集成了缓存服务,当然还有更好的方式来使用AOP,这里的继承 MarshalByRefObject 限制了我们在程序中使用AOP,必须继承这个类,当然后续我会讲解如何通过
另外的几类实现方案,我们可以对比结合我们的应用框架来无缝集成。
目前在.Net平台下的AOP大部分仍然处于最初的开发阶段,各自发布的版本基本都是beta版。其中较有代表性的AOP工具包括Aspect#,Spring.Net,Eos等。
Aspect#是基于Castle动态代理技术实现的。Castle动态代理技术利用了.Net的Emit技术,生成一个新的类去实现特定的接口,或者扩展一个已有的类,并将其委托指向IInterceptor接口的实现类。通过Castle动态代理技术,就可以拦截方法的调用,并将Aspect的业务逻辑织入到方法中。利用Castle动态代理技术,最大的缺陷是它只对虚方法有效,这限制了Aspect#的一部分应用。
Spring.Net从根本意义上来说,是对Spring工具从Java平台向.Net平台的完全移植。它在AOP的实现上与Spring几乎完全相似,仍然利用了AOP联盟提供的拦截器、Advice等实现AOP。
Spring.Net的配置文件也与Spring相同。
Eos采用的是静态织入的技术。它提供了独有的编译器,同时还扩展了C#语法,以类似于AspectJ的结构,规定了一套完整的AOP语法,诸如aspect,advice,before,after,pointcut等。
Eos充分的利用了.Net中元数据的特点,以IL级的代码对方面进行织入,这也使得它的性能与其他AOP工具比较有较大的提高。
下一篇,我们将会从从spring.NET与EF中的AOP的植入来剖析下,其他的可能的AOP的机制,这样我们能够更深入的了解AOP的技术和实现思路,我们掌握了基本的原理
后,应用它才能够游刃有余。能够更好的控制他,通过AOP能够更方便的让我们将关注度集中在业务层。由于本人水平有限,欢迎大家拍砖,不足之处,还请大家多多指出。