【框架学习与探究之AOP--Castle DynamicProxy】

声明

本文欢迎转载,原始地址:http://www.cnblogs.com/DjlNet/p/7603654.htmlhtml


前言

先说一点废话,在此以前博主也在早期就接触了或者看了些许AOP相关的文章,而后再去作了一些相关的实验,可是始终没有将AOP内化到本身的内功心法当中去,包括从概念仍是应用环境,以及当前生态当中的AOP工具等等,因此这里博主仍是按照以往的套路,在前人的基础之上学习而后吸取到集成到系统当中去。git


什么是AOP

仍是先看官方解释AOP(Aspect-Oriented Programming,面向切面的编程),它是能够经过预编译方式和运行期动态代理实如今不修改源代码的状况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。OOP是关注将需求功能划分为不一样的而且相对独立,封装良好的类,并让它们有着属于本身的行为,依靠继承和多态等来定义彼此的关系;AOP是但愿可以将通用需求功能从不相关的类当中分离出来,可以使得不少类共享一个行为,一旦发生变化,没必要修改不少类,而只须要修改这个行为便可AOP是使用切面(aspect)将横切关注点模块化,OOP是使用类将状态和行为模块化。在OOP的世界中,程序都是经过类和接口组织的,使用它们实现程序的核心业务逻辑是十分合适。可是对于实现横切关注点(跨越应用程序多个模块的功能需求)则十分吃力,好比日志记录,权限验证,异常拦截等。这里能够看到当咱们在应用程序中使用AOP来编程的话,是大大的节约个人代码量并且作到了职责依然分明,因此有这么多好处的状况下,咱们就应该勇敢的尝试来构建个人应用程序...github


.NET当中的AOP

根据上文关于AOP的两种方式来实现了面向切面编程,而在.NET领域当中有些比较出名的,例如在动态代理方面就是castle.core(原名:castle dynamic proxy)经过Reflect.Emit的方式在原始基础上面实现的动态代理方式,加上缓冲机制提高性能,该项目很好的屏蔽了底层使用emit+opcode的方式带来的复杂性和难度,使用友好的API调用方式提供外部程序访问castle.coree nuget地址,小吐槽:.net core已经2.0这么久了,castle家族虽然也还在努力,可是依然还没支持.net standrad2.0,多是由于castle.core如今打包了不止dynamicproxy一个包的缘由,须要一些时间,不过在@Lemon努力下率先提供了基于.net core的aop框架,这里博主也是对此框架目前也是浅尝辄止的状态尚未应用到项目中,因此很差对比评价哈,哈哈!),因此基于咱们当前的项目而已动态中的castle不失为咱们的一个好的选择;而后在.net静态编织方面当属postsharp首当其冲了,固然这玩意儿是收费不过你可使用express免费版可是和企业版对dll的修改差别就不得而知了,当初博主实验安装的企业版也是在免费期内使用后面作了一些小手脚才得以继续使用,固然这里不推荐破解使用了,有钱的主儿仍是支持正版吧注意使用postsharp须要vs安装插件(须要插件支持才能够编译后期对dll动手脚)以及nuget package支持,因此团队里面若是使用须要你们统一安装,还有就是目前许多都是基于Jenkins来作持续集成的,因此还得注意使用了postsharp的项目编译问题(使用了postsharp若是没有插件支持会报错)!!固然我这里的指出相对比较计较的性能问题,静态编织是比动态代理来的更快,由于dll在编译以后都已经造成了AOP之势,动态代理毕竟是运行时才得以去构建各类代理对象不过还好有缓冲,因此相对通常状况动态代理足以知足咱们的需求,仍是那个句话:在已经解决显而易见的瓶颈以后,才考虑一些锱铢必较的性能缺失!!!最后.NET周边其余AOP框架或者产品无论是动态仍是静态,你们可自行搜索是有的,不过使用率以及可用性、稳定性、性能等等就很差说了!express


Castle DynamicProxy文档带来了什么

