Castle AOP

Castle是一个框架,包含了AOP、IOC、ORM等多个方面,其中的Castle.DynamicProxy可以实现动态代理的功能,这个也是很多框架的基础。在IBatis.Net中就是使用了Castle.DynamicProxy来实现数据库连接等动态操作的。同时在NHibernet等其他框架中也使用到了这个技术。特点是实体不需要继承MarshalByRef、ContextBoundObject便可以实现代理类。基于透明代理的功能,可以实现对方法调用的拦截处理,例如NHibernate用它实现延迟加载DP的使用非常简单,内部没有使用反射,而是采用Emit、委托等方式生成代理类型,调用真实类的方法,性能方面也没有太多损失

项目地址:Castle Dynamic Proxy

一般情况下要有三个类:接口类,实现类,代理类。

一.Castle.DynamicProxy 实现的最简单的AOP

01 1. using System; 
02  2. using Castle.Core.Interceptor; 
03  3. using Castle.DynamicProxy; 
04  4.  
05  5. namespace DynamicProxyExample 
06  6. { 
07  7.  
08  8.     class Program 
09  9.     { 
10 10.  
11 11.         static void Main(string[] args) 
12 12.         { 
13 13.             ProxyGenerator generator = new ProxyGenerator(); 
14 14.             Logger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());  //泛型版
15 15.             //Logger logger = generator.CreateClassProxy(typeof(Logger), new TestInterceptor()) as Logger;  //非泛型版
16 16.             logger.Write("love"); 
17 17.         } 
18 18.     } 
19 19.  
20 20.     public class TestInterceptor : IInterceptor 
21 21.     { 
22 22.         public void Intercept(IInvocation invocation) 
23 23.         { 
24 24.             Console.Write("I "); 
25 25.             invocation.Proceed();
26 26.             Console.Write(" you.");
27 27.         } 
28 28.     } 
29 29.  
30 30.     public class Logger 
31 31.     { 
32 32.         public virtual void Write(string message) 
33 33.         { 
34 34.             Console.Write(message); 
35 35.         } 
36 36.     } 
37 37. } 

 

a.Logger是被代理的实体类,TestInterceptor是拦截器. 

b.Logger中:public virtual  void Write(string message) 。好像必须是虚方法,如果不是虚方法,拦截不了。很是奇怪。

c.生成代理对象时,可以使用泛型版也可不使用泛型。建议使用泛型。

1.重构增加一个接口

 

01 1.  using System; 
02  2. using Castle.Core.Interceptor; 
03  3. using Castle.DynamicProxy; 
04  4.  
05  5. namespace DynamicProxyExample 
06  6. { 
07  7.     class Program 
08  8.     { 
09  9.  
10 10.         static void Main(string[] args) 
11 11.         { 
12 12.             ProxyGenerator generator = new ProxyGenerator(); 
13 13.             ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor()); 
14 14.  
15 15.             logger.Write("love"); 
16 16.         } 
17 17.     } 
18 18.  
19 19.     //拦截器 
20 20.     public class TestInterceptor : IInterceptor 
21 21.     { 
22 22.         public void Intercept(IInvocation invocation) 
23 23.         { 
24 24.             Console.Write("I "); 
25 25.             invocation.Proceed(); 
26 26.             Console.Write(" you. "); 
27 27.         } 
28 28.     } 
29 29.      
30 30.     //接口 
31 31.     public interface ILogger 
32 32.     { 
33 33.         void Write(string message); 
34 34.     } 
35 35.  
36 36.     //实现类 
37 37.     public class Logger:ILogger 
38 38.     { 
39 39.         public virtual  void Write(string message) 
40 40.         { 
41 41.             Console.Write(message); 
42 42.         } 
43 43.     } 
44 44. } 

这次重构中增加了一个ILogger的接口。也是基于“接口隔离”,“依赖倒置”的设计原则。 

2.重构切面前方法和切面后方法

 

