Search This Blog

Monday, December 31, 2012

AOP with dynamic proxy API

Java supports dynamic proxy classes which are cool way of implementing your own AOP (aspect-oriented programming). That means you can increase modularity of your code by allowing separation of cross-cutting concerns or in simple words you have cleaner code by removing code parts form your methods that don't relate directly to your bussines logic - for example security concerns, validation, logging etc.

How does dynamic proxying work? You have some interface and class that implements that interface. You than create proxy instance for that interface where all client calls to interface methods can be intercepted and where you can do some action befeore the call is delegated to implementation class.

Suppose you have some simple interface class called UserApi  with method authUser. Suppose you want to validate method parameters for not null condition - you can do it directly in implementing class in authUser metod but better way would be to remove this validation logic to separate class. This way you have nicer and cleaner code.

So I have UserApi interface and  @NotNull annotation to mark parameters that will be validated for not null condition:

public interface UserApi {
    
    public UserData authUser(@NotNull String username, @NotNull String password);
}

Simple implemenation class for showcase - username and password must not be null in our case:

public class UserApiImpl implements UserApi {
    
    @Override
    public UserData authUser(String username, String password) {
        //find and auth user from somewhere and return UserData
        UserData data  = fetchData(username, password);
        return data;
    }
    
}

And this is my simple annotation:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
    
} 

Main part is instance of InvocationHandler class that serves as proxy to all method calls on our UserApi interface. InvocationHandlerImp can be generally used on any interface. Notice that InvocationHandlerImp implements special java.lang.reflect.InvocationHandler interface. Constructor parameter is real interface implementation that we are proxying.  So when client calls authUser method on UserApi interface we intercept this call in invoke  method and check parameters for not null validation.

class InvocationHandlerImp implements InvocationHandler {
    
    private final Object implementation;
    
    InvocationHandlerImp(Object realObject) {
        this.implementation = realObject;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        //check if parameters marked with NotNull are valid
        Annotation[][] parameterAnnotations =  method.getParameterAnnotations();
        for(int i = 0; i < parameterAnnotations.length; i++) {
            for(Annotation annotation : parameterAnnotations[i]) {
                if(annotation instanceof NotNull) {
                    if(args[i] == null) {
                        throw new NullPointerException("@NotNull parameters must be provided");
                    }
                }
            }
        }
        try{
            //call real method
            return method.invoke(implementation, args);
            } catch(InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

Finally, you setup proxy instance like this:

UserApi api = (UserApi)Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[] { UserApi.class },
new InvocationHandlerImp(new UserApiImpl()));

UserData user = api.authUser("someuser", "somepassword");

If you call authUser method with null parameters, NullPointerException  will be thrown.

5 comments: