proxy pattern

August 8, 2018 16:01:54

proxy pattern

Usage scenario

Proxy mode, which provides a proxy for other objects to control access to this object------ Design patterns: the foundation of Reusable Object-Oriented Software

1. Remote proxy: provide a local representation for an object in different address spaces, and hide the fact that the object exists in different address spaces, such as RMI stub (RMI is also an important concept). This different address space can be in the same host or in different hosts.

2. Virtual proxy: if you create an object with high cost, you can create a proxy object with low cost first. The real object is created only when needed, delaying loading.

3. Security agent (protection agent): used to control the access permissions of real objects.

4. Intelligent reference proxy: when calling a real object, the proxy provides additional operations, such as recording the number of times the object is called (i.e. reference count)

5. Buffer agent: provides temporary storage space for the results of a target operation so that multiple clients can share these results.

role

Abstract Subject: declare the common interface between real objects and Proxy objects, so that Proxy objects can be used wherever real objects are used.

Proxy role: the proxy object role contains references to real objects, so that real objects can be operated. At the same time, the proxy object provides the same interface as the domain real object, so that it can replace the real object at any time. The proxy object can realize the functions described in the above use scenario.

RealSubject: the real object represented by the proxy pattern.

Static proxy

--Illustration

--Code example

Abstract interface (Subject.java):

package com.mingmingcome.designpattern.proxy.staticproxy;

/** 
 * @ClassName: Subject
 * @Description: Abstract interface: a common interface between proxy objects and real objects
 * @author: luhaoming
 * @date: 2018 August 12, 2014 2:56:43 PM
 */
public interface Subject {
	void request();
}

Proxy class (Proxy.java):

package com.mingmingcome.designpattern.proxy.staticproxy;

/** 
 * @ClassName: Proxy
 * @Description: Proxy object: the proxy object role contains references to real objects, so that real objects can be manipulated,
 * At the same time, the proxy object provides the same interface as the real object in the domain, so that it can replace the real object at any time.
 * @author: luhaoming
 * @date: 2018 August 12, 2013 3:00:10 PM
 */
public class Proxy implements Subject {
	// References to real topic objects
	private Subject subject;
	
	public Proxy(Subject subject) {
		this.subject = subject;
	}
	
	@Override
	public void request() {
		preRequest();
		// Methods of executing real objects
		subject.request();
		afterRequest();
	}
	
	private void preRequest() {
		System.out.println("This is what you do before calling a real object...");
	}
	
	private void afterRequest() {
		System.out.println("This is what you do after calling the real object...");
	}

}

Real topic class (RealSubject):

package com.mingmingcome.designpattern.proxy.staticproxy;

/** 
 * @ClassName: RealSubject
 * @Description: Real object
 * @author: luhaoming
 * @date: 2018 August 12, 2013 3:10:59 PM
 */
public class RealSubject implements Subject {
	
	@Override
	public void request() {
		System.out.println("Real request");
	}

}

Test class (TestStaticProxy.java):

package com.mingmingcome.designpattern.proxy.staticproxy;

/** 
 * @ClassName: TestStaticProxy
 * @Description: Static proxy test class
 * @author: luhaoming
 * @date: 2018 August 12, 2013 3:22:19 PM
 */
public class TestStaticProxy {

	public static void main(String[] args) {
		Subject subject = new RealSubject();
		Proxy proxy = new Proxy(subject);
		proxy.request();
	}

}

Execution results:

This is what you do before calling a real object...
Real request
 This is what you do after calling the real object...

Summary:

1. You can extend the target function without modifying the function of the real object

2. Disadvantages: because the proxy object needs to implement the same interface with the real object, there will be many proxy classes corresponding to the real object class. In addition, if the interface adds or deletes methods, the real object class and proxy object class must be maintained at the same time.

3. The function of the proxy class and the real object proxy are determined at compile time and cannot be modified in the future

discuss

Above, we have implemented the proxy function in the way of static proxy, so why do we need dynamic proxy? Discuss two situations:

1. To add 100 methods to the abstract interface, you need to add logs for each method

2. There are 100 implementation classes with different abstract interfaces. You need to add logs for one of the methods in each class

In the first case, the proxy class using static proxy:

public class Proxy implements Subject {
	// References to real topic objects
	private Subject subject;
	
	public Proxy(Subject subject) {
		this.subject = subject;
	}
	
	@Override
	public void request0() {
		System.our.println("call Subject of request0 method");
		subject.request0();
	}

	@Override
	public void request1() {
		System.our.println("call Subject of request1 method");
		subject.request1();
	}

	// Provide logs for the methods of each interface
	// ...

}

In the second case, to create a corresponding proxy class for each implementation class, there are 100 more classes.

Static proxy is suitable for: the proxy object is fixed. We only need to proxy one class or several fixed classes when the number is not too large. However, like the two cases discussed above, we can use dynamic proxy to gracefully solve the problem of writing many methods or creating many classes.

Dynamic agent

JDK dynamic agent

Illustration

The following is the dynamic agent of JDK:

Code example

First, let's learn about the InvocationHandler interface under the java.lang.reflect package and the newProxyInstance method of the Proxy class.