01 1.  using System;   
02  2. using Castle.Core.Interceptor;   
03  3. using Castle.DynamicProxy;   
04  4.    
05  5. namespace DynamicProxyExample   
06  6. {   
07  7.     class Program   
08  8.     {   
09  9.    
10 10.         static void Main(string[] args)   
11 11.         {   
12 12.             ProxyGenerator generator = new ProxyGenerator();   
13 13.             ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());   
14 14.    
15 15.             logger.Write("love");   
16 16.         }   
17 17.     }   
18 18.    
19 19.     //拦截器   
20 20.     public class TestInterceptor : IInterceptor   
21 21.     {   
22 22.         public void Intercept(IInvocation invocation)   
23 23.         { 
24 24.             PreProceed(invocation);              
25 25.             invocation.Proceed(); 
26 26.             PostProceed(invocation);                
27 27.         } 
28 28.  
29 29.         private void PreProceed(IInvocation invocation) 
30 30.         { 
31 31.             Console.Write("I ");   
32 32.         } 
33 33.         private void PostProceed(IInvocation invocation) 
34 34.         { 
35 35.             Console.Write(" you. "); 
36 36.         } 
37 37.     }   
38 38.        
39 39.     //接口   
40 40.     public interface ILogger   
41 41.     {   
42 42.         void Write(string message);   
43 43.     }   
44 44.    
45 45.     //实现类   
46 46.     public class Logger:ILogger   
47 47.     {   
48 48.         public virtual  void Write(string message)   
49 49.         {   
50 50.             Console.Write(message);   
51 51.         }   
52 52.     }   
53 53. }  

 

通常切面前和切面后方法都很复杂,通过重构让每个方法责任明确,单一。PreProceed:切面前处理函数。PostProceed:切面后处理方法。

3.使用StandardInterceptor再次重构切面前后方法。

 

01 1.  using System;   
02  2. using Castle.Core.Interceptor;   
03  3. using Castle.DynamicProxy;   
04  4.    
05  5. namespace DynamicProxyExample   
06  6. {   
07  7.     class Program   
08  8.     {      
09  9.         static void Main(string[] args)   
10 10.         {   
11 11.             ProxyGenerator generator = new ProxyGenerator();   
12 12.             ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());   
13 13.    
14 14.             logger.Write("love");   
15 15.         }   
16 16.     }   
17 17.    
18 18.     //拦截器   
19 19.     public class TestInterceptor : StandardInterceptor   
20 20.     { 
21 21.         protected override void PreProceed(IInvocation invocation) 
22 22.         { 
23 23.             Console.Write("I ");   
24 24.         } 
25 25.         protected override void PostProceed(IInvocation invocation) 
26 26.         { 
27 27.             Console.Write(" you. "); 
28 28.         } 
29 29.     }   
30 30.        
31 31.     //接口   
32 32.     public interface ILogger   
33 33.     {   
34 34.         void Write(string message);   
35 35.     }   
36 36.    
37 37.     //实现类   
38 38.     public class Logger:ILogger   
39 39.     {   
40 40.         public virtual  void Write(string message)   
41 41.         {   
42 42.             Console.Write(message);   
43 43.         }   
44 44.     }   
45 45. }  

 

无需赘述。 

二、创建代理对象时可以指定多个拦截器

 

01 1.  using System;   
02  2. using Castle.Core.Interceptor;   
03  3. using Castle.DynamicProxy;   
04  4.    
05  5. namespace DynamicProxyExample   
06  6. {   
07  7.     class Program   
08  8.     {      
09  9.         static void Main(string[] args)   
10 10.         {   
11 11.             ProxyGenerator generator = new ProxyGenerator();   
12 12.             ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor1(), new TestInterceptor2() );   
13 13.    
14 14.             logger.Write("love");   
15 15.         } 
16 16.     } 
17 17. 
18 18.     #region 拦截器 
19 19.  
20 20.     //拦截器1   
21 21.     public class TestInterceptor1 : StandardInterceptor   
22 22.     { 
23 23.         protected override void PreProceed(IInvocation invocation) 
24 24.         { 
25 25.             Console.Write(" (张三 ");   
26 26.         } 
27 27.         protected override void PostProceed(IInvocation invocation) 
28 28.         { 
29 29.             Console.Write(" 张四) "); 
30 30.         } 
31 31.     } 
32 32.  
33 33.     //拦截器2   
34 34.     public class TestInterceptor2 : StandardInterceptor 
35 35.     { 
36 36.         protected override void PreProceed(IInvocation invocation) 
37 37.         { 
38 38.             Console.Write(" and 李三) "); 
39 39.         } 
40 40.         protected override void PostProceed(IInvocation invocation) 
41 41.         { 
42 42.             Console.Write(" (李四 "); 
43 43.         } 
44 44.     } 
45 45.     #endregion 
46 46.  
47 47.     //接口   
48 48.     public interface ILogger   
49 49.     {   
50 50.         void Write(string message);   
51 51.     }   
52 52.    
53 53.     //实现类   
54 54.     public class Logger:ILogger   
55 55.     {   
56 56.         public virtual  void Write(string message)   
57 57.         {   
58 58.             Console.Write(message);   
59 59.         }   
60 60.     }   
61 61. }  

