【问题标题】:How do I intercept a method invocation with standard java features (no AspectJ etc)?如何拦截具有标准 Java 功能(无 AspectJ 等)的方法调用?
【发布时间】:2010-10-09 06:44:50
【问题描述】:

我想拦截对某个类 MyClass 的所有方法调用,以便能够对某些 setter 调用做出反应。

我尝试使用动态代理,但据我所知,这仅适用于实现某些接口的类。但是MyClass没有这样的接口。

除了实现一个包装类之外,还有其他方法可以将所有调用委托给一个成员,该成员是 MyClass 的一个实例,还是使用 AOP?

【问题讨论】:

    标签: java reflection methods


    【解决方案1】:

    如果你准备做一些非常丑陋的事情,看看:

    http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/

    基本上,调试器接口应该允许您像调试器一样进行附加,从而拦截调用。请记住,我认为这是一个真的坏主意,但您问是否可能。

    【讨论】:

      【解决方案2】:

      AspectJ 中没有太多的魔法。您可以编写自己的代理。 http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html 似乎是一个不错的起点。

      【讨论】:

      • 我认为您低估了生产质量代理所需的工作。
      • 抱歉,我并不是要剥夺 AspectJ 的任何功劳。这是一件很棒的事情,我正在非常成功地使用它。我只是想说一个简单的拦截方法的代理不是很难写。感谢您纠正我。
      【解决方案3】:

      Java 没有任何用于方法拦截的实际语言功能(不确定是否有任何静态语言)

      我有点喜欢 Nick 使用调试器接口的想法,这很刻薄。

      我认为您需要的简短回答是:不,没有办法在不使用代理或包装器实际替换类的情况下拦截 Java 中的方法调用。

      注意:AOP 库只是自动执行此操作。

      【讨论】:

        【解决方案4】:

        一些 Java 大师可能对此不以为然,但我在完全避免原始类型和 setter 方面取得了一些成功。我的班级是这样的:

        class Employee extends SmartPojo {
            public SmartString name;
            public SmartInt age;
        }
        

        您会注意到两件事:1. 一切都是公开的。 2. 没有构造函数。

        魔法发生在 SmartPojo 中,它搜索实现“Smart”接口的任何字段并对其进行初始化。由于这不是原始类(也不是最终类),我可以在一个地方为模型中任何位置的所有字段添加 set() 和 get() 方法。所以不再浪费 setter/getter,添加通知(也在一个地方)等非常简单。

        没错,这不再是 POJO,而且在大多数方面它都不是 Bean,但我发现这些旧想法对我的限制大于它们的帮助。 YMMV。

        【讨论】:

        • 这基本上就是 grails 对其域对象所做的。不过,因为他们可以用 groovy 实现它,所以它更好,而且他们不需要让每个属性都实现一个“智能”接口。
        • 我需要一个纯 Java 的解决方案。我也用 Groovy 尝试过这个,虽然代码更紧凑,但这并不好。我是 Groovy 的忠实粉丝,但我觉得 1.x 仍然有点……“不完整”。
        【解决方案5】:
        1. 为什么你的类不能实现接口?您可以从中提取一些接口,其中包含您想要拦截的所有方法并轻松使用动态代理机制。使用接口而不是类进行编码也是一种很好的编程习惯。

        2. 您可以使用带有 Spring AOP 功能的 Spring 框架(内部使用动态代理)来执行此操作。您只需在配置文件中将您的类定义为 Spring bean,并且您的类的客户端必须从 Spring 应用程序上下文中获取其实例或自动作为依赖项(例如通过定义 setMyClass(MyClass mc) 方法) )。从那里您可以轻松地定义一个切入该类的所有方法调用的切面。

        【讨论】:

        • 也许他正在尝试调试一个他没有编写包含在某个 3rd 方库中的类?
        【解决方案6】:

        正如您所注意到的,您不能使用 JDK 动态代理(无接口),但使用 Spring 和 CGLIB(Spring 随附的 JAR),您可以执行以下操作:

        public class Foo
        {
            public void setBar()
            {
                throw new UnsupportedOperationException("should not go here");
            }
        
            public void redirected()
            {
                System.out.println("Yiha");
            }
        }
        
        Foo foo = new Foo();
        ProxyFactory pf = new ProxyFactory(foo);
        
        pf.addAdvice(new MethodInterceptor()
        {
            public Object invoke(MethodInvocation mi) throws Throwable
            {
                if (mi.getMethod().getName().startsWith("set"))
                {
                    Method redirect = mi.getThis().getClass().getMethod("redirected");
                    redirect.invoke(mi.getThis());
                }
                return null;
            }
        });
        
        Foo proxy = (Foo) pf.getProxy();
        proxy.setBar(); // prints "Yiha"
        

        【讨论】:

        • 拦截方法时,是否可以读取传递给它的参数?
        • 是的,这是可能的... for(Object arg : mi.getArguments()) { sysout(arg.getClass() + " -- " + arg); }
        【解决方案7】:

        我只是为此目的开发了一个小框架。 您可以通过以下方式查看:http://code.google.com/p/java-interceptor/(使用 svn 进行查看)。

        【讨论】:

        • 您能否简要解释一下这是如何工作的/它的作用是什么?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多