前几天看见在项目中用Castle来实现动态代理,觉得很有意思,就找个时间自己研究一下。介绍就免了,那开始吧。
public class AccountController : Controller
{
private AccountService accountService = new AccountService();
// GET: Account
public ActionResult Index(string name)
{
ViewBag.Msg = accountService.GetUser(name);
return View();
}
}
以前Service的实现基本上是IOC或者单例,今天我们就用动态代理来实现。
public ActionResult Index(string name)
{
var proxy = new ProxyGenerator(); //提供类和接口的代理对象
var interceptor =new AccountInterceptor(); //拦截器
//var logInterceptor = new LogInterceptor();
var accountService = proxy.CreateClassProxy<AccountService>(interceptor);
ViewBag.Msg = accountService.GetUser(name);
return View();
}
ProxyGenerator类会创建一个代理类来继承目标类,并重写目标类中虚方法。
public class AccountInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var arguments = invocation.GetArgumentValue(0) as string;
if (string.IsNullOrWhiteSpace(arguments))
{
invocation.ReturnValue = "nono";
invocation.SetArgumentValue(0, "Stranger");
invocation.Proceed();
invocation.ReturnValue = "Stranger";
return;
}
else if (arguments.Equals("陈绮贞"))
{
invocation.SetArgumentValue(0,arguments+"SAMA!");
invocation.Proceed();
return;
}
invocation.Proceed();
return;
}
}
拦截器需要需要实现IInterceptor接口,实现Intercept方法。Intercept接受一个实现了IInvocation接口类的参数,invocation个参数封装了一个代理的方法,其中Proceed用于调用目标方法。看一下方法注释。
// 摘要:(度娘翻译)
// Proceeds the call to the next interceptor in line, and ultimately to the target
// method.将调用到下一个拦截线,并最终向目标方法。
//
// 备注:
// Since interface proxies without a target don't have the target implementation
// to proceed to, it is important, that the last interceptor does not call this
// method, otherwise a System.NotImplementedException will be thrown.
没有目标的接口代理没有目标实现要继续进行,这是很重要的,即最后一个拦截不调用这个方法,否则会抛出 system.notimplementedexception。
void Proceed();
解释一下:ProxyGenerator的CreateClassProxy方法接受的是一个实现了IInterceptor接口的拦截器可变数组,并按顺序执行,类似一个调用链。Proceed被调用时,若果当前拦截器之后还有拦截器等待执行,则将调用传递到下一个拦截器,而不会去调用需要执行的方法,等到最后一个拦截器时才调用目标方法。如果我们调用的方法不是虚方法,也不会抛出异常的。(这里有个疑问:记得我在公司时,当我们调用的方法不是虚函数时也能被拦截到,Proceed方法不调用目标方法,然后拦截器结束后才会调用目标方法,今天debug时却发现拦截器一直没进去,不知道是不是由于使用的CreateClassProxy重载方法不同,明天去公司看一下。更新:虽然调用的方法不是虚函数,但是其内部调用了一个虚函数,这个虚函数被拦截到了,而不是调用的方法被拦截到。)
IInvocation接口里有一些方法可以使我们获取和设置目标方法的参数可返回值,在这里我们就可以做一些参数上的验证和日志的记录。
Castle的动态代理很简单明了,也是AOP实现方式的一种,有兴趣的可以了解一下。
PS:有错误欢迎指摘。