输出的结果为:(张三 和 李三 ) love (李四 和 张四) 

多个拦截器之间以管道方式处理调用顺序:

三、代理类型 Proxy Types

代理类型有class proxy、interface proxy with target、interface proxy without target、interface proxy with interface target几种

上面示例使用的是class proxy,使用CreateClassProxy方法创建,该方法的某些版本要求有默认构造器,不使用默认构造器时则必须传入构造参数。代理对象为 class proxy类型时,被拦截的方法必须为virtual,non-virtual的方法无法实现拦截

interface proxy with target其实跟class proxy差不多,在创建代理对象时client指定接口,并且提供一个实现了该接口的对象作为真实对象,DP将创建这个接口的代理对象,对代理对象方法的调用经过拦截器处理之后,最终将调用真实对象相应的方法。与class proxy的不同之处在于,真实对象的方法不必是virtual类型也可以实现拦截

interface proxy without target比较特殊,创建代理时只需要指定一个接口就可以,DP自动根据接口构造一个实现的类,作为代理对象的类型,但这个代理类只能用于拦截目的,无法像class proxy一样在拦截器中调用真实对象的处理方法。比如在提供了多个拦截器时,最后一个拦截器的接口方法中不能调用 invocation.Proceed()方法,否则会抛异常(因为真实对象根本不存在,只有一个假的代理对象)

interface proxy with interface target与interface proxy with target基本类似,但他提供了一个更改被代理对象(真实对象)的机会,示例如下: 

 

