[JAVA] Let's explain Dynamic Proxy as easily as possible

1.First of all

The Java standard library has a java.lang.reflect.Proxy class. .. This class was added from J2SE 1.3, and the provided function is generally called Dynamic Proxy (hereinafter referred to as "** Dynamic Proxy **" in Japanese). Since J2SE 1.3 was released in May 2000, dynamic proxies have been around for a long time and are already dead technology, but they are still used by many Java frameworks and libraries. On the other hand, it seems that the general Java programmers are not very well known.

In this article, for those who understand the basic Java grammar, we will introduce common proxy patterns to understand dynamic proxies, explain the need for dynamic proxies, and then explain the need for dynamic proxies. Explains how to use Java's dynamic proxy. It also shows how to implement dynamic proxies for languages other than Java (C #, Python, EcmaScript).

The Java code posted in this article has been confirmed to work in the following environment. (The operating environment of languages other than Java will be described at any time.) Also, the Java code posted omits some import clauses required for compilation.

name version
OpenJDK 11.0.1

2. "Static" proxy (continued)

Understanding how to use the dynamic proxy java.lang.reflect.Proxy shouldn't be difficult for programmers who are familiar with Java. However, in order to understand the need for dynamic proxies and use them effectively, understand the " static </ b>" proxy, which is the opposite of " dynamic </ b>". need to do it. This was explained in "I will explain the Proxy Pattern as easily as possible".

proxy_image_small.png

2-1. Review of static proxies

In "I will explain the Proxy Pattern as easily as possible", ** One about multiple classes that represent a common interface. Introduced up to ** points where common processing can be defined with the proxy of. In this article, we'll start with that anti-pattern.

staticproxy_manyclass_small.png

2-2. Limitations of static proxies

There are anti-patterns in applying static proxies.

Here is a more specific example of the "proxy that adds common processing for multiple concrete classes to an interface" shown above.

An implementation of the model layer of a sales site defines three classes: "** customer ", " order ", and " product **". In order for this class to access the RDBMS in the processing of all methods, it is necessary to get an RDBMS connection and start a transaction before executing the method. Then, after execution, it is necessary to commit or roll back the transaction and close the connection.

All three classes are "** add " to create data, " update " to update data, and " search" to search data. It seemed that it would be enough if there was a method of "" and " get details **" to get detailed data. Therefore, these three classes are concrete classes of the interface called Model. Then, define a class called TransactionBarrier, which is a proxy for RDBMS processing, with a concrete class of Model.

It is a class diagram showing the relationship between these classes.

ecmodel_proxy_class_small.png

To some extent, the class design above worked well, but one day I run into problems.

"Customer" information needs to be deleted in bulk to protect customer information.

Therefore, a method called "** Bulk Delete *" is added to "Customers", but this "Batch Delete" method also requires pre-post processing of RDBMS by TransactionBarrier. Therefore, add a method called " Delete all *" to the Model interface as well.

Then, you will have to add the "* Bulk Delete *" added to the Model interface to the" Orders "and" Products "that do not require the" * Bulk Delete * "method. However, in reality, "Order" and "Product" do not want to call "Delete all", so add the @deprecated tag to the" Delete all "method for" Order "and" Product ". I had to throw ʻUnsupportedOperationException` unconditionally when this method was called in.

ecmodel_proxy_class_unsupport_small.png

In the above example, it is difficult to convey the seriousness of the situation because only one special method addition occurred in three classes, but in actual system development, dozens to hundreds of models are defined depending on the project. .. Then, every time you define your own method in each model, the above cases occur frequently, and a lot of methods that throw ʻUnsuppportedOperationException` will be added to each class.

The reason for falling into this situation is that * a group of classes that should not be implemented as one interface is forcibly derived from one interface because we want to implement common processing with a proxy *.

Originally, "** Customer ", " Order ", and " Product **" should have defined separate interfaces and proxies, as shown below.

ideal_ecmode_proxy_small.png

However, in the above class design, the proxy class must be implemented for each interface, which is very troublesome. In the first place, even though there is a one-to-one relationship between the interface and the class as described above, if you define a proxy that implements the same processing for each, even if you implement the common processing directly in the implementation class, it will take time to implement. Even the benefits of using a proxy are lost in that it doesn't change.

It would be nice if there was a proxy that could be defined by one implementation even for multiple different interfaces, but such an implementation is not possible with the normal Java syntax. However, dynamic proxies allow you to ** implement a common proxy across multiple interfaces **.

3. "Dynamic" proxy

Now, let's introduce an implementation example using the dynamic proxy java.lang.reflect.Proxy (hereinafter referred to as" ** Proxy **"). </ p>

3-1. Implementation example of java.lang.reflect.Proxy class

First, define the class mediated by Proxy and its interface. This code is for MyInterface and MyClass posted in "I will explain the Proxy Pattern as easily as possible". Same as implementation.

MyInterface.java


public interface MyInterface {
    public String myMethod(String value);
}

MyClass.java


public class MyClass implements MyInterface {
    @Override
    public String myMethod(String value) {
        System.out.println("Method:" + value);
        return "Result";
    }
}

To use Proxy, you must define a concrete class for the java.lang.reflect.InvocationHandler interface. The concrete class of ʻInvocationHandler implements the ʻinvoke (Object, Method, Object []) method. This ʻinvoke` method is a method that implements specific proxy processing.

ʻThe code of MyInvocationHandler in the implementation example of the concrete class of InvocationHandler. In the proxy processing of MyInvocationHandler, MyInvocationHandler: startis output as pre-processing andMyInvocationHandler: exit` is output as standard output in post-processing.

MyInvocationHandler.java


import java.lang.reflect.*;

/** java.lang.reflect.InvocationHandler implementation class that implements proxy processing specified in Proxy*/
public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target)  {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy:before");
        Object result = method.invoke(target, args);
        System.out.println("DynamicProxy:before");

        return result;
    }
}

To use the MyInvocationHandler defined above, use the newProxyInstance method of the Proxy to get the object of the Proxy. Specify the object of the MyInvocationHandler class defined above in the third argument of the newProxyInstance method.

    MyClass myClassObj  = new MyClass();
    MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myClassObj);
    MyInterface proxiedObj = (MyInterface)Proxy.newProxyInstance(MyClass.class.getClassLoader(),
        new Class[] { MyInterface.class },
        myInvocationHandler);
    System.out.println(proxiedObj.myMethod("Argument"));

This is the result of executing the above code.

Execution result


DynamicProxy:before
Method:Argument
DynamicProxy:after
Result

3-2. NewProxyInstance method of Proxy class

First, I will explain the newProxyInstance method of Proxy.

You can get a Proxy object with newProxyInstance. The user casts the acquired ʻObject type object to one of the types in the class array of the interface specified by the second argument and uses it. The return value of this method is of type ʻObject, but it is a derived class of the Proxy class and is a concrete class of all interfaces of the second argument.

    MyInterface proxiedObj = (MyInterface)Proxy.newProxyInstance(MyClass.class.getClassLoader(),
        new Class[] { MyInterface.class },
        myInvocationHandler);
  • Specify the class loader in the ** first argument ** of newProxyInstance. Since the class loader can be obtained from all classes with the getClassLoader () method, it can be obtained with the getClassLoader () of any class, but an environment using many class loader chains such as Web applications. It is better to specify the class that is implemented independently in the application as much as possible, assuming that it will be used in. Here, the class loader of MyClass, which is a class mediated by a dynamic proxy, is specified.
  • Specify an array of interface classes in the ** second argument ** of newProxyInstance. In Java, one class can implement multiple interfaces, so specify it as an array so that you can generate a dynamic proxy that implements multiple interfaces.
  • In the ** third argument ** of newProxyInstance, specify an object of the concrete class of ʻInvocationHandler. The object specified by this argument is the process that is actually executed inside the dynamic proxy. Here, we actually implemented MyInvocationHandler, which is a concrete class of ʻInvocationHandler, so that object is specified.

3-3. InvocationHandler invoke method

Next, I will explain the ʻinvoke method of the ʻInvocationHandler interface implemented in MyInvocationHandler.

