Java 7 - custom method decoration without default delegating all, other, many methods

633 Views Asked by At

I'd like to decorate the interface PreparedStatement, in order to custom close it (just an example).

This means that I want to decorate an existing instance of PreparedStatement, thus, invoking other code, when close() is being invoked.

For that, I need to default implement all tens of methods of PreparedStatement decorator just to delegate the calls to the inner object, like done here. The downfall is that it's just a lot of work and code with little added value.

Another option is to try and use Java's Proxy and InvocationHandler in order to provide a default implementation that does the delegate for all the methods in a single method. If a custom method exists, the InvocationHandler, directs the call to it. See example here. The problem with this solution is that the custom method cannot be marked as @Override and its signature cannot be checked for correctness, as it will require an abstract PreparedStatement, which the Proxy will not be able to instantiate.

So, can this be done? How?

* Must be able to implement using Java 7 max, but feel free to provide Java 8 answers.

3

There are 3 best solutions below

2
On

As far as I understood you want to provide to the interface PreparedStatement concrete implementation. The only way I can think of is by creating abstract class that implements the interface. By doing so you don't need to implement all the methods from the interface and you'll have your desired implementation.

I'd try something like this:

public abstract class MyPreparedStatement implements PreparedStatement {

@Override
public void close() throws SQLException {
    System.out.println("Closing");
}

public static void main(String[] args) throws SQLException {
    Connection con = null;
    MyPreparedStatement statement = (MyPreparedStatement) con.prepareStatement("sql");
}
}
2
On

Can you explain in clearer terms what the Proxy solution is lacking? Consider something like this, which relies on a AOP-esque 'hook':

final PreparedStatement original = ...;
final InvocationHandler delegator = new InvocationHandler() {

  void onClose() {
    /* do stuff */
  }

  Object invoke(final Object proxy, final Method method, final Object[] args) {
    if (method.getName().equals("close")) {
      onClose();
    }

    return method.invoke(original, args);
  }
};
final PreparedStatement wrapped = (PreparedStatement) Proxy.newProxyInstance(this.getClass().getClassLoader(),
    new Class<?>[] { PreparedStatement.class }, delegator);
2
On

If you don't have access to the methods in order to do the usual inheritance thing with them, you can accomplish what you are attempting to do with Aspect Oriented Programming, leveraging AspectJ or the Spring Framework aspect functionality to provide advice on your desired methods.

A simple aspect basically comes down to:

@Aspect
public class MyAspect {

    @Pointcut("execution(* *(..))") //Replace expression with target method; this example 
    //will hit literally every method ever.
    public void targetmethod() {}; //Intentionally blank.
    //AspectJ uses byte code manipulation (or "black magic voodoo", if you 
    // will) to make this method a surrogate for any real one that matches the pointcut

    @Before("targetmethod()") //Or @After, or @Around, etc...
    public void doStuff() throws Throwable {
        //Put your code here
    }
}

Once you have your aspects together, add them to your aop.xml and weave your aspects (you can do this at compile time with appropriate build manager configuration, or at run time by running aspectjweaver with java -javaagent:/path/to/aspectjweaver.jar).

This does come with a disclaimer however: doing things like this to java.* classes allows you break things in new and interesting ways with all the side-effects you're introducing (in fact, AspectJWeaver refuses to weave into java.* classes by default, though you can override that setting). Be very aware of what you are doing, and use your aspects and aspected methods wisely.