动态代理的一个最主要的应用场合就是实现AOP - 截获方法调用,加入自己的预处理、后处理或Around处理。
我在ESBasic.Emit中实现了对这些截获的支持。
首先,介绍两个截获者:
在上面的接口方法中都有一个参数InterceptedMethod,它封装了被截获的目标方法的基本信息。
好,如何使用ESBasic.Emit来创建动态代理了?很简单,你只需要调用
举个例子,我们需要使用动态代理截获IComputer所有方法调用抛出的异常,并记录日志。IComputer定义如下:
最后,提醒一下,如果你让Computer不从IComputer继承,运行结果也一样。你应该已经看到 DynamicProxyFactory .CreateAopProxy方法的第一个参数是object类型,而不是泛型T,这说明只要对应的object包含了和目标接口一样的方法(签名完全一致)就可以 -- 这就是所谓的“换脸”能力,可以在 这里 看到更详细的说明。
由于动态代理是使用Emit发射实现,所以效率上来说,和你手写的代码是没有区别的。
下载 ESBasic.Emit.dll。
我在ESBasic.Emit中实现了对这些截获的支持。
首先,介绍两个截获者:
///
<summary>
/// IMethodInterceptor对方法进行截获并加入预处理和后处理。
/// </summary>
public interface IMethodInterceptor
{
void PreProcess( InterceptedMethod method);
void PostProcess( InterceptedMethod method, object returnVal);
}
IMethodInterceptor 很容易理解,用于为截获插入预处理、后处理。这个非常容易理解。
/// IMethodInterceptor对方法进行截获并加入预处理和后处理。
/// </summary>
public interface IMethodInterceptor
{
void PreProcess( InterceptedMethod method);
void PostProcess( InterceptedMethod method, object returnVal);
}
///
<summary>
/// IAroundInterceptor对方法进行Around截获处理。注意,必须要触发目标方法的调用。
/// </summary>
public interface IAroundInterceptor
{
object AroundCall( InterceptedMethod method);
}
Around处理由IAroundInterceptor完成。什么是Around处理了?比如,我们想捕获目标方法的异常,需要使用TryCatch将目标方法Around起来,这就是Around处理的一个例子。
/// IAroundInterceptor对方法进行Around截获处理。注意,必须要触发目标方法的调用。
/// </summary>
public interface IAroundInterceptor
{
object AroundCall( InterceptedMethod method);
}
在上面的接口方法中都有一个参数InterceptedMethod,它封装了被截获的目标方法的基本信息。
public
sealed
class
InterceptedMethod
{
#region Ctor
public InterceptedMethod(){}
public InterceptedMethod( object _target,MethodInfo_method, object []paras)
{
this .target = _target;
this .method = _method;
this .arguments = paras;
}
#endregion
#region Method
private MethodInfomethod;
/// <summary>
/// Method被截获的目标方法
/// </summary>
public MethodInfo Method
{
get { return method;}
set {method = value;}
}
#endregion
#region Target
private object target;
/// <summary>
/// Target被截获的方法需要在哪个对象上调用。
/// </summary>
public object Target
{
get { return target;}
set {target = value;}
}
#endregion
#region Arguments
private object []arguments;
/// <summary>
/// Arguments调用被截获的方法的参数
/// </summary>
public object []Arguments
{
get { return arguments;}
set {arguments = value;}
}
#endregion
#region Invoke
/// <summary>
/// Invoke执行目标方法
/// </summary>
public object Invoke()
{
return this .method.Invoke( this .target, this .arguments);
}
#endregion
}
{
#region Ctor
public InterceptedMethod(){}
public InterceptedMethod( object _target,MethodInfo_method, object []paras)
{
this .target = _target;
this .method = _method;
this .arguments = paras;
}
#endregion
#region Method
private MethodInfomethod;
/// <summary>
/// Method被截获的目标方法
/// </summary>
public MethodInfo Method
{
get { return method;}
set {method = value;}
}
#endregion
#region Target
private object target;
/// <summary>
/// Target被截获的方法需要在哪个对象上调用。
/// </summary>
public object Target
{
get { return target;}
set {target = value;}
}
#endregion
#region Arguments
private object []arguments;
/// <summary>
/// Arguments调用被截获的方法的参数
/// </summary>
public object []Arguments
{
get { return arguments;}
set {arguments = value;}
}
#endregion
#region Invoke
/// <summary>
/// Invoke执行目标方法
/// </summary>
public object Invoke()
{
return this .method.Invoke( this .target, this .arguments);
}
#endregion
}
好,如何使用ESBasic.Emit来创建动态代理了?很简单,你只需要调用
public
static
TInterfaceCreateAopProxy
<
TInterface
>
(
object
origin,
IMethodInterceptor
methodInterceptor,
IAroundInterceptor
aroundInterceptor)
如果不需要某种截获处理,对应的参数传入null即可。
举个例子,我们需要使用动态代理截获IComputer所有方法调用抛出的异常,并记录日志。IComputer定义如下:
public
interface
IComputer
{
int Add( int a, int b);
}
public class Computer: IComputer
{
public int Add( int a, int b)
{
throw new Exception( " TestException " );
return a + b;
}
}
我可以使用Around截获者来截获异常,所以我实现一个自己的IAroundInterceptor:
{
int Add( int a, int b);
}
public class Computer: IComputer
{
public int Add( int a, int b)
{
throw new Exception( " TestException " );
return a + b;
}
}
public
class
ExceprionAroundInterceptor
:
IAroundInterceptor
{
#region IAroundInterceptor成员
public object AroundCall( InterceptedMethod method)
{
try
{
return method.Invoke();
}
catch (Exceptionee)
{
ee = ee;
// ..logException
throw ;
}
}
#endregion
}
现在,我们可以这样获得针对IComputer的动态代理的引用:
{
#region IAroundInterceptor成员
public object AroundCall( InterceptedMethod method)
{
try
{
return method.Invoke();
}
catch (Exceptionee)
{
ee = ee;
// ..logException
throw ;
}
}
#endregion
}
IComputer
proxy
=
ESBasic.Emit.Application.
DynamicProxyFactory
.CreateAopProxy
<
IComputer
>
(
new
Computer
(),
null
,
new
ExceprionAroundInterceptor
());
proxy.Add( 1 , 2 );
这样就ok了,Add方法抛出的异常会被
ExceprionAroundInterceptor
截获。
proxy.Add( 1 , 2 );
最后,提醒一下,如果你让Computer不从IComputer继承,运行结果也一样。你应该已经看到 DynamicProxyFactory .CreateAopProxy方法的第一个参数是object类型,而不是泛型T,这说明只要对应的object包含了和目标接口一样的方法(签名完全一致)就可以 -- 这就是所谓的“换脸”能力,可以在 这里 看到更详细的说明。
由于动态代理是使用Emit发射实现,所以效率上来说,和你手写的代码是没有区别的。
下载 ESBasic.Emit.dll。