There is only one method in InvocationHandler, which is invoke.

package java.lang.reflect;

public interface InvocationHandler {
	/**
	 * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     * Process the method call of the proxy instance and return the result. When a method is called on its associated proxy instance,
     * This method is called on the calling processor.
     * (Proxy instance method - > invocationhandler method - > real object method)
     */
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
}

The methods called in the proxy class will be forwarded to the real object for execution through the invoke method. The figure is as follows:

In the figure, Proxy is a dynamically generated Proxy class, InvocationHandler is an implementation class that implements the InvocationHandler interface, and target is a real object Subject.

newProxyInstance method in Proxy class:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
	throws IllegalArgumentException

All Proxy classes are static methods. You can know that this should be a tool class. The newProxyInstance method is used to dynamically generate Proxy classes.

Three parameters:

  • `Classloader `: the class loader to define the proxy class defines the class loader of the proxy class
  • `Class[] interfaces `: the list of interfaces for the proxy class to implement
  • `InvocationHandler h `: the invocation handler to dispatch method invocations to the calling processor of the distribution method call

Relative to static agents:

1. The interface class and real object class of dynamic proxy are the same as that of static proxy, that is, Subject class and RealSubject class.

2. There is a SubjectInvocationHandler class that implements the InvocationHandler interface, which is used to handle the methods called on the proxy class and forward them to the real object.

3. Dynamically produce proxy classes through Proxy.newProxyInstance()

InvocationHandler implementation class (SubjectInvocationHandler.java):

package com.mingmingcome.designpattern.proxy.dynamicproxy;

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

/** 
 * @ClassName: Proxy
 * @Description: InvocationHandler Implementation class: the calling processor that distributes method calls as dynamically generated proxy classes
 * @author: luhaoming
 * @date: 2018 August 12, 2014 4:26:45 PM
 */
public class SubjectInvocationHandler implements InvocationHandler {
	
	// Here, the real Object is no longer a concrete interface, but an Object (all classes that implement any interface)
	private Object object;
	
	public SubjectInvocationHandler(Object object) {
		this.object = object;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// Function of imitating log
		System.out.println("Before calling");
		method.invoke(object, args);
		// Function of imitating log
		System.out.println("After call");
		return null;
	}

}

Test class (TestDynamicProxy.java):

package com.mingmingcome.designpattern.proxy.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/** 
 * @ClassName: TestDynamicProxy
 * @Description: TODO
 * @author: luhaoming
 * @date: 2018 August 12, 2006 6:59:23 PM
 */
public class TestDynamicProxy {

	public static void main(String[] args) {
		// the class loader to define the proxy class
		// Defines the class loader for the proxy class
		// The class loaders of Subjec, RealSuject and TestDynamicProxy in the test class are the same. I don't know whether to specify which class loader
		ClassLoader loader = Subject.class.getClassLoader();

		// the list of interfaces for the proxy class to implement
		// List of interfaces to be implemented by proxy class
		Class<?>[] interfaces = RealSubject.class.getInterfaces();
		
		// the invocation handler to dispatch method invocations to
		// Call processor for distributing method calls
		InvocationHandler handler = new SubjectInvocationHandler(new RealSubject());
		
		// Production agent class
		Subject proxy = (Subject)Proxy.newProxyInstance(loader, interfaces, handler);
		
		// Calling real object methods
		proxy.request();
	}

}

Summary:

1. Perfectly solve the shortcomings of these two static agents: 1) modifying the interface class (Subject) requires modifying the agent class; 2) Adding a proxy class (real object) requires adding a proxy class. No matter how you modify the interface class or add the proxy class (real object), as long as you provide your interface class and real object, the dynamic proxy can dynamically generate the proxy class you need.

2. Disadvantages: the proxied class (real object) always needs an interface.

Thinking: is there a proxy method that does not need an interface and is a dynamic proxy? Here comes the CGLIB agent.

CGLIB dynamic proxy

Illustration

Code example

CGLIB is implemented based on ASM and provides more powerful dynamic characteristics than reflection. It is very convenient to implement dynamic agent with CGLIB.

CGLIB proxy related classes:

  • net.sf.cglib.proxy.Enhancer is the main enhancement class.
  • net.sf.cglib.proxy.MethodInterceptor is the main method interception class. It is a sub interface of the Callback interface and needs to be implemented by the user.
  • Net.sf.cglib.proxy.methodproxy the proxy class of the java.lang.reflect.Method class of the JDK can easily call the source object methods.

cglib dynamically generates a subclass to override the non final method of the proxy class, and sets callback, then each method call of the original class will be transformed into calling user-defined interceptors.

Let's take a look at the MethodInterceptor class and Enhancer class.

1. MethodInterceptor interface

This class is similar to the InvocationHandler interface, with only one method, intercept();

package net.sf.cglib.proxy;

