Design Mode-Proxy Mode JAVA Implementation

Keywords: Java JDK SpringBoot Spring

The proxy model is simply a design pattern for pre- and post-intervention on Methods in existing classes to modify an existing business at the class or method level without modifying an existing business class.

There are currently two implementations, one is a static proxy, implemented purely through code based on design patterns.The other is dynamic proxy, which needs to be enhanced by JDK's default capabilities and importing CGLIG feature packages.

The implementation of the static proxy is carried out first.

package proxy.staticproxy;

import java.util.List;

import bean.PickDoc;
import bean.PickList;
import bean.PickTask;
import builder.IPickTask;

Specific implementation of static proxy
public class StaticPickTaskProxy implements IPickTask {

//Proxy Object
private IPickTask pickTask;

public StaticPickTaskProxy(IPickTask pickTask) {
    this.pickTask = pickTask;
}

@Override
public List<PickList> getPickList(List<PickDoc> list) {

    System.out.println("pre-processing"+pickTask.getClass().getName()+"Action before object invocation");
    pickTask.getPickList(list);
    System.out.println("Postprocessing"+pickTask.getClass().getName()+"Action before object invocation");
    return null;
}

@Override
public List<PickTask> getPickTask(List<PickList> list) {
    // TODO Auto-generated method stub
    return null;
}

}

//Actual call of static proxy
package proxy.staticproxy;

import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;

public class StaticPickTaskProxyMain {

public static void main (String[] args) {

    //Use proxy classes instead of specific business classes to operate on
    IPickTask pickTaskProxy = new StaticPickTaskProxy(new SinglePickTask());

    //Use proxy classes instead of specific business classes to operate on
    IPickTask pickTaskProxy1 = new StaticPickTaskProxy(new UnionPickTask());

    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

    //Non-merge picking
    pickTaskProxy.getPickList(pickDocList);

    //Merge picking
    pickTaskProxy1.getPickList(pickDocList);
}

}

Static proxy is simple and easy to understand.What's more, if you design your business well in the early stages, you can reduce the amount of open code development to a certain extent and improve maintainability at the same time.

For example, it is often described that a static proxy can only be manually implemented for one or more methods in a specific class.However, because the two business implementation classes of the example, SinglePickTask and UnionPickTask, are implemented for the IPickTask interface (in builder mode).This allows me to proxy this set of business implementation classes with a static proxy class.That's the real benefit

Default implementation of JDK in dynamic proxy
package proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicJDKProxy implements InvocationHandler {

//Proxy Object
private Object obj ;

public DynamicJDKProxy(Object obj) {
    this.obj = obj;
}

//When an overridden method call is made through an object of a proxy class, it is converted to a call to the following invoke method
@Override
/**
 * proxy   Make no mistake about the proxy class object, which implements the proxy method's object, not the proxy class object
 * proxy - the proxy instance that the method was invoked on
 * 
 * The parameter method is an instance that calls the interface method on the proxy instance.Declared
 * Method object class is the interface declared by this method, which is the parent of all proxy interfaces that inherit the current method
 * method - the Method instance corresponding to the interface method 
 * invoked on the proxy instance.The declaringclass of the Method 
 * object will be the interface that the method was declared in, which 
 * may be a superinterface of theproxy interface that the proxy 
 * class inherits the method through.
 * 
 * The parameter args is an array parameter that contains the object transferred in the proxy method call.Or the interface has no parameters.
 * Parameters of the original type are packaged in the appropriate wrapper class, such as Integer or Boolean.
 * args - an array of objects containing the values of thearguments 
 * passed in the method invocation on the proxy instance,or null if 
 * interface method takes no arguments.Arguments of primitive types 
 * are wrapped in instances of the appropriate primitive wrapper class,
 * such as java.lang.Integer or java.lang.Boolean.
 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("Current Proxy Object Name:"+obj.getClass().getSimpleName());
    System.out.println("Current proxy method name:"+method.getName());
    if(args!=null) {
        for(Object obj: args) {

            System.out.println("Parameter object:"+obj.getClass().getSimpleName());
        }
    }
    Object returnObj =null;
    if(method.getName().equals("getPickList")) {
        System.out.println("pre-processing");
        returnObj = method.invoke(obj, args);
        System.out.println("Postprocessing");
    }

    return returnObj;
}

}

//Implement Call Test Code
package proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;

public class DynamicJDKProxyMain {

public static void main(String[] arg) {

    IPickTask  singlePickTask = new SinglePickTask();

    IPickTask  unionPickTask = new UnionPickTask();

    //Define picking document list
    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

     //Object to proxy
    InvocationHandler singlePickTaskHandler = new DynamicJDKProxy(singlePickTask);

    InvocationHandler unionPickTaskHandler = new DynamicJDKProxy(unionPickTask);

    //Get Class Loader
    ClassLoader loader = singlePickTask.getClass().getClassLoader();
    //Get a list of class interface objects
    Class[] interfaces = singlePickTask.getClass().getInterfaces();
     /*
     * classloader,Which class to proxy uses the loader of which class to load the newly created proxy class
     * interfaces,Which interfaces the proxy class implements, consistent with the object of the proxy class
     * handler, Processor for method distribution calls
     * loader - the class loader to define the proxy class
     * interfaces - the list of interfaces for the proxy classto implement
     * h - the invocation handler to dispatch method invocations to
     * Here is to dynamically generate a new proxy class for the object to be proxied.
     * So as a new class, it needs a corresponding class loader, and this class is constructed by reflection.
     * So the list of methods it constructs comes from the proxied object
     */
    IPickTask proxyPickTask = (IPickTask) Proxy.newProxyInstance(loader, interfaces, singlePickTaskHandler);

    proxyPickTask.getPickList(pickDocList);
}

}
//Run results