01 1.  using System; 
02  2. using Castle.Core.Interceptor; 
03  3. using Castle.DynamicProxy; 
04  4.  
05  5. namespace DynamicProxyExample 
06  6. { 
07  7.     class Program 
08  8.     { 
09  9.         static void Main(string[] args) 
10 10.         { 
11 11.             ProxyGenerator generator = new ProxyGenerator(); 
12 12.             IStorageNode node = generator.CreateInterfaceProxyWithTargetInterface<IStorageNode>( 
13 13.                 new StorageNode("master"
14 14.                 , new DualNodeInterceptor(new StorageNode("slave")) 
15 15.                 , new CallingLogInterceptor()); 
16 16.             node.Save("my message"); //应该调用master对象 
17 17.             node.IsDead = true
18 18.             node.Save("my message"); //应该调用slave对象 
19 19.             node.Save("my message"); //应该调用master对象 
20 20.             Console.ReadKey(); 
21 21.         } 
22 22.     } 
23 23.     public interface IStorageNode 
24 24.     { 
25 25.         bool IsDead { get; set; } 
26 26.         void Save(string message); 
27 27.     } 
28 28.     public class StorageNode : IStorageNode 
29 29.     { 
30 30.         private string _name; 
31 31.         public StorageNode(string name) 
32 32.         { 
33 33.             this._name = name; 
34 34.         } 
35 35.         public bool IsDead { get; set; } 
36 36.         public void Save(string message) 
37 37.         { 
38 38.             Console.WriteLine(string.Format("\"{0}\" was saved to {1}", message, this._name)); 
39 39.         } 
40 40.     } 
41 41.     public class DualNodeInterceptor : IInterceptor 
42 42.     { 
43 43.         private IStorageNode _slave; 
44 44.         public DualNodeInterceptor(IStorageNode slave) 
45 45.         { 
46 46.             this._slave = slave; 
47 47.         } 
48 48.         public void Intercept(IInvocation invocation) 
49 49.         { 
50 50.             IStorageNode master = invocation.InvocationTarget as IStorageNode; 
51 51.             if (master.IsDead) 
52 52.             { 
53 53.                 IChangeProxyTarget cpt = invocation as IChangeProxyTarget; 
54 54.                 //将被代理对象 master更换为slave 
55 55.                 cpt.ChangeProxyTarget(this._slave); 
56 56.                 //测试中恢复 master的状态,以便看到随后的调用仍然使用master这一效果 
57 57.                 master.IsDead = false
58 58.             } 
59 59.             invocation.Proceed(); 
60 60.         } 
61 61.     } 
62 62.  
63 63.     public class CallingLogInterceptor : StandardInterceptor 
64 64.     { 
65 65.         private int _indent = 0; 
66 66.         protected override void PreProceed(IInvocation invocation) 
67 67.         { 
68 68.             if (this._indent > 0) 
69 69.                 Console.Write(" ".PadRight(this._indent * 4, ' ')); 
70 70.             this._indent++; 
71 71.             Console.Write("Intercepting: " + invocation.Method.Name + "("); 
72 72.             if (invocation.Arguments != null && invocation.Arguments.Length > 0) 
73 73.                 for (int i = 0; i < invocation.Arguments.Length; i++) 
74 74.                 { 
75 75.                     if (i != 0) Console.Write(", "); 
76 76.                     Console.Write(invocation.Arguments[i] == null 
77 77.                         ? "null" 
78 78.                         : invocation.Arguments[i].GetType() == typeof(string
79 79.                            ? "\"" + invocation.Arguments[i].ToString() + "\"" 
80 80.                            : invocation.Arguments[i].ToString()); 
81 81.                 } 
82 82.             Console.WriteLine(")"); 
83 83.         } 
84 84.         protected override void PostProceed(IInvocation invocation) 
85 85.         { 
86 86.             this._indent--; 
87 87.         } 
88 88.     } 
89 89. } 

只有在interface proxy with interface target的情况下IInterceptor的接口参数IInvocation对象才实现了IChangeProxyTarget接口

IChangeProxyTarget的ChangeInvocationTarget方法将本次调用的被代理对象替换掉,而ChangeProxyTarget方法则永久的将被代理对象替换,但不包括本次调用  

四、Mixins 

要准确的将mixin翻译为中文比较难找到一个合适的词汇,他的大意是指在运行期使用“合并”的方式修改对象的行为。比如对象obj的类型为A,在运行时将类型B的所有属性和方法“混入”到对象obj上,使得对象obj同时具备类型A和B的属性和行为,就好像obj同时继承了A和B。在动态语言中比较容易实现这个效果,比如python本身支持多继承,还可以通过__bases__动态修改基类,所以python中使用mixin技术是非常简单的,包括 javascript也很容易实现这个效果。关于mixin概念的详细说明参考wikipedia

使用DP我们可以在C#中实现mixin,示例如下:

 

01 1.  using System; 
02  2. using Castle.Core.Interceptor; 
03  3. using Castle.DynamicProxy; 
04  4.  
05  5. namespace DynamicProxyExample 
06  6. { 
07  7.     class Program 
08  8.     { 
09  9.         static void Main(string[] args) 
10 10.         { 
11 11.             ProxyGenerator generator = new ProxyGenerator(); 
12 12.             var options = new ProxyGenerationOptions(); 
13 13.             options.AddMixinInstance(new ClassA()); 
14 14.             ClassB objB = generator.CreateClassProxy<ClassB>(options, new CallingLogInterceptor()); 
15 15.             objB.ActionB(); 
16 16.             InterfaceA objA = objB as InterfaceA; 
17 17.             objA.ActionA(); 
18 18.             Console.ReadKey(); 
19 19.         } 
20 20.     } 
21 21.     public interface InterfaceA 
22 22.     { 
23 23.         void ActionA(); 
24 24.     } 
25 25.     public class ClassA : InterfaceA 
26 26.     { 
27 27.         public void ActionA() 
28 28.         { 
29 29.             Console.WriteLine("I'm from ClassA"); 
30 30.         } 
31 31.     } 
32 32.     public class ClassB 
33 33.     { 
34 34.         public virtual void ActionB() 
35 35.         { 
36 36.             Console.WriteLine("I'm from ClassB"); 
37 37.         } 
38 38.     } 
39 39.  
40 40.     public class CallingLogInterceptor : StandardInterceptor 
41 41.     { 
42 42.         private int _indent = 0; 
43 43.         protected override void PreProceed(IInvocation invocation) 
44 44.         { 
45 45.             if (this._indent > 0) 
46 46.                 Console.Write(" ".PadRight(this._indent * 4, ' ')); 
47 47.             this._indent++; 
48 48.             Console.Write("Intercepting: " + invocation.Method.Name + "("); 
49 49.             if (invocation.Arguments != null && invocation.Arguments.Length > 0) 
50 50.                 for (int i = 0; i < invocation.Arguments.Length; i++) 
51 51.                 { 
52 52.                     if (i != 0) Console.Write(", "); 
53 53.                     Console.Write(invocation.Arguments[i] == null 
54 54.                         ? "null" 
55 55.                         : invocation.Arguments[i].GetType() == typeof(string
56 56.                            ? "\"" + invocation.Arguments[i].ToString() + "\"" 
57 57.                            : invocation.Arguments[i].ToString()); 
58 58.                 } 
59 59.             Console.WriteLine(")"); 
60 60.         } 
61 61.         protected override void PostProceed(IInvocation invocation) 
62 62.         { 
63 63.             this._indent--; 
64 64.         } 
65 65.     } 
66 66. } 

可以看到代理对象同时具备了ClassA和ClassB的行为  

 五、导出、生成代理类型 

Castle Dynamic Proxy允许我们将运行时生成的代理类型生成dll文件存到磁盘上,下次启动时通过加载这个dll文件可以避免动态生成代理类型

 

01 1.  using System; 
02  2. using Castle.Core.Interceptor; 
03  3. using Castle.DynamicProxy; 
04  4.  
05  5. namespace DynamicProxyExample 
06  6. { 
07  7.     class Program 
08  8.     { 
09  9.         static void Main(string[] args) 
10 10.         { 
11 11.             String path = AppDomain.CurrentDomain.BaseDirectory; 
12 12.             ModuleScope scope = new ModuleScope(true, "Invocation", path + "\\Invocation.dll", "Proxy", path + "\\Proxy.dll"); 
13 13.             DefaultProxyBuilder builder = new DefaultProxyBuilder(scope);  
14 14.  
15 15.  
16 16.             ProxyGenerator generator = new ProxyGenerator(builder); 
17 17.             ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor()); 
18 18.  
19 19.             logger.Write("love"); 
20 20.  
21 21.             scope.SaveAssembly(true); //加这句话可以将动态生成的Invocation类保存到本地硬盘 
22 22.  
23 23.             scope.SaveAssembly(false); //加这句话可以将动态生成的Proxy类保存到本地硬盘 
24 24.  
25 25.  
26 26.             Console.ReadKey(); 
27 27.         } 
28 28.     } 
29 29.  
30 30.     //拦截器   
31 31.     public class TestInterceptor : IInterceptor 
32 32.     { 
33 33.         public void Intercept(IInvocation invocation) 
34 34.         { 
35 35.             Console.Write("I "); 
36 36.             invocation.Proceed(); 
37 37.             Console.Write(" you. "); 
38 38.         } 
39 39.     } 
40 40.  
41 41.     //接口   
42 42.     public interface ILogger 
43 43.     { 
44 44.         void Write(string message); 
45 45.     } 
46 46.  
47 47.     //实现类   
48 48.     public class Logger : ILogger 
49 49.     { 
50 50.         public virtual void Write(string message) 
51 51.         { 
52 52.             Console.Write(message); 
53 53.         } 
54 54.     } 
55 55. }  

可以用reflector查看生成的dll,大致了解代理对象是如何工作的启动时,可以使用scope.LoadAssemblyIntoCache(assembly);将生成的代理类型加载到内存中,其中assembly需要我们手动加载

下载地址

posted @ 2010-10-06 21:25  13路易的  阅读(3686)  评论(1编辑  收藏  举报