ʻImplement proxy processing with the invokemethod. Whenever a method that embodies the interface of aProxy object is called, it calls this ʻinvoke method. ʻInvoke method is passed as an argument which method of the Proxy` object was called with what argument.

** If you want to implement a general proxy **, call the method of the target object in the ʻinvoke method. The method to call is passed as an argument to the ʻinvoke method, but the object to call is held by the concrete class of ʻInvocationHandler` itself.

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
  • The Proxy class object that called this ʻinvoke is passed to the ** first argument ** of ʻinvoke.
  • The ** second argument ** of ʻinvoke is passed an object of the Methodclass that represents the method called by theProxy object. In the process of ʻinvoke, the method of the actual object is called using the value of this argument.
  • In the ** third argument ** of ʻinvoke, the argument specified in the method in which the Proxy` object is called is passed as an Object array. This also uses the value of this argument to call the method of the actual object.

3-4. What is a dynamic proxy and what is "dynamic"?

If you look at the result of executing the implementation of the dynamic proxy, you can get the same execution result as "2-2. Defining the proxy class", so it is easy to understand that the dynamic proxy is working as a proxy. I will. But why call this "** dynamic **"?

To understand the "dynamic" nature of a dynamic proxy, run the code that calls the newPorxyInstance of the Proxy as follows: You can leave the MyInvocationHandler in the implementation shown above.

    ArrayList<String> list = new ArrayList<String>();
    MyInvocationHandler myInvocationHandler = new MyInvocationHandler(list);
    @SuppressWarnings("unchecked")
    List<String> proxiedObj = (List<String>)Proxy.newProxyInstance(MyInterface.class.getClassLoader(),
        new Class[] { List.class },
        myInvocationHandler);
    proxiedObj.add("hoge");

This is the result of executing the above code.

Execution result


DynamicProxy:before
DynamicProxy:after

The output of the above execution result shows that the call to proxiedObj.add ("hoge"); called ʻinvoke of MyInvocationHandler`.

It should be noted here that what used to be able to get the Proxy object of MyInterface can also get the proxy of the List interface without changing the MyInvocationHandler at all. The code for MyInvocationHandler has no MyInterface or List characters, and MyInvocationHandler does not actually depend on these interfaces at all. By specifying the interface class of the second argument of newProxyInstance of Proxy, a new class is generated ** during execution of newProxyInstance ** inside Proxy, and the Proxy object is acquired. I will.

The relationship between the dynamic proxy and the implemented class is shown in the class diagram.

dynamicproxy_class_small.png

The green class is a class prepared before the Java process is started, while the red class is created after the Java process is started and the newProxyInstance of Proxy is called ** (instance). Not) class **. newProxyInstance returns an instance of the newly created class (although it will be retrieved from the map if it has already created the same class).

** How, the process in the newProxyInstance method creates a class that doesn't exist in the source code! ** [^ 1]

Proxy "** static " and " dynamic " are pre-defined and invariant proxy classes that are " static" during the period in which the Java process is running. "", the proxy class generated from the middle of the process being executed is called " dynamic **".

4. Implementation of a more practical dynamic proxy

This is the end of how to use Proxy.

However, in the dynamic proxy implementation examples introduced so far, the user of the object calls newProxyInstance of Proxy to get the Proxy object including the target object. However, this implementation is not very convenient.

Implementation with factory pattern applied

Therefore, there is a GoF design pattern called ** Factory Pattern **, so we will use this pattern to simplify the code to get the Proxy object. The factory pattern is a method to obtain from the method of the dedicated class that creates the object without calling the constructor directly to the user when creating the object.

For example

Factory pattern example ①


    MyInterface myObj = MyFactory.getInstance();

If the Proxy object can be obtained by calling thegetInstance ()method of MyFactory, the implementation on the Proxy object user side will be very simple.

However, since the getInstance () method of MyFactory does not pass any information from the caller, as it is, it always goes through the same ʻInvocationHandler and the same class (here, Proxy that calls the object of MyClass". You can only get objects. If you pass the MyInterfaceclass, theMyClass object and the ʻInvocationHandler object as arguments, it will be a versatile factory. But if you think about it, the ʻInvocationHandler is a proxy. Since it is the definition of the processing to be executed, the factory may use a fixed concrete class of ʻInvocationHandler internally (switching the concrete class of ʻInvocationHandleris possible by separating the factory class and the method). Also, if the derived class for theMyClass object is fixed at MyClass`, it is not necessary to specify it as an argument.

Therefore, here, we will implement a factory that acquires the Proxy object by specifying only the interface in the argument of the factory as shown below.

Factory pattern example ②


    MyInterface myObj = MyFactory.getInstance(MyInterface.class);

Assuming you implement a factory like the one above, there is one problem.

Since only the interface class is specified in this argument, it is necessary to get the derived class for the interface inside the factory and create an object of that derived class. The easiest way to do this is to have a map of the classes for the interface in the factory. This method is fine and practical enough, but in a system where there are dozens or hundreds of interface and class definitions, each time a new interface and class is defined, it is in the factory. You need to add a map, which is a bit annoying and can cause source conflicts in group development.

Therefore, here we will introduce how to specify the derived class in the additional information of the interface. First, define the annotation as follows.

MyAnnotation.java


/**Annotation that specifies the implementation class for the interface*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    Class<?> concretizedClass();
}

Add this annotation to the MyInterface you defined earlier. In the annotation argument, specify the derived class to select in the factory.

MyInterface.java


/**Added implementation class definition to instantiate with annotation*/
@MyAnnotation(concretizedClass = MyClass.class)
public interface MyInterface {
    public String myMethod(String value);
}

Finally, it is an implementation of MyFactory which is a factory class. The concrete class of ʻInvocationHandler is defined by the anonymous class in the getInstancemethod ofMyFactory`.

MyFactory.java


/**Class that creates an object of the interface specified by the argument*/
public class MyFactory {
    /**Method to get the object of the implementation class of the interface specified by the argument interfaceClass*/
    public static <I> I getInstance(Class<I> interfaceClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException  {
        //Get the implementation class for MyInterface from the annotation of MyInterface
        MyAnnotation myAnnotation = interfaceClass.getAnnotation(MyAnnotation.class);
        Class<?> objClass = myAnnotation.concretizedClass();

        //Create an implementation class object
        @SuppressWarnings("unchecked")
        I obj = (I)objClass.getDeclaredConstructor().newInstance();

        //Create an object by defining InvocationHandler with an anonymous class
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("DynamicProxy:before");
                Object result = method.invoke(obj, args);
                System.out.println("DynamicProxy:after");
                return result;
            }
        };

        //Create a proxy object
        @SuppressWarnings("unchecked")
        I proxiedObj = (I)Proxy.newProxyInstance(interfaceClass.getClassLoader(),
            new Class[] { interfaceClass },
            invocationHandler);
        return proxiedObj;
    }
}

It is the implementation and execution result of the above MyFactory user side.

    MyInterface myObj = MyFactory.getInstance(MyInterface.class);
    System.out.println(myObj.myMethod("Argument"));

Execution result


DynamicProxy:before
Method:Argument
DynamicProxy:after
Result

To make the code more practical

With the above improvements, we're getting closer to the actual "usable" code, but there's still room for improvement and issues that need to be fixed.

They are listed below for your reference when actually applying a dynamic proxy.

  • The getInstance method of MyFactory is throwing the NoSuchMethodException that can occur internally. Exceptions that cannot actually occur should not be included in the throws clause
  • For ʻinterface Class specified by the argument, it should be checked that ʻobjClass specified by the annotation is a concrete class of ʻinterface Class`.
  • It is convenient to provide an option that allows you to specify ʻobjClass for ʻinterface Class directly to MyFactory, or to set it from the configuration file, in addition to the interface annotation.
  • ʻInterface Class` is cooler to specify generically than to specify it as a method argument
  • ʻObject result = method.invoke (obj, args); You should decide whether to throw the exception that occurs inas it is, or to throw theThrowable object that caused it in getCause () `.
  • getInstance () creates a new Proxy object every time, but if it is a good object with Sigletone, do not create it every time
  • Allows users of Proxy to get objects by DI (Devedency Injection) instead of using MyFactory directly

In addition to the factory pattern, the pattern that determines the derived class for such an interface and returns an appropriate object is called the ** ServiceLocator pattern **.

5. Dynamic proxies in other languages

So far, we have introduced dynamic proxies using Java. This chapter introduces how dynamic proxies are provided in other programming languages.

5-1.C#

(According to cfm-art, the standard RealProxy provides the dynamic proxy function. This chapter will be added again.)

The .NET Framework, the standard library for ~~ C #, does not have a class equivalent to a dynamic proxy. ~~ ~~ However, ~~ The Castle of the external project provides the dynamic proxy function in the external library.

name version
.NET Framework 4.6.1
Castle.DynamicProxy2 2.2.0

DynamicProxyTest.cs


using Castle.Core.Interceptor;
using Castle.DynamicProxy;

namespace MyNamespace
{
    public interface IMyInterface
    {
        string MyMethod(int value);
    }

    public class MyClass : IMyInterface
    {
        public string MyMethod(int value)
        {
            Console.WriteLine("MyMethod:" + value);
            return "Result";
        }
    }

    public class MyInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine("MyInterceptor:before");
            object ret = invocation.Method.Invoke(invocation.InvocationTarget, invocation.Arguments);
            Console.WriteLine("MyInterceptor:after");
            invocation.ReturnValue = ret;
        }
    }
}
    ProxyGenerator generator = new ProxyGenerator();

    IMyInterface proxy = generator.CreateInterfaceProxyWithTargetInterface(
        typeof(IMyInterface),
        new MyClass(),
        new MyInterceptor()) as IMyInterface;

    Console.WriteLine(proxy.MyMethod(1));

