Java proxy objects for Interceptor implementation.
23.02.2016

Maybe you heard something about design patterns before. Design patterns could be useful for your developer life as they shows you some good practices for various situations, which can occur during software development. In our post we will implement Interceptor pattern.

In Java there are some interesting language / API constructs since version 1.5. In our current article, we will be interested especially in annotations and proxy objects. If you work with CDI, you probably know the concept of interceptors there. We will implement it similar way, but without need of CDI. It can be useful for some older JEE projects or other Java projects, where CDI can't be used.

Ok, let's start with some motivation why we should thing about it. Imagine you have a service class, which contains the following method:

            public void output(String msg) {
            	System.out.println(msg);			// Actual action code.
            }
            

This method takes one String argument and print it out. Everything should work ok. But now what if there is a need to perform this action transactionally? So basically we need to perform some action at the beginning and at the end of the method. We have something like this:

            public void output(String msg) {
            	System.out.println("--Start Transaction");	// Code for transaction start.
            	System.out.println(msg);			// Actual action code.
            	System.out.println("--End Transaction");	// Code for transaction end.
            }
            

It is working well now, but it is not ideal. As you can see these actions for transaction are unrelated to the business logic of the method (business logic is to print out String). But we need them. Even worse - if we have let's say hundreds of methods in our project containing transactional behavior done this way and want to change for example "--Start Transaction" to "--Begin Transaction", you are in trouble. Of course, you can create methods like beginTransaction(), endTransaction() and write something like this:

            public void output(String msg) {
            	beginTransaction();				// Code for transaction start.
            	System.out.println(msg);			// Actual action code.
            	endTransaction();				// Code for transaction end.
            }
            

But still you have code unrelated to the business logic mixed in. There must be something better...

Proxy objects to the rescue.

Our goal is to have transactional behavior defined somehow declarative. For this declarative approach, annotations help us a lot. The result will look like:

            @Transaction
            public void output(String msg) {
            	System.out.println(msg);			// Actual action code.
            }
            

Maybe it is good idea to explain, what the proxy object is. Basically it is an object, which mimics class and properties of given regular object. Additionally proxy object allow us to control method execution. We are then able to intercept actual execution and we can perform own actions during execution.

At first, we define the annotation @Transaction. It will help us to recognize, which method should be transactional-aware. Methods, which are not annotated will be processed in normal way.

            @Target(ElementType.METHOD)
            @Retention(RetentionPolicy.RUNTIME)
            public @interface Transaction {
            
            }
            

Now we must implement some code, which will do an actual method calling interception. In terms of Java proxy object, it is called invocation handler and this code is called for every single method invocation of defined proxy object.

            public class MyInvocationHandler<T> implements InvocationHandler {
            
                private final T proxied;
            
                public MyInvocationHandler(T proxied) {
                    this.proxied = proxied;
                }
            
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Object result;
                    Method m = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
                    if (m.isAnnotationPresent(Transaction.class)) {
                        System.out.println("--Start Transaction");
                        result = method.invoke(proxied, args);
                        System.out.println("--End Transaction");
                    } else {
                        result = method.invoke(proxied, args);
                    }
                    return result;
                }
            }
            

Let me explain the code a little. As you can see our class is generic, which helps us to implement it universally without dealing with potentially dangerous type casting. Class must implement InvocationHandler interface. Method Object invoke(Object proxy, Method method, Object[] args) must be overridden and it contains all necessary code for interceptor. On if (m.isAnnotationPresent(Transaction.class)) we are testing, if our annotation is present and if so we perform start transaction, method invocation and end transaction. Code result = method.invoke(proxied, args); is actual method invocation, result of it is stored in the result variable. If there is no annotation, we only perform method invocation itself. That's it. Now we only need the way, how the proxy object with our invocation handler could be instantiated.

Instead of standard instantiation like Object o = new Object();, we must create proxy object based on regular object's instance.

            MyInvocationHandler invocationHandler = new MyInvocationHandler(new ServiceImpl());
            Service service = (Service) Proxy.newProxyInstance(ServiceImpl.class.getClassLoader(),
                      new Class[]{Service.class}, invocationHandler);
            

And now we have in variable service proxy object, which behaves the same way as Service object instance itself. Only difference is, that we can control method invocations thru the invocation handler.

You can download all source code for this article on Github. It can help you understand the whole concept. It is fully working example available as Maven project. Feel free to modify and reuse the code as you want.