Learn how to implement application concerns such as logging and remote error handling across classes using dynamic proxies. Along the way, you'll find out what dynamic proxies and aspect-oriented programming (AOP) have in common.

by Lara D'Abreo

Jul 9, 2004

Page 2 of 4

Proxy by Example
Take a common crosscutting concern, logging, as an example. Create a simple InvocationHandler class that logs method entries and exits using System.out.println (download the sample source code). Your class must implement the InvocationHandler interface and provide an implementation of the invoke() method. Pass the real class in as a parameter on the constructor, as follows:

Exception Handling
Your InvocationHandler should aim to mimic the real interfaces, even conforming to the interface exception strategy. If your handler adopts a different exception handling strategy from what is declared on the real interfaces, then your proxies will behave differently from your real classes. Your proxies will no longer be transparent and you may have to add special-purpose code to handle the differences.

In the LoggingHandler, if the underlying delegate method throws an exception, then the call to method.invoke() throws an InvocationTargetException exception. If the handler method throws a checked exception, then it must be assignable to one of the declared exceptions in the throws clause of the interface method being called. If not, the proxy will propagate a runtime exception—UndeclaredThrowableException—to the caller. Avoid this by adopting one or more of the following approaches:

Never throw checked exceptions that are not declared on the interface (although this can be difficult for general-purpose handlers).

Adopt a common application-level exception to be thrown by all interface methods. That way, you can assume that the exception is always thrown by your interface. Wrap any checked exceptions inside the common exception.

For reflection calls, ensure that the real exception is communicated to the caller by extracting the underlying exception using InvocationTargetException::getTargetException() and re-throwing. This way, all exceptions that are declared on the interface and thrown inside the real class will be propagated back to the caller.

To test the LoggingHandler, define an interface (Test) that contains one method [ping()] and a class (TestImpl) that implements Test:

The LoggingHandler is flexible in that it can be added to any arbitrary class (via a proxy) without changing your class logic. The handler centralizes your logging code and, by keeping your logging semantics distinct from your business functions, reduces code clutter.