Agent, static, dynamic

agent

A proxy is provided for other objects to control the access of this object. In some cases, when an object cannot directly access that object, the proxy acts as an intermediary between the client and the * * proxied object (delegate class) * *.

According to the creation period of the agent, the agent class can be divided into two types:

Static: programmers create proxy classes or specific tools to automatically generate source code and then compile it. The. Class file of the proxy class already exists before the program runs.

Dynamic: it is created dynamically by using reflection mechanism when the program is running.

Static proxy

Subject: the proxy class and the proxy class implement the same interface

Proxy: proxy class, which contains the proxy class. The specific logic is delegated to the proxy class for processing

RealSubject: a proxy class in which you can perform access control and additional business processing

Client: you see the proxy class, but you don't know the class that handles the business logic, which reduces the coupling

code implementation

UserDAO proxy and proxied public interface (Subject)

public interface ProxyDao {
    boolean insert(String name);
}

UserDAOImpl proxy class (RealSubject)

public class ProxyDaoImpl implements ProxyDao {
    @Override
    public boolean insert(String name) {
        System.out.println("insert name=" + name);
        return true;
    }
}

ProxyByInterface proxy class, which implements proxy by implementing interface

public class ProxyByInterface implements ProxyDao {
    private ProxyDao proxyDao;
    public ProxyByInterface(ProxyDao proxyDao) {
        this.proxyDao = proxyDao;
    }

    @Override
    public boolean insert(String name) {
        System.out.println("before insert by interface");
        return proxyDao.insert(name);
    }
}

ProxyByExtend proxy class, which inherits the proxy method (Proxy) implemented by the proxy class

public class ProxyByExtend extends ProxyDaoImpl{
    private ProxyDaoImpl proxyDao;
    public ProxyByExtend(ProxyDaoImpl proxyDao) {
        this.proxyDao = proxyDao;
    }

    @Override
    public boolean insert(String name) {
        System.out.println("before insert by extend");
        return proxyDao.insert(name);
    }
}

Get proxy object client

public class Client {
    public static void main(String[] args) {
        ProxyDaoImpl proxyDao = new ProxyDaoImpl();
        //The proxy class implements the same interface as the proxy class
        ProxyByInterface proxyByInterface = new ProxyByInterface(proxyDao);
        proxyByInterface.insert("zc-Interface");
        //Proxy by inheriting the proxied class
        ProxyByExtend proxyByExtend = new ProxyByExtend(proxyDao);
        proxyByExtend.insert("zc-Extend");
    }
}

Benefits:

You can add some functions again without touching the logic of the original class, which conforms to the opening and closing principle.

The real business is still handled by the proxy object. You can use the proxy without modifying the original class.

Disadvantages:

A lot of code duplication has occurred. If a method is added to the interface, all proxy classes need to implement this method in addition to all implementation classes. It increases the complexity of code maintenance.

Proxy objects only serve one type of objects. If you want to serve multiple types of objects. It is bound to proxy for each object. Static proxy is incompetent when the program scale is a little large.

Dynamic agent

JDK dynamic agent

Jdk dynamic proxy is aimed at the class that implements the interface;

It is required that the target object must implement the interface, because it is created according to the interface when creating the proxy object. The proxy object can implement multiple interfaces. When creating a proxy, the proxy object that specifies to create an interface can call the method defined by the interface.

java.lang.reflect.InvocationHandler interface and java.lang.reflect.Proxy class support are required

//Object proxy: the proxy object
//Method: method to call
//Object[] args: required parameters for method call
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
//Classloader: loader of class
//Class<?>  Interfaces: get all interfaces
//InvocationHandler h: get the instance of subclass of InvocationHandler interface
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

code implementation

NameDao name - Interface (Subject)

public interface NameDao {
    public boolean addName(String name);
}

AgeDao age - Interface (Subject)

public interface AgeDao {
    public boolean addAge(Integer age);
}

NameAndAgeDaoImpl name, age implementation class (RealSubject)

public class NameAndAgeDaoImpl implements NameDao,AgeDao {
    @Override
    public boolean addName(String name) {
        System.out.println("NameDaoImpl----->" + name);
        return true;
    }
    @Override
    public boolean addAge(Integer age) {
        System.out.println("AgeDaoImpl----->" + age);
        return true;
    }
}

