Transaction Proxies
Transactions are another crosscutting concern you could consider. In J2EE, EJB Session beans manage your transactions. You make a method transactional by marking it as TX_NEW or TX_REQUIRED in the EJB descriptor. However, not all applications run inside an EJB container or have access to Session beans. In order to execute updates to a DBMS in a transaction manner, you must code the low-level connection management and transaction demarcation logic yourself.
Rather than explicitly creating transactions within your code, you can delegate this responsibility to a transaction proxy. The proxy will ensure that your methods are executed within a transactional context. If your real method completes, then any updates performed inside your method will be committed. Conversely if your method fails, all updates will be rolled back:
public class TransactionProxyHandler implements InvocationHandler {
protected Object delegate;
public TransactionProxyHandler(Object delegate) {
this.delegate = delegate;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// Get connection
Connection conn = null;
try {
// Get a connection
// subsequent getConnection() calls will return the same connection
// from the DAOUtil (backed by threadlocal)
conn = DAOUtil.getConnection();
// start a transaction
conn.setAutoCommit(false);
// execute the method
Object result = method.invoke(delegate, args);
// commit any statements on the current connection
conn.commit();
return result;
} catch (InvocationTargetException e) {
// rollback transaction
conn.rollback();
throw e.getTargetException();
} finally {
// Close the connection for this call in this thread
DAOUtil.closeConnection(conn);
}
}
}
You can also aggregate or chain your proxies via your Invocation Handlers. This way, you can combine Logging with Transactions or Logging with Remote handling, etc. Instead of passing the real class as the delegate to the Invocation Handler constructor, pass another proxy instead. The order in which you construct your proxies will determine the order in which your behavior gets invoked:
// Create an instance of TestImpl
Test t = new TestImpl();
// Create Transaction Handler
InvocationHandler txnHandler = new TransactionProxyHandler(t);
// Create Txn Proxy
Test txnProxy =
(Test) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
txnHandler);
// Create Log Handler and wrap around Transaction proxy
InvocationHandler loggingHandler = new LoggingProxyHandler(txnProxy);
// Create Proxy with 2 levels of indirection
Test proxy =
(Test) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
loggingHandler);
// Test Proxy class
proxy.ping();
Although useful, dynamic proxies are not always appropriate. Code simplification comes at the expense of performance. Each level of indirection adds overhead—this is especially true of reflection-based mechanisms. Take care to keep your proxies lightweight. Ensure that your handler code is simple, and be selective in which classes you target.
The Next Step: Aspect-oriented Programming
Now that you understand dynamic proxies, you may wonder whether you can implement more of your application logic in this way. You can—it's called aspect-oriented programming (AOP). Dynamic proxies illustrate the basics of AOP. Proxies are just one way that aspect-oriented behavior or crosscutting concerns can be added dynamically to your code. AOP complements existing object-oriented design by facilitating the consolidation and modularization of common functions that would normally be interleaved throughout your code. The following are just some examples of AOP frameworks that enable aspects to be woven transparently into your code: