zhuweisky

君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。

ESFramework,基于.NET的通信框架。DataRabbit,轻量的数据访问框架。sky.zhuwei@163.com
posts - 191, comments - 1276, trackbacks - 94, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

使用动态代理,提高工作效率

Posted on 2008-05-21 17:42 zhuweisky 阅读(2724) 评论(10)  编辑 收藏 所属分类: Emit
    动态代理的一个最主要的应用场合就是实现AOP - 截获方法调用,加入自己的预处理、后处理或Around处理。
    我在ESBasic.Emit中实现了对这些截获的支持。
    首先,介绍两个截获者:
    /// <summary>
    
/// IMethodInterceptor 对方法进行截获并加入预处理和后处理。
    
/// </summary>
    public interface IMethodInterceptor
    {
        
void PreProcess(InterceptedMethod method);

        
void PostProcess(InterceptedMethod method ,object returnVal);
    }
    IMethodInterceptor 很容易理解,用于为截获插入预处理、后处理。这个非常容易理解。
     /// <summary>
    
/// IAroundInterceptor 对方法进行Around截获处理。注意,必须要触发目标方法的调用。
    
/// </summary>
    public interface IAroundInterceptor
    {
        
object AroundCall(InterceptedMethod method);
    }
    Around处理由IAroundInterceptor完成。什么是Around处理了?比如,我们想捕获目标方法的异常,需要使用TryCatch将目标方法Around起来,这就是Around处理的一个例子。
    在上面的接口方法中都有一个参数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 MethodInfo method;
        
/// <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 TInterface CreateAopProxy<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
    public class ExceprionAroundInterceptor : IAroundInterceptor
    {
        
#region IAroundInterceptor 成员
        
public object AroundCall(InterceptedMethod method)
        {
            
try
            {
                
return method.Invoke();
            }
            
catch (Exception ee)
            {
                ee 
= ee;
                
//.. log Exception

                
throw;
            }
        }
        
#endregion
    }
    现在,我们可以这样获得针对IComputer的动态代理的引用:
    IComputer proxy = ESBasic.Emit.Application.DynamicProxyFactory.CreateAopProxy<IComputer>(new Computer() ,null ,new ExceprionAroundInterceptor()) ;
    proxy.Add(
12);
    这样就ok了,Add方法抛出的异常会被ExceprionAroundInterceptor截获。

    最后,提醒一下,如果你让Computer不从IComputer继承,运行结果也一样。你应该已经看到DynamicProxyFactory.CreateAopProxy方法的第一个参数是object类型,而不是泛型T,这说明只要对应的object包含了和目标接口一样的方法(签名完全一致)就可以 -- 这就是所谓的“换脸”能力,可以在这里看到更详细的说明。
    由于动态代理是使用Emit发射实现,所以效率上来说,和你手写的代码是没有区别的。

    下载 ESBasic.Emit.dll。

    











Feedback

#1楼    回复  引用    

2008-05-21 18:18 by 不然 [未注册用户]
学习中。。。

#2楼    回复  引用  查看    

2008-05-21 20:35 by 簡簡單單..      
路过! 还是自己写的动态代理好用..

#3楼    回复  引用  查看    

2008-05-21 20:41 by henry      
我也是干这事,但期拦载方式不同。
[Aspect(typeof(IComputer ))]
public int Add(int a, int b)
{
try
{
return MethodContext.Invoke<int>(a,b);
}
catch (Exception ee)
{
ee = ee;
//.. log Exception

throw;
}

}
这段时间搞只基于方法的泛型方法搞到一头烟后才搞定。

#4楼    回复  引用  查看    

2008-05-21 20:43 by henry      
还有楼主你可以用些IOC组件,把原有接口实现类变成可配置的这样就灵活很多了。

#5楼 [楼主]   回复  引用  查看    

2008-05-21 21:14 by zhuweisky      
@ henry:基于Attribute的实现方式,我也做过,请参见:http://www.cnblogs.com/zhuweisky/archive/2005/09/28/245578.html。这种方式无论是效率还是灵活性都不如Emit方式来得好。

实际上已经可以直接使用Spring.NET配置ESBasic.Emit.Application.DynamicProxyFactory来将动态代理的引用存放在IOC容器中。

#6楼    回复  引用  查看    

2008-05-21 21:21 by henry      
@zhuweisky
我的也是Emit,而并不是用.net的代理。
[Aspect(typeof(IComputer ))] 是用于描述方法是切入于那个接口的相关方法:)

#7楼    回复  引用  查看    

2008-05-21 22:18 by 自由、创新、研究、探索……      
用PostSharp来实现AOP更好,更有效率。
关于PostSharp可参看http://www.openbeta.cn/postsharp.ashx

#8楼    回复  引用  查看    

2008-05-21 22:23 by henry      
@自由、创新、研究、探索……
这个出来也有好一段时间,当时看她的相关视频感到很不可思议,后来了解一下是通过插件生成静态。

#9楼    回复  引用  查看    

2008-05-22 11:37 by 隐姓埋名      
晕!

为什么 我看不懂呢!

哪位 大虾指点下撒!

#10楼    回复  引用    

2008-05-28 09:57 by woa [未注册用户]
一个东西好不好要在实际应用中才可见真知,楼主把自己的方法公布出来,很好

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-09-19 16:50 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: