Castle 系列:Castle DynamicProxy动态生成透明代理类型详解
来源:互联网 发布:有人gprs模块与单片机 编辑:程序博客网 时间:2024/06/01 07:47
Castle DynamicProxy动态生成透明代理类型,
实体不需要继承MarshalByRef、ContextBoundObject便可以实现代理类
基于透明代理的功能,可以实现对方法调用的拦截处理,例如NHibernate用它实现延迟加载
DP的使用非常简单,内部没有使用反射,而是采用Emit、委托等方式生成代理类型,调用真实类的方法,性能方面也没有太多损失
基本示例
引用的命名空间:
using
Castle.Core.Interceptor;
using
Castle.DynamicProxy;
1
2
using
Castle.Core.Interceptor;
using
Castle.DynamicProxy;
1
2
3
4
5
6
7
8
9
public
class
SimpleSamepleEntity
{
public
virtual
string
Name {
get
;
set
; }
public
virtual
int
Age {
get
;
set
; }
public
override
string
ToString()
{
return
string
.Format(
"{{ Name: \"{0}\", Age: {1} }}"
,
this
.Name,
this
.Age);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public
class
CallingLogInterceptor : IInterceptor
{
private
int
_indent = 0;
private
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(
")"
);
}
private
void
PostProceed(IInvocation invocation)
{
this
._indent--;
}
public
void
Intercept(IInvocation invocation)
{
this
.PreProceed(invocation);
invocation.Proceed();
this
.PostProceed(invocation);
}
}
1
2
3
4
5
6
7
8
ProxyGenerator generator =
new
ProxyGenerator();
CallingLogInterceptor interceptor =
new
CallingLogInterceptor();
SimpleSamepleEntity entity = generator.CreateClassProxy<SimpleSamepleEntity>(interceptor);
entity.Name =
"Richie"
;
entity.Age = 50;
Console.WriteLine(
"The entity is: "
+ entity);
Console.WriteLine(
"Type of the entity: "
+ entity.GetType().FullName);
Console.ReadKey();
上面示例使用CreateClassProxy方法创建了一个代理对象entity,从图中输出的entity类型可以看到他的类型为Castle.Proxies.SimpleSampleEntity
当调用代理对象的任何方法时,将使用CallingLogInterceptor这个拦截器进行处理,在CallingLogInterceptor中输出了调用日志信息
在拦截器的接口方法Intercept中,可以修改入参,决定是否调用真实方法,修改返回结果等
可以拦截的方法:
1. 对于Class Proxy类型的代理,因为DP使用继承和override虚方法的方式实现代理,所以被拦截的方法必须为virtual类型,非virtual类型的方法无法拦截
2. 实体继承自Object、MarshalByRefObject等对象的方法,如果在实体类中没有override将无法拦截
DP会将动态创建的代理类型缓存起来
Interceptor Pipeline
创建代理对象时可以指定多个拦截器,例如再简单的实现一个拦截器:
1
2
3
4
5
6
7
8
public
class
SimpleLogInterceptor : IInterceptor
{
public
void
Intercept(IInvocation invocation)
{
Console.WriteLine(
">>"
+ invocation.Method.Name);
invocation.Proceed();
}
}
1
2
3
4
5
6
7
8
9
ProxyGenerator generator =
new
ProxyGenerator();
SimpleSamepleEntity entity = generator.CreateClassProxy<SimpleSamepleEntity>(
new
SimpleLogInterceptor(),
new
CallingLogInterceptor());
entity.Name =
"Richie"
;
entity.Age = 50;
Console.WriteLine(
"The entity is: "
+ entity);
Console.WriteLine(
"Type of the entity: "
+ entity.GetType().FullName);
Console.ReadKey();
多个拦截器之间以pipeline方式处理调用顺序:
实际中并不一定所有方法都需要运用全部的拦截器,对方法调用有选择性的选择拦截器有2种方式,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public
class
InterceptorSelector : IInterceptorSelector
{
public
IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
{
if
(method.Name.StartsWith(
"set_"
))
return
interceptors;
else
return
interceptors.Where(i => i
is
CallingLogInterceptor).ToArray<IInterceptor>();
}
}
public
class
InterceptorFilter : IProxyGenerationHook
{
public
bool
ShouldInterceptMethod(Type type, MethodInfo memberInfo)
{
return
memberInfo.IsSpecialName &&
(memberInfo.Name.StartsWith(
"set_"
) || memberInfo.Name.StartsWith(
"get_"
));
}
public
void
NonVirtualMemberNotification(Type type, MemberInfo memberInfo)
{
}
public
void
MethodsInspected()
{
}
}
1
2
3
4
5
6
7
8
9
10
ProxyGenerator generator =
new
ProxyGenerator();
var options =
new
ProxyGenerationOptions(
new
InterceptorFilter()) { Selector =
new
InterceptorSelector() };
SimpleSamepleEntity entity = generator.CreateClassProxy<SimpleSamepleEntity>(
options,
new
SimpleLogInterceptor(),
new
CallingLogInterceptor());
entity.Name =
"Richie"
;
entity.Age = 50;
Console.WriteLine(
"The entity is: "
+ entity);
Console.WriteLine(
"Type of the entity: "
+ entity.GetType().FullName);
Console.ReadKey();
IProxyGenerationHook接口决定整个方法是否运用拦截器,他是在动态构造代理类型的时候使用的;而IInterceptorSelector接口决定某个方法该运用哪些拦截器,他在每次调用被拦截的方法时执行
上面的示例只对setter和getter方法进行拦截,并且对getter方法只使用CallingLogInterceptor这个拦截器
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基本类似,但他提供了一个更改被代理对象(真实对象)的机会,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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();
}
}
1
2
3
4
5
6
7
8
9
10
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();
只有在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,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"
);
}
}
1
2
3
4
5
6
7
8
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();
可以看到代理对象同时具备了ClassA和ClassB的行为
导出、生成代理类型
Castle Dynamic Proxy允许我们将运行时生成的代理类型生成dll文件存到磁盘上,下次启动时通过加载这个dll文件可以避免动态生成代理类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var scope =
new
ModuleScope(
true
,
ModuleScope.DEFAULT_ASSEMBLY_NAME,
ModuleScope.DEFAULT_FILE_NAME,
"DynamicProxyTest.Proxies"
,
"DynamicProxyTest.Proxies.dll"
);
var builder =
new
DefaultProxyBuilder(scope);
var generator =
new
ProxyGenerator(builder);
var options =
new
ProxyGenerationOptions(
new
InterceptorFilter())
{
Selector =
new
InterceptorSelector()
};
SimpleSamepleEntity entity = generator.CreateClassProxy<SimpleSamepleEntity>(
options,
new
SimpleLogInterceptor(),
new
CallingLogInterceptor());
IStorageNode node = generator.CreateInterfaceProxyWithTargetInterface<IStorageNode>(
new
StorageNode(
"master"
)
,
new
DualNodeInterceptor(
new
StorageNode(
"slave"
))
,
new
CallingLogInterceptor());
options =
new
ProxyGenerationOptions();
options.AddMixinInstance(
new
ClassA());
ClassB objB = generator.CreateClassProxy<ClassB>(options,
new
CallingLogInterceptor());
scope.SaveAssembly(
false
);
可以用reflector查看生成的dll,大致了解代理对象是如何工作的
启动时,可以使用
scope.LoadAssemblyIntoCache(assembly);
将生成的代理类型加载到内存中,其中assembly需要我们手动加载
参考:
Castle Dynamic Proxy Tutorial
Castle's DynamicProxy for .NET
- Castle 系列:Castle DynamicProxy动态生成透明代理类型详解
- Castle 系列: Castle DynamicProxy动态生成透明代理类型
- .Net动态代理Castle系列
- .Net动态代理Castle系列
- Castle.DynamicProxy的二次开发
- Castle学习系列(十二)---Windsor和DynamicProxy实现AOP
- 循序渐进学习Castle.DynamicProxy AOP
- AOP:PostSharp和Castle DynamicProxy
- castle动态代理的使用
- Castle.DynamicProxy实现WCF接口服务端模拟
- Castle
- Castle
- .Net动态代理Castle ( 了解创建动态代理 )
- 动态代理DynamicProxy介绍
- 动态代理DynamicProxy 介绍
- Castle 开发系列文章
- Castle 开发系列文章
- Castle 开发系列文章
- ORA-00257 archiver error. 错误的处理方法
- Android OpenGL ES 分析与实践(4)
- c/c++试题
- Android 获取坐标
- Android OpenGL ES 分析与实践 (3)
- Castle 系列:Castle DynamicProxy动态生成透明代理类型详解
- android 中bitmap 缩放和阴影效果
- Java学习之路
- 服务器处理能力,你估算正确过吗?
- linux下查找某个文件位置的方法
- 第十周实验报告(二)
- Phone界面上文本框UITextField的软键盘隐藏和移动键盘下面的内容和限制字数
- PL/SQL导出sql脚本
- 啊