Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Java Dynamic Proxies: One Step from Aspect-oriented Programming : Page 2

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.


advertisement
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:

public class LoggingHandler implements InvocationHandler { protected Object delegate; public LoggingHandler(Object delegate) { this.delegate = delegate; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { System.out.println( "Calling method " + method + " at " + System.currentTimeMillis()); Object result = method.invoke(delegate, args); return result; } catch (InvocationTargetException e) { throw e.getTargetException(); } finally { System.out.println( "Called method(" + method + " at " + System.currentTimeMillis()); } } }

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:

public interface Test { public void ping()throws Exception; } public class TestImpl implements Test { public void ping()throws Exception { System.out.println("ping()"); } }

Create an instance of TestImpl and LoggingHandler and pass TestImpl into the LoggingHandler constructor. Create a dynamic proxy and pass in the LoggingHandler:

// Create an instance of TestImpl Test t = new TestImpl(); // Create InvokeHandler InvocationHandler handler = new LoggingHandler(t); Create a dynamic proxy and pass in the LoggingHandler. // Create Proxy Test proxy = (Test) Proxy.newProxyInstance( t.getClass().getClassLoader(), t.getClass().getInterfaces(), handler);

Execute the real class TestImpl and then execute the proxy. The output from the proxy should be identical to the output of TestImpl except for additional method diagnostics:

// Test real class t.ping(); ping() // Test Proxy class proxy.ping(); Calling method public abstract void au.com.proxy.Test.ping() throws java.lang.Exception at 1088912557093 ping() Called method(public abstract void au.com.proxy.Test.ping() throws java.lang.Exception at 1088912557093

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.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap