views:

2206

answers:

6

I want to intercept all method invocations to some class MyClass to be able to react on some setter-invocations.

I tried to use dynamic proxies, but as far as I know, this only works for classes implementing some interface. But MyClass does not have such an interface.

Is there any other way, besides implementing a wrapper class, that delegates all invocations to a member, which is an instance of the MyClass or besided using AOP?

A: 

If you are prepared to do something really ugly, have a look at:

http://java.sun.com/javase/technologies/core/toolsapis/jpda/

Basically the debugger interface ought to allow you to attach like a debugger, and hence intercept calls. Bear in mind I think this is a really bad idea, but you asked if it was possible.

Nick Fortescue
A: 

There isn't a lot of magic in AspectJ. You can write your own agent. http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html seems to be good starting point.

Hemal Pandya
+1  A: 

Java doesn't have any actual language features for method interception (not sure any static language does)

I kinda like Nick's idea of using the debugger interface, that's just mean.

I think the short answer you need is: No there isn't a way of intercepting a method call in Java without actually replacing the class using a proxy or wrapper.

Note: The AOP libraries just make this happen automatically.

Gareth Davis
A: 

Some of the Java gurus might frown upon this but I've had some good success with avoiding primitive types and setters altogether. My class looks like this:

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

You'll notice two things: 1. everything is public. 2. No constructor.

The magic happens in SmartPojo which searches for any field which implements the "Smart" interface and initializes it. Since this is no primitive (and no final class), I can add set() and get() methods for all fields anywhere in my model in a single place. So no setter/getter wastes anymore, it's stunningly simple to add notification (also in a single place), etc.

True, this is no POJO anymore and it's not a Bean in most ways but I've found that these old ideas limit me more than they help. YMMV.

Aaron Digulla
this is basically what grails does with their domain objects. Though, because they can implement it with groovy, its much nicer, and they wont need to have every property implementing a "smart" interface.
Chii
I needed a Java only solution. I've tried this with Groovy as well and, while the code is much more compact, this isn't as nice. I'm a huge fan of Groovy but I feel that 1.x is still a little bit ... "incomplete".
Aaron Digulla
A: 
  1. Why cannot your class implement an interface? You could just extract some interface from it containing all the methods that you want to intercept and use the dynamic proxies mechanism easily. It's also a good programming practice to code with interfaces and not classes.

  2. You could use Spring framework with Spring AOP capabilities (which are using dynamic proxies inside) to do it. You will just have to define your class as a Spring bean in the configuration file and clients of your class will have to either get its instance from the Spring application context or as a dependency automatically (by defining the setMyClass(MyClass mc) method for instance). From there you can easily go to defining an aspect that intercepts all the method calls to this class.

Stas
Perhaps he is trying to debug a class which he did not write contained in some 3rd party library?
oxbow_lakes
+2  A: 

As you note, you cannot use JDK dynamic proxies (no interface), but using Spring and CGLIB (JAR included with Spring), you can do the following:

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"
eljenso
Thanks! Saved my day.
abhin4v