Castle 动态代理介绍
CLR提供的代理功能非常棒。ProxyAttribute
、
message sinks等奇妙的想法,使对平台进行扩展非常容易。
不过,这里还是有些缺点,例如:如果要使用代理功能,你的类必须继承自MarshalByRef
or ContextBoundObject
(该类型被另外的环境所支持),通过这样做,你扰乱了类的对象模型层次,这是一种高侵入。不过,如果你需要自己来控制对象模型,那么这种方式是可选的,
这篇文章说明了怎么使用Castle Project中的动态代理,使用一个快速、干净的方式为你的类建立一个拦截器(interceptors),如动态代理一样高效,不会使用反射机制去调用一个对象实例的方法。
怎么使用
动态代理能代理接口和实现类,你总是需要提供被代理对象的类型和拦截器的实例,拦截器将会在对代理的每次方法调用的时候被调用,所以你可以在拦截器逻辑中承载一些逻辑(日志、事务等)和然后决定是否继续处理该调用,如果需要继续处理,你必须调用Proceed
方法。
2
3 {
4 public object Intercept(IInvocation invocation, params object[] args)
5 {
6 DoSomeWorkBefore(invocation, args);
7
8 object retValue = invocation.Proceed( args );
9
10 DoSomeWorkAfter(invocation, retValue, args);
11
12 return retValue;
13 }
14 }
15
IInvocation接口向你提供了一系列有用的信息:
2 {
3 object Proxy { get; }
4
5 object InvocationTarget { get; set; }
6
7 MethodInfo Method { get; }
8
9 object Proceed( params object[] args );
10 }
11
为了实现接口,你必须标识出目标对象实例,复杂么?其实不然,考虑下面一个例子:
2 {
3 int Calc(int x, int y);
4 int Calc(int x, int y, int z, Single k);
5 }
6
7 public class MyInterfaceImpl : IMyInterface
8 {
9 public virtual int Calc(int x, int y)
10 {
11 return x + y;
12 }
13
14 public virtual int Calc(int x, int y, int z, Single k)
15 {
16 return x + y + z + (int)k;
17 }
18 }
19
20 ProxyGenerator generator = new ProxyGenerator();
21
22 IMyInterface proxy = (IMyInterface) generator.CreateProxy(
23 typeof(IMyInterface), new StandardInterceptor(), new MyInterfaceImpl() );
24
这个就是作为DynamicProxy的需求,需要一个针对调用的默认目标
后面我们将解释为什么。
现在,如果你需要代理一个真实类,这个类不能是sealed,而且只有virtual方法才能被拦截,理由是动态代理将为该类建立一个子类,该子类将重载所有方法,所以能把调用放到拦截器中。看下面一个例子:
2
3 Hashtable proxy = (Hashtable) generator.CreateClassProxy(
4 typeof(Hashtable), new HashtableInterceptor() );
5
6 object value = proxy["key"]; // == "default"
7
8 public class HashtableInterceptor : StandardInterceptor
9 {
10 public override object Intercept(IInvocation invocation, params object[] args)
11 {
12 if (invocation.Method.Name.Equals("get_Item"))
13 {
14 object item = base.Intercept(invocation, args);
15 return (item == null) ? "default" : item;
16 }
17 return base.Intercept(invocation, args);
18 }
19 }
20
如果你不希望代理的类暴露出一个默认的构造函数,OK,你只需要提供一些参数给CreateClassProxy
Mixins
Mixins是一种在C++世界很著名的继承,Mixin样式的继承,能将一个类和其他的类进行混合,同时暴露出单独的能力,动态代理允许你一个类和其他的类进行混合,结果将生成一个混合的代理实例,如果混合类暴露接口,他们将自动通过代理实例进行暴露。
2 {
3 int DoSomething();
4 }
5
6 public class SimpleMixin : ISimpleMixin
7 {
8 public int DoSomething()
9 {
10 return 1;
11 }
12 }
13
14 ProxyGenerator generator = new ProxyGenerator();
15 GeneratorContext context = new GeneratorContext();
16
17 SimpleMixin mixin_instance = new SimpleMixin();
18
19 context.AddMixinInstance( mixin_instance );
20
21 SimpleClass proxy = (SimpleClass) generator.CreateCustomClassProxy(
22 typeof(SimpleClass), interceptor, context );
23
24 ISimpleMixin mixin = (ISimpleMixin) proxy;
25
象Ruby一样的动态语言是mixins的天堂,每种混合的模块通过目标类暴露,但是那是另一个讨论的主题。
Aspect#使mixins更上了一个台阶,他实现了一个协议,允许mixins实例得到代理实例的引用,然而动态代理将这种决定带给了开发人员。
内部实现
动态代理生成了一个类,类似如下代码
2 {
3 // Exposes all constructors
4
5 // Overrides all methods
6
7 public override int DoSomething(int x)
8 {
9 MethodInfo m = ldtoken currentMethod;
10 IInvocation invocation = ObtainInvocationFor( deletegateForDoSomething,
11 callableForDoSomething, m);
12 return (int) _interceptor.Intercept( invocation, x );
13 }
14
15 private int BaseCallDoSomething(int x)
16 {
17 return base.DoSomething(x);
18 }
19 }
20
正如你所见到的,动态代理依靠委托达到很好的性能,唯一的性能瓶井是在对值类型进行装箱和拆箱工作上。
基本上,每个代理都有一个指针指向调用基类方法的另外一个方法,他会在你使用IInvocation
实例上的Proceed
时发生。
结论
对于程序集的静态织入,可以考虑RAIL项目,如果你需要的是动态,则考虑动态代理。同时,我也需要谢谢所有的回馈和从Aspect#和Castle开发邮件列表中收到的补丁,动态代理的用户给了我一次又一次重构的力量。
关于动态代理的更新信息,请检查其站点
文章出处:http://www.codeproject.com/csharp/hamiltondynamicproxy.asp