Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the actual purpose of designing a proxy class?

Tags:

java

proxy

I've been studying the proxy classes and I don't get the whole idea of designing it.

From what I learned so far it is a wrapper object that can control access to the original object. But if we want to control it why can't we design the original class with those access mechanisms.

I read that these proxy objects are useful for tracing method calls, routing method calls to remote servers.

But I searched for a question that would explain this to me in java but I didn't find any.

I'll illustrate the code for a method tracing program which was in the book that I was referring.

public class ProxyTest {

  public static void main(String[] args) throws ClassNotFoundException {

     var elements = new Object[1000];

     // fill elements with proxies for the integers 1 . . . 1000
     for (int i = 0; i < elements.length; i++) {
       Integer value = i + 1;
       var handler = new TraceHandler(value);
       Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparable.class}, handler);
       elements[i] = proxy;
     }

     // construct a random integer
     Integer key = new Random().nextInt(elements.length) + 1;

     // search for the key
     int result = Arrays.binarySearch(elements, key);

     // print match if found
     if (result >= 0)
        System.out.println(elements[result]);

  }

}

/**
 * An invocation handler that prints out the method name and parameters, then
 * invokes the original method
 **/

class TraceHandler implements InvocationHandler{

  private Object target;

  /**
   * Constructs a TraceHandler
   * @param t the implicit parameter of the method call
   **/

  public TraceHandler(Object t){
    target = t;
  }

  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {

     // print implicit argument
     System.out.print(target);

     // print method name
     System.out.print("." + m.getName() + "(");

     // print explicit arguments
     if (args != null){
       for (int i = 0; i < args.length; i++){
         System.out.print(args[i]);
         if (i < args.length - 1)
           System.out.print(", ");
       }
     }

     System.out.println(")");

     // invoke actual method
     return m.invoke(target, args);

  }

}

Can someone please point out to me what is going on with this proxy design pattern, what does it do in this particular program and its advantages?

like image 499
Clarke Avatar asked Dec 31 '22 02:12

Clarke


1 Answers

Proxy classes are extremely useful when you are dealing with code from other teams or third parties and can be used for all sorts of diagnostics or enhanced operations.

I've used them with database vendor JDBC Connection jars and fault tolerant Remote server calls where the proxy could deal with failover for errors or re-connections such that the application logic is cleaner in all client calling code.

Your TraceHandler could be a self contained class by adding the method:

@SuppressWarnings("unchecked")
public static <T> T create(final T impl, final Class<?>... interfaces)
{
    final Class<?> cls = impl.getClass();

    return (T)Proxy.newProxyInstance(cls.getClassLoader(), interfaces, new TraceHandler(impl));
}

Then you could use to TraceHandler to monitor / log ANY interface used by your application:

SomeObject x = TraceHandler.create(x, SomeObject.class);
FileVisitor myvisitor = TraceHandler.create(visitor, FileVisitor.class)

Hopefully it will then be clearer how proxy classes can be helpful, and an example as follows:

public class ProxyTest
{
    public static void main(String[] args) {

        var elements = new Integer[1000];

        for (int i = 0; i < elements.length; i++) {
            elements[i] = Integer.valueOf(i);
        }

        // construct a random integer
        Integer key = new Random().nextInt(elements.length) + 1;

        Comparator<Integer> comparator = Integer::compare;
        Comparator<Integer> comparator2 = TraceHandler.create(comparator, Comparator.class);

        // search for the key without proxy
        System.out.println("Search for "+key+" without proxy:");
        int result = Arrays.binarySearch(elements, key, comparator);

        // print match
        System.out.println("Found result="+result);

        // search for the key with proxy prints debug info per call
        System.out.println("Search "+key+" with proxy:");
        int result2 = Arrays.binarySearch(elements, key, comparator2);
        System.out.println("Found result2="+result2);
     }
}

Example output from the above run shows that the proxy class can print details of each compare call.

Search for 486 without proxy:
Found result at 486
Search 486 with proxy:
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](499, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](249, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](374, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](436, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](467, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](483, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](491, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](487, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](485, 486)
stackoverflow.proxy.ProxyTest$$Lambda$1/[email protected](486, 486)
Found result at 486
like image 78
DuncG Avatar answered Feb 06 '23 22:02

DuncG