首先Castle DynamicProxy是一个在运行时即时生成轻量级.NET代理的库,而后除了介绍AOP使用场景就是当前一些流行项目对于castle.core的依赖,侧面也反应除了此AOP的地位,好吧,233333,官方文档是要捧一下本身的。咱们会发现 moq(mock的实现)、NHibernate(延迟加载virtual+getter拦截实现等)等都依赖了castle.core,说明了上面的框架功能提供确定是须要动态代理的支撑的,而后根据官方说明在版本2.5以后原先独立的castle.dynamicproxy.dll就合并了,官方高能若是您有一个长时间运行的进程(网站,Windows服务等),而且必须建立许多动态代理,那么您应该确保重用相同的ProxyGenerator实例。若是没有,请注意,您将绕过缓存机制,反作用是CPU使用率高,内存消耗不断增长。编程

这里引用官方对于catle dynamicproxy的工做原理及流程的理解,显示给出执行流程图:

咱们这里外部的蓝色框就是代理区域,黄色箭头将会层层进入各级代理对象,接着代理执行PreAction逻辑,而后调用invocation.Proceed()(每一个代理只能调用一次否则出爆发异常)进入下一个代理或者原始对象逻辑,就这样一直进入到最下层也就是被代理的对象,执行原始逻辑以后,再按照层层代理执行PostAction逻辑弹出也就是绿色箭头所表达的意思,就是一条完成的传递链就造成了多级代理模式。注意点:每一层代理对象均可以拿到目标的对象也就是被代理对象IInvocation,该接口包含了一些重要属性和方法,例如:invocation.MethodInfo、invocation.ResultValue 等对象,咱们接下来对API实验详细看看这些对象的真实面目,至此根据官方文档就这样没了,还有些stackoverflow的参考代码,咱们同时翻看园中其余使用代码和对API的探究完善对castle dynamicproxy的了解....设计模式


Castle DynmaicProxy API提供了什么

首先这里要说明框架当中几个重要的对象:
一、IInterceptor 拦截器该接口提供了能够自定义拦截器逻辑的入口,实现接口的 Intercept 方法便可,在后面建立代理对象须要接口实例来控制代理对象行为,这里框架也提供了StandardInterceptor标准的拦截器继承MarshalByRefObject对象以及实现了IInterceptor接口,它包含了protected virtual void PerformProceed(IInvocation invocation);protected virtual void PostProceed(IInvocation invocation);protected virtual void PreProceed(IInvocation invocation);三个经常使用的接口方法....api

public interface IInterceptor
{
    void Intercept(IInvocation invocation);
}