Execution result


MyInterceptor:before
MyMethod:1
MyInterceptor:after
Result

As you can see by looking at the methods defined in ProxyGenerator, there are many functions provided by Java Proxy. In Java, the Proxy class can only be created in a concrete class of an interface, but in C # ProxyGenerator you can create a dynamic proxy for a class derived class in addition to the interface.

5-2.Python Python also has no equivalent to a dynamic proxy. However, standard special methods can be used to achieve dynamic proxy-equivalent functionality.

name version
Python 3.7.1

dynamicProxyTest.py


class MyProxy(object):
    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, name):
        def func(*args):
            print(before)
            result = getattr(self._obj, name)(*args)
            print('after')
            return result
        return func

class MyClass(object):
    def myFunction(self, arg):
        print('myFunction ' + arg)
        return 'result'

myObj = MyClass()
myProxy = MyProxy(myObj)
print(myProxy.myFunction('arg'))

Execution result


before
myFunction arg
after
result

This MyProxy class is a brief explanation for those who are not familiar with Python grammar.

In Python, there is a special method (Special Method) of a method with a specific name with "\ _ \ _" before and after the class definition. Special methods are called by certain events that occur on an object, such as creating an object, calling an attribute, or being specified by the value of a particular operator.

The __getattr__ method is called when you try to get an Attribute that is not defined in an object of that class. The user side of the object gets the return value of __getattr__ as the attribute value if the attribute with the name specified in the class is not defined.

In Python, all variables and methods on a class are attributes. The MyProxy class defines only two special methods, the __init __ method and the __ getattr__ method, so it tries to get attributes from an object of this class (that is, it tries to call some method). And the __getattr__ method is always called. Therefore, in order for MyProxy to behave in the same way as a dynamic proxy, we define a function called func inside the __getattr__ method that acts as a proxy and return that function.

The caller implementation is one line,

myProxy.myFunction('hoge')

However, this is different from Java method invocation.

  1. Get the attribute named myFunction from the myProxy object.
  2. Execute the attribute value obtained in 1. with ('hoge').

Should be interpreted as. I tried to get the attribute named myFunction from the myProxy object, but since myProxy did not have the attribute named myFunction, I get the return value from the __getattr__ method and execute it.

5-3.EcmaScript(JavaScript) EcmaScript provides a class called Proxy from 6 onwards. However, it is used differently from Java's Proxy and acts as an event handler for the object.

Here, we will introduce a function called ʻapply, which is one of the event handlers in Proxy. The ʻapply function is an event handler that is called when you call the function.

name version
node.js v6.11.3

dynamicProxyTest.js


function myFunction(arg1, arg2) {
    console.log("myFunction:" + arg1 + ", " + arg2);
    return "result";
}

