mscharhag, Programming and Stuff;

A blog about programming and software development topics, mostly focused on Java technologies including Java EE, Spring and Grails.

Tuesday, 15 September, 2020

Implementing the Proxy Pattern in Java

The Proxy Pattern

Proxy is a common software design pattern. Wikipedia does a good job describing it like this:

[..] In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. [..]

(Wikipedia)

UML class diagram:

proxy pattern

A client requires a Subject (typically an interface). This subject is implemented by a real implementation (here: RealSubject). A proxy implements the same interface and delegates operations to the real subject while adding its own functionality.

In the next sections we will see how this pattern can be implemented in Java.

Creating a simple proxy

We start with an interface UserProvider (the Subject in the above diagram):

public interface UserProvider {
    User getUser(int id);
}

This interface is implemented by UserProviderImpl (the real implementation):

public class UserProviderImpl implements UserProvider {
    @Override
    public User getUser(int id) {
        return ...
    }
}

UserProvider is used by UsefulService (the client):

public class UsefulService {
    private final UserProvider userProvider;

    public UsefulService(UserProvider userProvider) {
        this.userProvider = userProvider;
    }
    
    // useful methods
}

To initialize a UsefulService instance we just have to pass a UserProvider object to the constructor:

UserProvider userProvider = new DatabaseUserProvider();
UsefulService service = new UsefulService(userProvider);

// use service

Now let's add a Proxy object for UserProvider that does some simple logging:

public class LoggingUserProviderProxy implements UserProvider {
    private final UserProvider userProvider;

    public LoggingUserProviderProxy(UserProvider userProvider) {
        this.userProvider = userProvider;
    }

    @Override
    public User getUser(int id) {
        System.out.println("Retrieving user with id " + id);
        return userProvider.getUser(id);
    }
}

We want to create a proxy for UserProvider, so our proxy needs to implement UserProvider. Within the constructor we accept the real UserProvider implementation. In the getUser(..) method we first write a message to standard out before we delegate the method call to the real implementation.

To use our Proxy we have to update our initialization code:

UserProvider userProvider = new UserProviderImpl();
LoggingUserProviderProxy loggingProxy = new LoggingUserProviderProxy(userProvider);
UsefulService usefulService = new UsefulService(loggingProxy);

// use service

Now, whenever UsefulService uses the getUser() method we will see a console message before a User object is returned from UserProviderImpl. With the Proxy pattern we were able to add logging without modifying the client (UsefulService) and the real implementation (UserProviderImpl).

The problem with manual proxy creation

The previous solution has a major downside: Our Proxy implementation is bound to the UserProvider interfaces and therefore hard to reuse.

Proxy logic is often quite generic. Typical use-cases for proxies include caching, access to remote objects or lazy loading.

However, a proxy needs to implement a specific interface (and its methods). This contradicts with re-usability.

Solution: JDK Dynamic Proxies

The JDK provides a standard solution to this problem, called Dynamic Proxies. Dynamic Proxies let us create a implementation for a specific interface at runtime. Method calls on this generated proxy are delegated to an InvocationHandler.

With Dynamic Proxies the proxy creation looks like this:

UserProvider userProvider = new DatabaseUserProvider();
UserProvider proxy = (UserProvider) Proxy.newProxyInstance(
        UserProvider.class.getClassLoader(),
        new Class[]{ UserProvider.class },
        new LoggingInvocationHandler(userProvider)
);
UsefulService usefulService = new UsefulService(proxy);

With Proxy.newProxyInstance(..) we create a new proxy object. This method takes three arguments:

  • The classloader that should be used
  • A list of interfaces that the proxy should implement (here UserProvider)
  • A InvocationHandler implementation

InvocationHandler is an interface with a single method: invoke(..). This method is called whenever a method on the proxy object is called.

Our simple LoggingInvocationHandler looks like this:

public class LoggingInvocationHandler implements InvocationHandler {

    private final Object invocationTarget;

    public LoggingInvocationHandler(Object invocationTarget) {
        this.invocationTarget = invocationTarget;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(String.format("Calling method %s with args: %s",
                method.getName(), Arrays.toString(args)));
        return method.invoke(invocationTarget, args);
    }
}

The invoke(..) method has three parameters:

  • The proxy object on which a method has been called
  • The method that has been called
  • A list of arguments that has been passed to the called method

We first log the method and the arguments to stdout. Next we delegate the method call to the object that has been passed in the constructor (note we passed the real implementation in the previous snippet).

The separation of proxy creation (and interface implementation) and proxy logic (via InvocationHandler) supports re-usability. Note we do not have any dependency to the UserProvider interface in our InvocationHandler implementation. In the constructor we accept a generic Object. This gives us the option to reuse the InvocationHandler implementation for different interfaces.

Limitations of Dynamic Proxies

Dynamic Proxies always require an interface. We cannot create proxies based on (abstract) classes.

If this really a great issue for you can look into the byte code manipulation library cglib. cglib is able to create proxy via subclassing and therefore is able to create proxies for classes without requiring an interface.

Conclusion

The Proxy Pattern can be quite powerful. It allows us to add functionality without modifying the real implementation or the client.

Proxies are often used to add some generic functionality to existing classes. Examples include caching, access to remote objects, transaction management or lazy loading.

With Dynamic Proxies we can separate proxy creation from proxy implementation. Proxy method calls are delegated to an InvocationHandler which can be re-used.

Note that in some situations the Proxy Pattern can be quite similar to the Decorator pattern (see this Stackoverflow discussion).

 

Comments

  • AnLK19 - Sunday, 1 October, 2023

    Thanks, it's really helpful

Leave a reply