MyInvocationHandler, which enhances the methods provided by the interface (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("----------before----------");
        System.out.println("Proxy=" + proxy.getClass());
        System.out.println("method=" + method);
        System.out.println("args=" + Arrays.toString(args));
        //Execute target method object
        Object result = method.invoke(target, args);
        System.out.println("----------after----------");
        return result;
    }
}

ProxyFactory agent factory

public class ProxyFactory {
    public static Object getProxy(Object proxyObj) {
        /**
         * loader Specifies the loader that loads proxy objects dynamically generated by the jvm runtime
         * interface All interfaces implemented by real objects
         * h Implements the InvocationHandler interface object
         */
      // return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(),
      // proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj));
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj));
    }

    public static void main(String[] args) {
        NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl());
        AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl());
        nameDao.addName("zc");
        ageDao.addAge(20);
    }
}

reflection

  • Why do you need classes that implement interfaces, not classes

In the main function, run the statement:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
public static void main(String[] args) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

    NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl());
    AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl());
    nameDao.addName("zc");
    ageDao.addAge(20);
}

You can view the $Proxy0 class:

You will find that he has inherited the Proxy and then created one (more) interfaces; Because java has the characteristics of single inheritance and multiple interfaces, JDK dynamic Proxy needs to implement interface classes.

CGLib dynamic proxy

CGLIB implements dynamic proxy without requiring the proxy class to implement the interface. The bottom layer uses asm bytecode generation framework to generate proxy class bytecode (this proxy class inherits the proxy class).

Therefore, the proxied class must not be defined as final class, and the final method cannot be proxied.

Implementation needs

//intercept method of MethodInterceptor interface
/**
*obj Proxy object
*method Delegate class method, method of the proxy object, bytecode object
*arg Method parameters
*MethodProxy Proxy method MethodProxy object. Each method will have such an object 
*/
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
Ehancer enhancer = new Enhancer() //Enhancer is a bytecode enhancer, which is very convenient to extend classes
enhancer.setSuperClass(Proxy class.class);
enhancer.setCallback(realization MethodInterceptor Object of interface)
enhancer.create()//Returns the proxy object, which is a subclass of the proxied class

code implementation

UserDaoImpl user implementation class (RealSubject)

public class UserDaoImpl {
    public boolean insert(String name) {
        System.out.println("insert name=" + name);
        return true;
    }

    public final boolean insert1(String name) {
        System.out.println("final insert name=" + name);
        return true;
    }
}

CglibProxy CGLIB proxy class (Proxy)

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----------before----------");
        System.out.println("Proxy=" + o.getClass());
        System.out.println("method=" + method);
        System.out.println("args=" + Arrays.toString(objects));
        System.out.println("methodProxy=" + methodProxy);
        //Execute target method object
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("----------after----------");
        return result;
    }
}

ProxyFactory agent factory

public class ProxyFactory {
    private static Enhancer enhancer = new Enhancer();
    private static CglibProxy cglibProxy = new CglibProxy();
    
    public static Object getProxy(Class cls) {
        enhancer.setSuperclass(cls);
        enhancer.setCallback(cglibProxy);
        return enhancer.create();
    }
    
    public static void main(String[] args) {
        UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class);
        userDao.insert("zc");
    }
}

reflection

  • Why do you use invokeSuper() instead of invoke()
  1. Method method is the method bytecode object of the proxy object.

  2. MethodProxy methodProxy is the method bytecode object of the proxy object.

Benefits of using MethodProxy:

  1. The proxy object does not need to be passed in to the proxy object, which is more efficient.
  2. There will be no dead cycle.

From the first point of view, you can see that the second point is explained:

How to cause the phenomenon of dead circulation:

	Proxy.newProxyInstance(xxx, xxx,
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    	...
                      //Add this sentence  
                    	proxy.toString();
                    	...
                    }
                });

Reason: when the proxy object method, it will pass through the interceptor method. Therefore, if the method of the proxy object is called again in the interceptor, it will enter the interceptor again, forming an endless loop.

**invokeSuper() * * method, you can use the method of the parent class of the proxy object (that is, the proxy object) without going through the interceptor ----- for details, you can learn: class loading mechanism and parent delegation model

Tags: Dynamic Proxy

Posted on Sun, 05 Sep 2021 13:06:19 -0400 by purencool