const handler = {
    apply : function (target, thisArg, argumentsList) {
        console.log("before");
        var result = target(...argumentsList);
        console.log("after");
        return result;
    }
}

let proxy = new Proxy(myFunction, handler);
console.log(proxy("hoge", "hage"));

Execution result


before
myFunction:hoge, hage
after
result

6. Dynamic proxy anti-patterns

If you read this far, you should be able to implement it with a dynamic proxy. However, due to the high degree of freedom and the special features of dynamic proxies, even if they meet the functional requirements if misused, they are also technologies that may involve non-functional problems. The languages introduced in "5. Dynamic Proxy in Other Languages" are major programming languages that are widely used at the time of writing this article (December 2018), but ~~ are all standard libraries. It should be noted that it does not provide a dynamic proxy ~~. To put it the other way around, the use of dynamic proxies is itself an anti-pattern. [^ 2]

Here, the features of the dynamic proxy are summarized below.

  1. Generate a non-existent source code class when a dynamic process is executed.
  2. Completely separate the dynamic proxy processing from the proxy-mediated processing.
  3. Dynamic proxies can mediate independently of the intervening interface or class.

Among these features, "2. Completely separate the processing of the dynamic proxy from the processing mediated by the proxy" is both an advantage and a disadvantage of the dynamic proxy. Developers of classes that implement objects mediated by dynamic proxies may not be aware of the fact that their classes are being called by dynamic proxy mediated in the environment in which the application actually runs. However, assuming that dynamic proxy mediation is essential for the class to work, inside the proxy in order to reuse the class you created in another environment or run it in your own environment for testing. The difficulty will increase because you have to reproduce the process of. It can also make it difficult to resolve errors and failures.

The simplest alternative to dynamic proxies is to use a lambda expression to define the proxy as follows:

    MyInterface myObj = new MyClass();
    MyProxy.invoke(() -> { nyObj.myMethod(); });

In the MyProxy ʻinvoke method, you can implement the proxy processing and call the Runnableobject passed as an argument to call the original processing that was mediated. This method also allows you to implement proxies across interfaces and classes. What's more, developers can see what kind of mediation the class they define is called by looking at the implementation ofMyProxy`.

Based on the above, I will give an example of use that seems to be an anti-pattern of the application method of dynamic proxy. However, these patterns are not necessarily patterns that should not be used. Is Dynamic Proxy Application Really Optimal? Are there any other more effective alternatives? It's an anti-pattern in the sense that you should consider. The original meaning of a proxy is an agent. Therefore, I will introduce anti-patterns under the title of "○○'s agent" below.

6-1. Agent working in one corner

For example, in the process of checking the input value in the method for retrieving information in the class defined to handle limited information such as users in a huge Web system, it is dynamic. Applying a proxy may not be very desirable. Even if processing normalization and standardization are required due to the large number of input value items, there are other ways to standardize some of the large number of different processing other than dynamic proxy.

Dynamic proxies have the side effect of making it difficult to read the whole picture of the process from your code because it completely separates the process. Even if the scale of processing in a small area becomes large, it would be desirable to implement it so that the whole can be grasped.

6-2. Secretive agent

Suppose that the contents of the concrete class of ʻInvocationHandler used by Proxy` are not exposed, but the developer is forced to implement a class that can be executed only through the processing of the dynamic proxy. Keeping the prerequisites of the implementation you develop secret can be stressful for developers. Of course, if the system encounters problems caused by dynamic proxy processing, as well as psychological problems, the developer can spend a lot of time investigating them. Also, it may be difficult to reuse the implemented processing.

6-3. Agent with too much work

This is a case where the responsibility of the dynamic proxy becomes ambiguous. Initially, the dynamic proxy was implemented only for non-system operations such as log output and DB-related initialization / termination processing, but HTTP request body parsing processing is included and pre-check processing is performed. This is a case where the responsibility of the dynamic proxy increases as the approval process is entered.

This isn't just about dynamic proxies, but class responsibilities should be clarified. You should also follow that policy when making corrections. If necessary, you should go through multiple dynamic proxies with different responsibilities.

6-4. Flooding of agents

In the case where the system has a multi-layered structure such as the controller layer, service layer, logic layer, and data access layer, or in a system in which objects depend on each other in a complicated manner, all objects act as agents. In this situation, some proxies may be needed for processing, but you can imagine that many proxies are not needed. Proxy mediation should be limited to what you need, and you should carefully consider whether you really need other proxy mediation.

6-5. Agent-like

As many of you may have noticed by reading the implementation example of the concrete class of ʻInvocationHandler, Proxy does not enforce the implementation of the proxy pattern. Whether or not to actually call the method passed as an argument in the ʻinvoke method of ʻInvocationHandler is completely up to the implementer of the ʻInvocationHandler concrete class. Therefore, Proxy can be applied to other than proxy patterns.

For example, DTO (Data Transfer Object) is a pattern that defines class members and their corresponding set and get methods. This is because the DTO definition is only the interface that declares the set method and get method, and the internal implementation is defined by ʻInvocation Hadler`.