public interface MethodInterceptor extends Callback
{
    /**
     * All generated proxied methods call this method instead of the original method.
     * The original method may either be invoked by normal reflection using the Method object,
     * or by using the MethodProxy (faster).
     * All generated proxy methods call this Method instead of the original Method. The original Method or Method object is called through ordinary reflection,
     * Or use the MethodProxy call (faster).
     * @param obj "this", the enhanced object Enhanced object
     * @param method intercepted Method Intercepted method
     * @param args argument array; primitive types are wrapped Parameter array; The original type is boxed
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed Used to call superclasses (do not intercept methods); May be called multiple times as needed
     * @throws Throwable any exception may be thrown; if so, super method will not be invoked
     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
     * @see MethodProxy
     */    
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

The intercept method is used to intercept methods that call real objects. As shown in the figure:

2. Enhancer class

Several main methods used by agents:

  • void setSuperclass(java.lang.Class superclass) sets the parent class of the generated proxy object.
  • void setCallback(Callback callback) sets the instance of the CallBack interface, that is, the CallBack function that implements the MethodInterceptor interface. When a method is intercepted, it is forwarded to the real object
  • void setCallbacks(Callback[] callbacks) sets instances of multiple CallBack interfaces.
  • Void setcallbackfilter (callbackfilter) sets the method callback filter.
  • Object create() creates the target object using the default parameterless constructor.
  • Object create(Class[], Object []) creates a target object using a constructor with parameters. The parameter Class [] defines the type of the parameter, and the second object [] is the value of the parameter.
3. Code

The real object in the code example still uses RealSubject.

MethodInterceptor implementation class SubjectMethodInterceptorImpl (SubjectMethodInterceptorImpl.java):

package com.mingmingcome.designpattern.proxy.cglibproxy;

import java.lang.reflect.Method;

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

/** 
 * @ClassName: SubjectMethodInterceptorImpl
 * @Description: The method interceptor of MethodInterceptor is implemented
 * @author: luhaoming
 * @date: 2018 August 13, 2007 7:10:08 PM
 */
public class SubjectMethodInterceptorImpl implements MethodInterceptor {

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("Before interception");
		// Call the methods of the original class on a concrete object
		Object ret = proxy.invokeSuper(obj, args);
		// Object ret1 = method.invoke(obj, args); //  In this way, a life and death cycle will occur because the method will be intercepted
		System.out.println("After interception");
		return ret;
	}

}

The intercept method is implemented.

update begin at 2018-12-19 08:45:26:

As for method.invoke(obj, args), a life and death cycle will occur, as shown in the figure:

Why is there an endless cycle? First, we generate a proxy instance that inherits the subclass of the superclass we set by calling setSuperclass(), overriding the Method of the parent class. Secondly, when we call the Method of the proxy object, we call the Method of the subclass, that is, the Method after rewriting, and the Method object is in the code. If method.invoke(obj, args) is used at this time, it is equivalent to calling itself, similar code:

public void request() {
	request();
	System.out.println("Real request");
}

This creates an endless cycle.

update end at 2018-12-19 09:10:11

Dynamically generate the test class of the proxy instance (TestCglibProxy.java)

package com.mingmingcome.designpattern.proxy.cglibproxy;

import net.sf.cglib.proxy.Enhancer;

/** 
 * @ClassName: TestCglibProxy
 * @Description: Test: dynamically generate the proxy instance and call the proxy instance method
 * @author: luhaoming
 * @date: 2018 August 13, 2007 7:43:41 PM
 */
public class TestCglibProxy {

	public static void main(String[] args) {
		// Dynamic agent enhancer
		Enhancer enhancer = new Enhancer();
		// Set a superclass, which can be a concrete class that does not implement any interface, or an interface.
		// If it is an interface, setInterfaces will be called.
		enhancer.setSuperclass(RealSubject.class);
		// Set Callback function (MethodInterceptor interface inherits Callback interface)
		enhancer.setCallback(new SubjectMethodInterceptorImpl());
		// Dynamically generated proxy instance
		RealSubject proxy = (RealSubject)enhancer.create();
		// Call the method of the proxy instance
		proxy.request();
		
	}

}

Create a proxy instance through the Enhancer method.

Problems encountered during:

Exception in thread "main" java.lang.VerifyError: class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at net.sf.cglib.core.AbstractClassGenerator.<init>(AbstractClassGenerator.java:38)
	at net.sf.cglib.core.KeyFactory$Generator.<init>(KeyFactory.java:127)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:112)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
	at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104)
	at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69)
	at com.mingmingcome.designpattern.proxy.cglibproxy.TestCglibProxy.main(TestCglibProxy.java:15)

In fact, the versions of cglib and ASM do not correspond. I use cglib-2.2.2.jar , it should correspond to asm-3.3.1.jar , and not built with maven.

Summary: CGLIB agent is simpler and easier to use than JDK dynamic agent, and the real object connector does not need to be implemented.

summary

This paper introduces three implementation methods of agent mode: static agent, JDK dynamic agent and CGLIB dynamic agent. There are many rules and regulations for static agents. JDK dynamic agents need to implement interfaces, and CGLIB agent interfaces do not need to be implemented.

reference resources:

CGLIB learning notes

Three proxy modes of Java

Java static proxy & dynamic proxy notes

August 14, 2018 09:54:29

Posted on Mon, 29 Nov 2021 05:55:41 -0500 by DLR