Castle动态代理在类内部调用时无法拦截方法调用

16
我在使用Castle的动态代理时遇到了一些(我认为是)奇怪的行为。
以下是代码:
class Program
{
    static void Main(string[] args)
    {
        var c = new InterceptedClass();
        var i = new Interceptor();

        var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

        cp.Method1();
        cp.Method2();

        Console.ReadLine();
    }
}

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine(string.Format("Intercepted call to: " + invocation.Method.Name));

        invocation.Proceed();
    }
}

public class InterceptedClass
{
    public virtual void Method1()
    {
        Console.WriteLine("Called Method 1");
        Method2();
    }

    public virtual void Method2()
    {
        Console.WriteLine("Called Method 2");
    }
}

我预期的输出结果是:

  • 拦截到方法调用: Method1
  • 调用了方法 1
  • 拦截到方法调用: Method2
  • 调用了方法 2
  • 拦截到方法调用: Method2
  • 调用了方法 2

但是实际得到的却是:

  • 拦截到方法调用: Method1
  • 调用了方法 1
  • 调用了方法 2
  • 拦截到方法调用: Method2
  • 调用了方法 2

据我所知,动态代理只能代理从类外部发起的方法调用,因此当从InterceptedClass内部调用Method2时,它并没有被代理。我可以理解在从代理类内部进行调用时,它不再通过代理进行调用。但我想确认一下这是否是预期的行为,如果是的话,是否有任何方法可以拦截所有的方法调用,而不考虑它们来自哪里?

谢谢!

1个回答

19

编辑:简而言之 - 我刚刚尝试了另一种创建代理的方法,如下所述,它会生成你想要的输出。我只需要更改这个:

var c = new InterceptedClass();
var i = new Interceptor();

var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

变成这样:

var i = new Interceptor();
var cp = new ProxyGenerator().CreateClassProxy<InterceptedClass>(i);
据我理解,代理生成器实际上是创建了一个包装对象。它们是两个不同的对象 - 一个只是在另一个对象周围进行包装,包装层中有拦截等操作。
很难看出它如何能够改变InterceptedClass实例对其自己方法调用的执行方式:
- DynamicProxy无法更改现有对象的类型;一旦对象被创建,其类型就是固定的。 - DynamicProxy无法更改现有对象的现有调用如何绑定。
如果你想让Method1通过当前代理创建代码使用包装器来调用Method2,你需要向现有对象传递包装器,可以将其作为字段或方法参数传递。
或者,可能有一种不同的创建代理的方法 - 其中代理在某种程度上是目标对象。我怀疑你可能需要查看CreateClassProxy而不是CreateClassProxyWithTarget - 我认为提供目标对象是导致问题的原因。
当然,你所看到的行为是否“预期”的显然取决于你的期望 - 但这绝对是我在不知道Castle Dynamic Proxy的情况下所期望的 :)

是的,我也差不多猜到了。换句话说,“cp”的调用可以被拦截,而“c”的调用则不能,包括从“c”内部调用“c”的情况——这一点并不令人惊讶。顺便说一句:当我提问时,我应该考虑使用更具描述性的变量名,对吧? - George Goodchild
谢谢更新 - 使用CreateClassProxyWithTarget的原因是在我尝试解决的实际问题中,目标对象是由IoC容器创建的,然后我想用代理包装它。但是那个特定的问题超出了这个问题的范围。感谢您的帮助。 - George Goodchild
@George:你能说服IoC容器自己使用Castle Dynamic Proxy吗?正如你所说,这与这个问题稍微有些分离,但这可能是可行的... - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接