MyDtoInvocationHadler.java


public class MyDtoInvocationHandler implements InvocationHandler {
    private Map<String, Object> map = new HashMap<String, Object>();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Processing of methods starting with get
        if (method.getName().startsWith("get")) {
            return map.get(method.getName().substring(3));
        }
        //Processing of methods starting with set
        else if (method.getName().startsWith("set")) {
            //abridgement
        }
    }
}

It is doubtful that this application is really useful. Even if you can omit the implementation of the DTO class, you need to define the interface, and you must define the set method and get method of each item in the interface. In most development projects, the implementation of the Entity class corresponding to the DTO class and DB table will have a tool that automatically outputs the code from the design document etc. instead of handwriting. In that case, it doesn't really matter whether the output code is a class or an interface. Conversely, it deprives developers of the option of incorporating implementations other than simple class member interactions into a particular DTO.

6-6. Agent with many orders

It's not a restaurant. An agent that requires a large amount of information in interface annotations and configuration files to mediate the agent. It often happens when you try to make Proxy perform a process other than proxy, as in" 6-5. Proxy-like ". If a large amount of information is ordered from an agent user, shouldn't it be implemented independently by the user instead of the agent?

7. Finally

That concludes the explanation of dynamic proxies in this article. For more detailed explanation, please read the reference site below.

Understanding dynamic proxies is not difficult at all, but as I mentioned in "1. Introduction", in my personal experience, I feel that the general Java programmers of dynamic proxies are not very well known. I will. We speculate that this is due to the following reasons.

  • Beginner literature does not introduce dynamic proxies.
  • The name "proxy" is easily confused with "proxy" on the server side.
  • There are few situations where dynamic proxies are needed in general Java development. (In the first place, there is no function that can be realized only by dynamic proxy)

However, if you are a programmer who has worked on some Java development projects, when you run your code on the framework, the stack trace shows a class call that you don't recognize. Have you ever felt strange when you saw? To answer such questions, it's a good idea to understand dynamic proxies. Also, I don't think Java's dynamic proxy knowledge is wasted when learning other programming languages from Java.

On the other hand, while the release of J2SE 1.3 including Proxy was in May 2000, it is irrelevant that Martin Fowler proposed POJO (Plane Old Java Object) in September 2000. However, I couldn't handle it in this article due to lack of preparation.

We hope that this article will give you an opportunity to gain knowledge of dynamic proxies.

Reference site

https://docs.oracle.com/javase/jp/10/docs/api/java/lang/reflect/Proxy.html https://docs.oracle.com/javase/jp/8/docs/technotes/guides/reflection/proxy.html https://www.ibm.com/developerworks/jp/java/library/j-jtp08305/

[^ 1]: Strictly speaking, the official Java documentation shown on the reference site above says because the proxy class code is generated by trusted system code, only in the newProxyInstnce method Does not specify that it will generate a new class. It really depends on the VM implementation, and it's possible that you're implementing a dynamic proxy with a Python-like approach. However, since the Proxy class does not know what kind of dynamic proxy class is needed until the interface class is specified in the argument of the newProxyInstnce method, it is interpreted as creating a new class in the newProxyInstnce method. I have. [^ 2]: This is an extreme argument, but you should always consider using dynamic proxies carefully enough to suspect that you may fall into anti-patterns.

Recommended Posts