How do I intercept a method invocation with standard java features (no AspectJ etc)?
Solution 1:
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"
Solution 2:
If you are prepared to do something really ugly, have a look at:
http://docs.oracle.com/javase/7/docs/technotes/guides/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.
Solution 3:
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.
Solution 4:
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.