There are already many notes here and they should be easier to understand.All in all, JDK automatically builds a proxy class to proxy the current business class in the same way as the static proxy implementation.The advantage over static proxies is that you don't have to implement all the methods of the business class's corresponding interface manually, especially for proxies of different business classes based on multiple interfaces.

CGLIB Dynamic Proxy Implementation
First, this is not a default feature of JDK; you need to either download the JAR package separately or run it under the springboot project, introducing the corresponding implementation that comes with the springboot project.This example references cglib-nodep-2.2.2.jar

The proxy class code is as follows
package proxy.dynamicproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class DynamicCGLibProxy implements MethodInterceptor {

private Object target;

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

public Object newInstance() {

    //Enhance the proxy object to generate a proxy object here
    Enhancer enhancer = new Enhancer();                
    enhancer.setSuperclass(target.getClass());                
    enhancer.setCallback(this);                
    return enhancer.create();

}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

    System.out.println("Current Proxy Object Name:"+obj.getClass().getSimpleName());
    System.out.println("Current proxy method name:"+method.getName());
    if(args!=null) {
        for(Object arg: args) {

            System.out.println("Parameter object:"+arg.getClass().getSimpleName());
        }
    }
    Object returnObj =null;
    if(method.getName().equals("getPickList")) {
        System.out.println("pre-processing");
        returnObj = proxy.invokeSuper(obj, args);
        System.out.println("Postprocessing");
    }

    return returnObj;
}

}

Call the demo code as follows
package proxy.dynamicproxy;

import java.util.ArrayList;
import java.util.List;

import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;

public class DynamicCGLibProxyMain {

public static void main(String[] args) {

    List<PickDoc> pickDocList = new ArrayList<PickDoc>();

    //SinglePickTask Actual Business Class
    //Proxy class generated by pickTaskProxy
    IPickTask pickTaskProxy=(SinglePickTask)new DynamicCGLibProxy(new SinglePickTask()).newInstance();

    pickTaskProxy.getPickList(pickDocList);

}

}
//Call Effect

CGLIB proxy implementations are essentially the same as jdk, both of which are new proxy classes.The difference is that they have different sources of methods to generate proxy classes.JDK generates the appropriate method for reflection based on the list of object interfaces fetched.CGLIB solves this problem by generating a subclass object of the object being proxied

Priority is given to the proxy implementation that comes with JDK in spring, and CGLIB when business methods are not implemented based on interfaces.

Another student added that spring boot2.x uses cglib as a dynamic proxy by default

In addition, in all three implementations, the object that needs to be proxied is passed in through the construction method, which I think is a better way to handle.

Posted by mwasif on Sun, 05 Apr 2020 20:15:41 -0700