二、IProxyGenerator 代理对象的建立者,包含了两个属性:LoggerProxyBuilder(只读,具体由它来建立代理类型),包含以下几个重要的API方法:
(1)动态建立类代理 proxyGenerator.CreateClassProxy 包含重载:Creates proxy object intercepting calls to virtual members of type TClass on newly created instance of that type with given interceptors. (建立一个新的代理对象subclass,经过配置的拦截器拦截原始对象标记了公开public的虚方法virtual的method产生效果,包含使用方法传递进来的代理配置项);
(2)动态建立类代理经过既有实例 proxyGenerator.CreateClassProxyWithTarget 包含重载:Creates proxy object intercepting calls to virtual members of type TClass on newly created instance of that type with given interceptors.(建立一个新的代理对象subclass,经过配置的拦截器拦截原始对象标记了公开public的虚方法virtual的method产生效果,提供了方法参数传递一个既有的目标实例对象,包含使用方法传递进来的代理配置项);
(3)动态建立接口代理不须要实现接口的实例对象 proxyGenerator.CreateInterfaceProxyWithoutTarget:Creates proxy object intercepting calls to members of interface TInterface on target object generated at runtime with given interceptor.(动态建立接口对象实现的实例且不须要实现了接口实例参数,经过拦截器凑效于接口方法实现拦截,注意这里若是接口方法要求了返回值,就须要在拦截器中指定返回值,相似于:invocation.ReturnValue=2;
(4)动态建立接口代理经过实现接口的实例对象 proxyGenerator.CreateInterfaceProxyWithTarget:Creates proxy object intercepting calls to members of interface TInterface on target object with given interceptors.(动态建立接口代理对象,经过传递实现了接口的对象实例,使用配置的拦截器对象做用于接口的每一个方法,这里实现接口的对象实例的实现方法就能够不须要配置为vritual了,由于在接口代理对象中已经包裹住了原始对象是采用了相似于注入的方式而不是继承,能够参考下面的示例代码
(5)动态建立接口代理经过实现了接口的实例对象 proxyGenerator.CreateInterfaceProxyWithTargetInterface:Creates proxy object intercepting calls to members of interface TInterface on target object with given interceptors. Interceptors can use Castle.DynamicProxy.IChangeProxyTarget interface to provide other target for method invocation than default target.(与上述CreateInterfaceProxyWithTarget类似,那么它们的区别在于哪里呐,这里博主本着研究的精神找了一下,不至于翻看源码了....,找到相似代码提供者的一点描述以下:http://kozmic.net/2009/11/13/interfaceproxywithtarget-interfaceproxywithtargetinterface-ndash-whatrsquos-the-difference/,大致总结就是:通常状况下调用者须要的就是CreateInterfaceProxyWithTargetInterface这个API的调用,其中它提供了两个优势:当使用InterfaceProxyWithTargetInterface时,它的调用实现了IChangeProxyTarget接口,该接口容许拦截途中更改目标对象。而后最重要的是 InterfaceProxyWithTargetInterface更好地使用缓存,详情参考连接代码验证过程,固然博主也亲自测试如同文中做者一模一样!!。)博主实验参考以下:数组

IMyCompare oneCompare = proxyGenerator.CreateInterfaceProxyWithTarget<IMyCompare>(new MyCompareOne(), new MyStandradInterceptor());
            IMyCompare twoCompare = proxyGenerator.CreateInterfaceProxyWithTarget<IMyCompare>(new MyCompareTwo(), new MyStandradInterceptor());

            Type oneCompareType = oneCompare.GetType();
            Type twoCompareType = twoCompare.GetType();

            Console.WriteLine("{0}", object.ReferenceEquals(oneCompareType, twoCompareType)); // false

            IMyCompare oneCompare1 = proxyGenerator.CreateInterfaceProxyWithTargetInterface<IMyCompare>(new MyCompareOne(), new MyStandradInterceptor());
            IMyCompare twoCompare1 = proxyGenerator.CreateInterfaceProxyWithTargetInterface<IMyCompare>(new MyCompareTwo(), new MyStandradInterceptor());
            Type oneCompare1Type = oneCompare1.GetType();
            Type twoCompare1Type = twoCompare1.GetType();
            Console.WriteLine("{0}", object.ReferenceEquals(oneCompare1Type, twoCompare1Type)); // ture

关于castle dynamicproxy动态代理中的对与class与interface的处理方式大体原理探究关于class的代理,相信不少同窗都应该直到,也就是设计模式当中的代理模式的利用,具体就是继承原始类对象实现对原始对象虚方法的重写实现注入拦截的逻辑,不过这一切都是动态的不须要咱们去构建了,参考代码以下(这里只考虑一级代理,多级也就是多层继承关系):缓存

public class Caller
    {
        public virtual void Call()
        {
            Console.WriteLine("calling...");
        }
    }

    public class CallerProxy : Caller
    {
        public override void Call()
        {
            // 执行前
            Console.WriteLine("pre action");
            base.Call();
            // 执行后
            Console.WriteLine("post action");
        }
    }

    static void Main(string[] args)
        {
            Caller caller = new CallerProxy();
            caller.Call(); Console.ReadKey();
        }

接着关于interface的代理的大体原型是,经过实现接口产生一个包含了传递的接口实习实例对象的接口代理对象,可能这里有点绕,不过依然仍是代理模式,关系从继承变成了包含,同理这些东西castle已经帮我用动态的方式构建好了,咱们看一示例代码就知道了,这里展现一层代理多级代理就是层层包含了架构

public interface IService
    {
        void Process();
    }

    public class Service : IService
    {
        public void Process()
        {
            // do something
            Console.WriteLine("processing...");
        }
    }

    public class ServiceProxy : IService
    {
        private readonly Service _service;
        public ServiceProxy(Service service)
        {
            _service = service;
        }

        public void Process()
        {
            // pre action
            Console.WriteLine("pre action");
            _service.Process();
            // post action
            Console.WriteLine("post action");
        }
    }

IService service = new ServiceProxy(new Service());
            service.Process();
            Console.ReadKey();

相信看到这里你也以为,我去,动态代理这么简单么,其实否则,虽然道理你们一看就懂是简单就是设计模式的代理模式的运用嘛,可是将这个动做泛化为一个通用的API支持可变拦截器数量配置以及各类代理配置将是一项繁杂而当心的工做,既要考虑友好的API还有重中之重的性能保证,也就是使用上面提到的 Reflect.Emit + OpCode 来实现接近于元数据编程....,可怕!!!因此,这里给写AOP的同窗点赞!@Lemon
三、代理生成配置对象 ProxyGenerationOptions,在生成代理对象是可传递自定义配置对象,实现可控的拦截,该对象主要配置项:

public IProxyGenerationHook Hook { get; set; } (决定了该方法是否受拦截器拦截,能够实现自定义Hook)
public IInterceptorSelector Selector { get; set; } (决定了该方法受那些拦截器拦截,能够实现自定义Selector)
public Type BaseTypeForInterfaceProxy { get; set; } (决定了接口代理的基础类型,详情使用参考连接:https://stackoverflow.com/questions/2969274/castle-dynamicproxy-how-to-proxy-equals-when-proxying-an-interface

具体例子参考以下:

public class MyInterceptorSelector : IInterceptorSelector
    {
        public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
        {
            if (method.Name.EndsWith("Repository"))
            {
                return interceptors.Where(x => x is TestInterceptor).ToArray();
            }
            return interceptors.Where(x => x is TestInterceptor2 || x is MyStandradInterceptor).ToArray();
        }
    }
 public class MyGenerationProxyHook : IProxyGenerationHook
    {
        public void MethodsInspected()
        {
        }

        public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo)
        {
        }

        public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo)
        {
            return methodInfo.Name.EndsWith("Service");
        }
    }

这里相信你们一看就明白了,就很少说了.....这里注意点就是: 拦截器的植入顺序与生效顺序是一致的....


从其余框架复盘认识castle与集成

了解过[abp]:(https://aspnetboilerplate.com/) 的同窗,确定就知道此框架强制把castle家族的castle.core+Castle.Windsor(依赖前者)融入进abp当中了,采用了接口代理实现了日志记录、异常处理、权限审查、审计日志等等,固然abp框架当中确实不止一处是值得咱们学习,不过此框架构建在一个及集众家之所长的状况下就显得复杂,从知名度能够看出选择caslte来做为aop+ioc的集成是个不错的选择,接着从moq的部分实例代码中能够看出,就是利用了proxyGenerator.CreateInterfaceProxyWithoutTarget等等,之类的细节就能够回想起原始API作起底层的支撑做用,再者castle与许多第三方ioc有着比较好的集成,例如比较出名的ioc框架autofac+castle也是不少人的选择,参考连接:[AOP系列]Autofac+Castle实现AOP事务:http://www.cnblogs.com/jianxuanbing/p/7199457.html
这里博主为什么要把ioc扯进来一块儿说呐?
从castle的api来看你们以为有木有点觉着好像有着一点ioc的功能,可是为什么咱们要明确概念说aop是aop,ioc是ioc呐,这里博主的理解就是,它们各自的职责是不一样,它们各自只须要各司其职就好了,也不要越界也是编程开发当中的单一职责的体现了,虽然多多少少你们都有些动态建立对象那么回事儿(无论是emit仍是activor.CreateInstance)!可是咱们从ioc的职能分析获得的是:一、负责对象的存储(注册)与建立(解析) 二、负责对象的依赖关系维护 三、负责对象的生命周期,这三点就能够看出与AOP功能不一致,可是咱们看到对象建立这个时候,想一下是否能够在对象建立的时植入Interceptor???回答是:确定是能够的,因此这就是为什么咱们经常把ioc与aop一块儿来食用了,据说这样用更配哦!!


总结

咱们从AOP的定义到AOP的使用场景,而后.net下面的AOP的介绍,接着重点介绍了动态代理中的castle.core的官方说明与文档,后面尤为重要的详解了框架当中重要的一些对象和API以及原理和实践,也途中参考一些文章和stackoverflow,也请教AOP相关人士,这里感谢!好了,时间也不早了,相信学无止境,那么就保持持续学习,持续内化知识,就像修炼内功心法同样,半途而废还容易走火入魔,只知其一;不知其二说出去的东西本身都没搞明白,岂不是笑话了!!加油吧,骚年

这里引用知乎大大的一段话以此激励:成长必须经历一个步骤,就是把知识内化成能力。知识是用脑记住的,能力是用手练习出来的。在工做的几年里,咱们可能看过不少书,听过不少技术讲座和视频,可是经过听和看只是让你能记住这些知识,这些知识还不能转换成你的能力。听和看只是第一步,更重要的是实践,经过刻意练习把听到和看到的知识内化成你的能力。刻意练习,就是有目的的练习,先规划好,再去练习。首先给本身定一个目标,目标能够有效的引导你学习,而后使用3F练习法:1: 专一(Focus),专一在眼前的任务上,在学习过程当中保持专一,能够尝试使用番茄工做法。2:反馈(Feedback),意识到本身的不足,学习完以后进行反思,思考下本身哪些方面不足,为何不足,3: 修正(Fix),改进本身的不足。不停的练习和思考能够改变大脑结构,大脑像肌肉同样,挑战越大,影响越大,学习更高效,而且也会产生突破性。 -- 原始连接: https://www.zhihu.com/question/26572626/answer/246901769?utm_medium=social&utm_source=qq


收集备注园中相关AOP好文

一、Asp.Net Core轻量级Aop解决方案:AspectCore http://www.cnblogs.com/liuhaoyang/p/aspectcore-introduction-1.html
二、C# 实现AOP 的几种常见方式http://www.cnblogs.com/zuowj/p/7501896.html
三、.Net基于RealProxy实现AOP : http://www.cnblogs.com/lflyq/p/6286925.html
四、Aspect-Oriented Programming : 使用 RealProxy 类进行面向方面的编程 : https://msdn.microsoft.com/zh-cn/magazine/dn574804.aspx


补充更新(2017年10月20日15:07:33)

关于 castle api 当中对于 IInvocation对象的解释少了一些,这里补充一下:
一、invocation.Arguments: 方法执行被拦截时的方法参数数组
二、invocation.GenericArguments: 被拦截方法的泛型参数类型数组,若是没有就是null
三、invocation.InvocationTarget: 获取当前执行的目标对象,例如:若是是class代理就是YourClassProxy,接口代理就是实现接口的对象实例,若是没有则为null,也就是当使用xxxWithoutTarget的时候
四、invocation.Method:获取代理对象的方法信息,例如:若是是class代理的时候就是YourClass.YourMethod对象的MethodInfo且这个时候invocation.Method == invocation.MethodInvocationTarget;若是是interface代理就是接口对象的方法信息,例如:ICall.Call 这个方法的MethodInfo信息且这个时候invocation.Method != invocation.MethodInvocationTarget,由于invocation.MethodInvocationTarget是接口对应实现的目标对象的方法信息,也就是例如:MyCall.Call 方法对应上面的 ICall 接口来讲,固然也可使用 WithoutTarget方式,这样就会致使 invocation.MethodInvocationTarget==null的状况
五、invocation.MethodInvocationTarget: 指向真正的目标对象的方法信息MethodInfo,大体能够根据第四点给出了说明
六、invocation.Proxy : 获取拦截时的代理对象,例如:YourClassProxy(类代理) 或者 ICallProxy(接口代理) 对象
七、invocation.ResultValue: 获取或者设置代理方法的返回值
八、invocation.TargetType: 获取真实目标对象的类型
九、invocation.GetArgumentValue(int index);: 经过index获取参数值
十、invocation.GetConcreteMethod();: 同理第四点
十一、invocation.GetConcreteMethodInvocationTarget();: 同理第五点
十二、invocation.Proceed();: 调用下一个拦截器直到目标方法
1三、invocation.SetArgumentValue(int index, object value);: 设置更改参数值经过下标
这里博主列举除了拦截途中对象IInvocation的全部成员,你们在使用拦截器的时候可根据本身逻辑使用以上API到达要求!


若是以为阅读了博主的小文以为对您有帮助,您的点赞和评论都是对博主最大的支持!!!

相关文章
相关标签/搜索