您的位置:首页 > 运维架构

循序渐进学习Castle.DynamicProxy AOP

2012-04-08 08:57 549 查看
http://www.it118.org/Specials/321869dd-98cb-431b-b6d2-82d973cd739d/0c2ad54b-387e-4761-8013-6d2007040051.htm

标签:Castle,AOP,Castle.DynamicProxy

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

项目地址:Castle Dynamic Proxy

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

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

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{

class Program
{

static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

Logger logger = generator.CreateClassProxy<Logger>(new TestInterceptor()); //泛型版

//Logger logger = generator.CreateClassProxy(typeof(Logger), new TestInterceptor()) as Logger; //非泛型版

logger.Write("love");
}
}

public class TestInterceptor : IInterceptor

{
public void Intercept(IInvocation invocation)

{
Console.Write("I ");
invocation.Proceed();
Console.Write(" you.");

}
}

public class Logger

{
public virtual
void Write(string message)

{
Console.Write(message);
}
}
}

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

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

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

1.重构增加一个接口

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{

static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());

logger.Write("love");

}
}

//拦截器
public class TestInterceptor : IInterceptor

{
public void Intercept(IInvocation invocation)

{
Console.Write("I ");
invocation.Proceed();
Console.Write(" you. ");
}
}

//接口
public interface ILogger

{
void Write(string message);

}

//实现类
public class Logger:ILogger

{
public virtual
void Write(string message)

{
Console.Write(message);
}
}
}

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

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

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{

static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());

logger.Write("love");

}
}

//拦截器
public class TestInterceptor : IInterceptor

{
public void Intercept(IInvocation invocation)

{
PreProceed(invocation);
invocation.Proceed();
PostProceed(invocation);
}

private void PreProceed(IInvocation invocation)

{
Console.Write("I ");

}
private void PostProceed(IInvocation invocation)

{
Console.Write(" you. ");

}
}

//接口
public interface ILogger

{
void Write(string message);

}

//实现类
public class Logger:ILogger

{
public virtual
void Write(string message)

{
Console.Write(message);
}
}
}

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

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

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{
static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());

logger.Write("love");
}
}

//拦截器
public class TestInterceptor : StandardInterceptor

{
protected override
void PreProceed(IInvocation invocation)

{
Console.Write("I ");

}
protected override
void PostProceed(IInvocation invocation)

{
Console.Write(" you. ");

}
}

//接口
public interface ILogger

{
void Write(string message);

}

//实现类
public class Logger:ILogger

{
public virtual
void Write(string message)

{
Console.Write(message);
}
}
}

无需赘述。

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

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{
static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor1(),
new TestInterceptor2() );

logger.Write("love");
}
}

#region 拦截器

//拦截器1
public class TestInterceptor1 : StandardInterceptor

{
protected override
void PreProceed(IInvocation invocation)

{
Console.Write("(张三 ");

}
protected override
void PostProceed(IInvocation invocation)

{
Console.Write(" 张四) ");

}
}

//拦截器2
public class TestInterceptor2 : StandardInterceptor

{
protected override
void PreProceed(IInvocation invocation)

{
Console.Write(" and 李三) ");
}
protected override
void PostProceed(IInvocation invocation)

{
Console.Write(" (李四 ");
}
}
#endregion

//接口
public interface ILogger

{
void Write(string message);

}

//实现类
public class Logger:ILogger

