Castle是一个框架,包含了AOP、IOC、ORM等多个方面,其中的Castle.DynamicProxy可以实现动态代理的功能,这个也是很多框架的基础。在IBatis.Net中就是使用了Castle.DynamicProxy来实现数据库连接等动态操作的。同时在NHibernet等其他框架中也使用到了这个技术。特点是实体不需要继承MarshalByRef、ContextBoundObject便可以实现代理类。基于透明代理的功能,可以实现对方法调用的拦截处理,例如NHibernate用它实现延迟加载DP的使用非常简单,内部没有使用反射,而是采用Emit、委托等方式生成代理类型,调用真实类的方法,性能方面也没有太多损失
项目地址:Castle Dynamic Proxy
一般情况下要有三个类:接口类,实现类,代理类。
一.Castle.DynamicProxy 实现的最简单的AOP
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
11 |
11. static void Main( string [] args) |
13 |
13. ProxyGenerator generator = new ProxyGenerator(); |
14 |
14. Logger logger = generator.CreateClassProxy<Logger>( new TestInterceptor()); |
16 |
16. logger.Write( "love" ); |
20 |
20. public class TestInterceptor : IInterceptor |
22 |
22. public void Intercept(IInvocation invocation) |
24 |
24. Console.Write( "I " ); |
25 |
25. invocation.Proceed(); |
26 |
26. Console.Write( " you." ); |
30 |
30. public class Logger |
32 |
32. public virtual void Write( string message) |
34 |
34. Console.Write(message); |
a.Logger是被代理的实体类,TestInterceptor是拦截器.
b.Logger中:public virtual void Write(string message) 。好像必须是虚方法,如果不是虚方法,拦截不了。很是奇怪。
c.生成代理对象时,可以使用泛型版也可不使用泛型。建议使用泛型。
1.重构增加一个接口
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
10 |
10. static void Main( string [] args) |
12 |
12. ProxyGenerator generator = new ProxyGenerator(); |
13 |
13. ILogger logger = generator.CreateClassProxy<Logger>( new TestInterceptor()); |
15 |
15. logger.Write( "love" ); |
20 |
20. public class TestInterceptor : IInterceptor |
22 |
22. public void Intercept(IInvocation invocation) |
24 |
24. Console.Write( "I " ); |
25 |
25. invocation.Proceed(); |
26 |
26. Console.Write( " you. " ); |
31 |
31. public interface ILogger |
33 |
33. void Write( string message); |
37 |
37. public class Logger:ILogger |
39 |
39. public virtual void Write( string message) |
41 |
41. Console.Write(message); |
这次重构中增加了一个ILogger的接口。也是基于“接口隔离”,“依赖倒置”的设计原则。
2.重构切面前方法和切面后方法
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
10 |
10. static void Main( string [] args) |
12 |
12. ProxyGenerator generator = new ProxyGenerator(); |
13 |
13. ILogger logger = generator.CreateClassProxy<Logger>( new TestInterceptor()); |
15 |
15. logger.Write( "love" ); |
20 |
20. public class TestInterceptor : IInterceptor |
22 |
22. public void Intercept(IInvocation invocation) |
24 |
24. PreProceed(invocation); |
25 |
25. invocation.Proceed(); |
26 |
26. PostProceed(invocation); |
29 |
29. private void PreProceed(IInvocation invocation) |
31 |
31. Console.Write( "I " ); |
33 |
33. private void PostProceed(IInvocation invocation) |
35 |
35. Console.Write( " you. " ); |
40 |
40. public interface ILogger |
42 |
42. void Write( string message); |
46 |
46. public class Logger:ILogger |
48 |
48. public virtual void Write( string message) |
50 |
50. Console.Write(message); |
通常切面前和切面后方法都很复杂,通过重构让每个方法责任明确,单一。PreProceed:切面前处理函数。PostProceed:切面后处理方法。
3.使用StandardInterceptor再次重构切面前后方法。
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
09 |
9. static void Main( string [] args) |
11 |
11. ProxyGenerator generator = new ProxyGenerator(); |
12 |
12. ILogger logger = generator.CreateClassProxy<Logger>( new TestInterceptor()); |
14 |
14. logger.Write( "love" ); |
19 |
19. public class TestInterceptor : StandardInterceptor |
21 |
21. protected override void PreProceed(IInvocation invocation) |
23 |
23. Console.Write( "I " ); |
25 |
25. protected override void PostProceed(IInvocation invocation) |
27 |
27. Console.Write( " you. " ); |
32 |
32. public interface ILogger |
34 |
34. void Write( string message); |
38 |
38. public class Logger:ILogger |
40 |
40. public virtual void Write( string message) |
42 |
42. Console.Write(message); |
无需赘述。
二、创建代理对象时可以指定多个拦截器
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
09 |
9. static void Main( string [] args) |
11 |
11. ProxyGenerator generator = new ProxyGenerator(); |
12 |
12. ILogger logger = generator.CreateClassProxy<Logger>( new TestInterceptor1(), new TestInterceptor2() ); |
14 |
14. logger.Write( "love" ); |
21 |
21. public class TestInterceptor1 : StandardInterceptor |
23 |
23. protected override void PreProceed(IInvocation invocation) |
25 |
25. Console.Write( " (张三 " ); |
27 |
27. protected override void PostProceed(IInvocation invocation) |
29 |
29. Console.Write( " 张四) " ); |
34 |
34. public class TestInterceptor2 : StandardInterceptor |
36 |
36. protected override void PreProceed(IInvocation invocation) |
38 |
38. Console.Write( " and 李三) " ); |
40 |
40. protected override void PostProceed(IInvocation invocation) |
42 |
42. Console.Write( " (李四 " ); |
48 |
48. public interface ILogger |
50 |
50. void Write( string message); |
54 |
54. public class Logger:ILogger |
56 |
56. public virtual void Write( string message) |
58 |
58. Console.Write(message); |
输出的结果为:(张三 和 李三 ) 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基本类似,但他提供了一个更改被代理对象(真实对象)的机会,示例如下:
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
09 |
9. static void Main( string [] args) |
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" ); |
17 |
17. node.IsDead = true ; |
18 |
18. node.Save( "my message" ); |
19 |
19. node.Save( "my message" ); |
20 |
20. Console.ReadKey(); |
23 |
23. public interface IStorageNode |
25 |
25. bool IsDead { get ; set ; } |
26 |
26. void Save( string message); |
28 |
28. public class StorageNode : IStorageNode |
30 |
30. private string _name; |
31 |
31. public StorageNode( string name) |
33 |
33. this ._name = name; |
35 |
35. public bool IsDead { get ; set ; } |
36 |
36. public void Save( string message) |
38 |
38. Console.WriteLine( string .Format( "\"{0}\" was saved to {1}" , message, this ._name)); |
41 |
41. public class DualNodeInterceptor : IInterceptor |
43 |
43. private IStorageNode _slave; |
44 |
44. public DualNodeInterceptor(IStorageNode slave) |
46 |
46. this ._slave = slave; |
48 |
48. public void Intercept(IInvocation invocation) |
50 |
50. IStorageNode master = invocation.InvocationTarget as IStorageNode; |
51 |
51. if (master.IsDead) |
53 |
53. IChangeProxyTarget cpt = invocation as IChangeProxyTarget; |
55 |
55. cpt.ChangeProxyTarget( this ._slave); |
57 |
57. master.IsDead = false ; |
59 |
59. invocation.Proceed(); |
63 |
63. public class CallingLogInterceptor : StandardInterceptor |
65 |
65. private int _indent = 0; |
66 |
66. protected override void PreProceed(IInvocation invocation) |
68 |
68. if ( this ._indent > 0) |
69 |
69. Console.Write( " " .PadRight( this ._indent * 4, ' ' )); |
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++) |
75 |
75. if (i != 0) Console.Write( ", " ); |
76 |
76. Console.Write(invocation.Arguments[i] == null |
78 |
78. : invocation.Arguments[i].GetType() == typeof ( string ) |
79 |
79. ? "\"" + invocation.Arguments[i].ToString() + "\"" |
80 |
80. : invocation.Arguments[i].ToString()); |
82 |
82. Console.WriteLine( ")" ); |
84 |
84. protected override void PostProceed(IInvocation invocation) |
只有在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,示例如下:
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
09 |
9. static void Main( string [] args) |
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()); |
16 |
16. InterfaceA objA = objB as InterfaceA; |
18 |
18. Console.ReadKey(); |
21 |
21. public interface InterfaceA |
25 |
25. public class ClassA : InterfaceA |
27 |
27. public void ActionA() |
29 |
29. Console.WriteLine( "I'm from ClassA" ); |
32 |
32. public class ClassB |
34 |
34. public virtual void ActionB() |
36 |
36. Console.WriteLine( "I'm from ClassB" ); |
40 |
40. public class CallingLogInterceptor : StandardInterceptor |
42 |
42. private int _indent = 0; |
43 |
43. protected override void PreProceed(IInvocation invocation) |
45 |
45. if ( this ._indent > 0) |
46 |
46. Console.Write( " " .PadRight( this ._indent * 4, ' ' )); |
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++) |
52 |
52. if (i != 0) Console.Write( ", " ); |
53 |
53. Console.Write(invocation.Arguments[i] == null |
55 |
55. : invocation.Arguments[i].GetType() == typeof ( string ) |
56 |
56. ? "\"" + invocation.Arguments[i].ToString() + "\"" |
57 |
57. : invocation.Arguments[i].ToString()); |
59 |
59. Console.WriteLine( ")" ); |
61 |
61. protected override void PostProceed(IInvocation invocation) |
可以看到代理对象同时具备了ClassA和ClassB的行为
五、导出、生成代理类型
Castle Dynamic Proxy允许我们将运行时生成的代理类型生成dll文件存到磁盘上,下次启动时通过加载这个dll文件可以避免动态生成代理类型
02 |
2. using Castle.Core.Interceptor; |
03 |
3. using Castle.DynamicProxy; |
05 |
5. namespace DynamicProxyExample |
09 |
9. static void Main( string [] args) |
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); |
16 |
16. ProxyGenerator generator = new ProxyGenerator(builder); |
17 |
17. ILogger logger = generator.CreateClassProxy<Logger>( new TestInterceptor()); |
19 |
19. logger.Write( "love" ); |
21 |
21. scope.SaveAssembly( true ); |
23 |
23. scope.SaveAssembly( false ); |
26 |
26. Console.ReadKey(); |
31 |
31. public class TestInterceptor : IInterceptor |
33 |
33. public void Intercept(IInvocation invocation) |
35 |
35. Console.Write( "I " ); |
36 |
36. invocation.Proceed(); |
37 |
37. Console.Write( " you. " ); |
42 |
42. public interface ILogger |
44 |
44. void Write( string message); |
48 |
48. public class Logger : ILogger |
50 |
50. public virtual void Write( string message) |
52 |
52. Console.Write(message); |
可以用reflector查看生成的dll,大致了解代理对象是如何工作的启动时,可以使用scope.LoadAssemblyIntoCache(assembly);将生成的代理类型加载到内存中,其中assembly需要我们手动加载
下载地址