{
public virtual
void Write(string message)

{
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基本类似,但他提供了一个更改被代理对象(真实对象)的机会,示例如下:

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{
static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

IStorageNode node = generator.CreateInterfaceProxyWithTargetInterface<IStorageNode>(

new StorageNode("master")

, new DualNodeInterceptor(new StorageNode("slave"))

, new CallingLogInterceptor());

node.Save("my message"); //应该调用master对象

node.IsDead = true;

node.Save("my message"); //应该调用slave对象

node.Save("my message");
//应该调用master对象
Console.ReadKey();
}
}
public interface IStorageNode

{
bool IsDead { get;
set; }
void Save(string message);

}
public class StorageNode : IStorageNode

{
private string _name;

public StorageNode(string name)

{
this._name = name;

}
public bool IsDead {
get; set; }

public void Save(string message)

{
Console.WriteLine(string.Format("\"{0}\" was saved to {1}", message,
this._name));
}
}
public class DualNodeInterceptor : IInterceptor

{
private IStorageNode _slave;

public DualNodeInterceptor(IStorageNode slave)

{
this._slave = slave;
}
public void Intercept(IInvocation invocation)

{
IStorageNode master = invocation.InvocationTarget
as IStorageNode;
if (master.IsDead)

{
IChangeProxyTarget cpt = invocation as IChangeProxyTarget;

//将被代理对象master更换为slave
cpt.ChangeProxyTarget(this._slave);

//测试中恢复master的状态,以便看到随后的调用仍然使用master这一效果

master.IsDead = false;

}
invocation.Proceed();
}
}

public class CallingLogInterceptor : StandardInterceptor

{
private int _indent = 0;

protected override
void PreProceed(IInvocation invocation)

{
if (this._indent > 0)

Console.Write(" ".PadRight(this._indent * 4,
' '));
this._indent++;
Console.Write("Intercepting: " + invocation.Method.Name +
"(");
if (invocation.Arguments != null && invocation.Arguments.Length > 0)

for (int i = 0; i < invocation.Arguments.Length; i++)

{
if (i != 0) Console.Write(", ");

Console.Write(invocation.Arguments[i] == null

? "null"
: invocation.Arguments[i].GetType() == typeof(string)

? "\"" + invocation.Arguments[i].ToString() +
"\""
: invocation.Arguments[i].ToString());
}
Console.WriteLine(")");
}
protected override
void PostProceed(IInvocation invocation)

{
this._indent--;
}
}
}

只有在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,示例如下:

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{
static void Main(string[] args)

{
ProxyGenerator generator = new ProxyGenerator();

var options = new ProxyGenerationOptions();

options.AddMixinInstance(new ClassA());

ClassB objB = generator.CreateClassProxy<ClassB>(options,
new CallingLogInterceptor());
objB.ActionB();
InterfaceA objA = objB as InterfaceA;

objA.ActionA();
Console.ReadKey();
}
}
public interface InterfaceA

{
void ActionA();
}
public class ClassA : InterfaceA

{
public void ActionA()

{
Console.WriteLine("I'm from ClassA");

}
}
public class ClassB

{
public virtual
void ActionB()
{
Console.WriteLine("I'm from ClassB");

}
}

public class CallingLogInterceptor : StandardInterceptor

{
private int _indent = 0;

protected override
void PreProceed(IInvocation invocation)

{
if (this._indent > 0)

Console.Write(" ".PadRight(this._indent * 4,
' '));
this._indent++;
Console.Write("Intercepting: " + invocation.Method.Name +
"(");
if (invocation.Arguments !=
null && invocation.Arguments.Length > 0)

for (int i = 0; i < invocation.Arguments.Length; i++)

{
if (i != 0) Console.Write(", ");

Console.Write(invocation.Arguments[i] ==
null
? "null"
: invocation.Arguments[i].GetType() ==
typeof(string)
? "\"" + invocation.Arguments[i].ToString() +
"\""
: invocation.Arguments[i].ToString());
}
Console.WriteLine(")");

}
protected override
void PostProceed(IInvocation invocation)

{
this._indent--;
}
}
}

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

五、导出、生成代理类型

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

using System;
using Castle.Core.Interceptor;

using Castle.DynamicProxy;

namespace DynamicProxyExample

{
class Program
{
static void Main(string[] args)

{
String path = AppDomain.CurrentDomain.BaseDirectory;

ModuleScope scope = new ModuleScope(true,
"Invocation", path + "\\Invocation.dll",
"Proxy", path + "\\Proxy.dll");

DefaultProxyBuilder builder = new DefaultProxyBuilder(scope);

ProxyGenerator generator = new ProxyGenerator(builder);

ILogger logger = generator.CreateClassProxy<Logger>(new TestInterceptor());

logger.Write("love");

scope.SaveAssembly(true);
//加这句话可以将动态生成的Invocation类保存到本地硬盘

scope.SaveAssembly(false);
//加这句话可以将动态生成的Proxy类保存到本地硬盘

Console.ReadKey();
}
}

//拦截器
public class TestInterceptor : IInterceptor

{
public void Intercept(IInvocation invocation)

{
Console.Write("I ");

invocation.Proceed();
Console.Write(" you. ");

}
}

//接口
public interface ILogger

{
void Write(string message);

}

//实现类
public class Logger : ILogger

{
public virtual
void Write(string message)

{
Console.Write(message);
}
}
}

可以用reflector查看生成的dll,大致了解代理对象是如何工作的启动时,可以使用scope.LoadAssemblyIntoCache(assembly);将生成的代理类型加载到内存中,其中assembly需要我们手动加载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: