Dynamic Proxy Mechanism of Java Technology

#static proxy

The conventional proxy mode consists of the following three parts: functional interface

interface IFunction {
	void doAThing();
}

function provider

class FunctionProvider implement IFunction {
	public void doAThing {
		System.out.print("do A");
	}
}

functional agent

class Proxy implement IFunction {
	private FunctionProvider provider;
	Proxy(FunctionProvider provider) {
		this.provider = provider;
	}
	public void doAThing {
		provider.doAThing();
	}
}

The first two are ordinary interfaces and implementation classes, while the third is the so-called proxy class. For the user, he will let the proxy class to complete a certain task, and does not care about the specific errand runner of the task.

This is a static proxy. The advantage is that it is convenient to adjust and transform the concrete implementation class, and the user will not be affected in any way.

However, this method also has drawbacks: for example, if there are multiple interfaces that need to be proxied, then a corresponding proxy class must be created for each function provider, which will become larger and larger. Also, the so-called "static" proxy means that the delegated class to be proxied must be known in advance.

Let’s illustrate it with the following example: Statistical function time-consuming – static proxy implementation

Now I hope to use a proxy class to perform time-consuming statistics on the methods I am interested in. Using static proxy, the following implementations are achieved:

interface IAFunc {
	void doA();
}
interface IBFunc {
	void doB();
}

class TimeConsumeProxy implement IAFunc, IBFunc {
	private AFunc a;
	private BFunc b;
	public(AFunc a, BFunc b) {
		this.a = a;
		this.b = b;
	}
	void doA() {
		long start = System.currentMillions();
		a.doA();
		
		System.out.println("耗时:" + (System.currentMillions() - start));
	}
	void doB() {
		long start = System.currentMillions();
		b.doB();
		
		System.out.println("耗时:" + (System.currentMillions() - start));
	}
}

The disadvantage is obvious. If there are more interfaces, the TimeConsumeProxy proxy class must be modified every time a function is added: the delegate class object is passed in, the interface is implemented, and the time consumption is counted before and after the function is executed.

This method is obviously not sustainable. Let's take a look at the implementation method using dynamic agents for comparison. #dynamic proxy

The core idea of ​​dynamic proxy is to dynamically generate a proxy object for any incoming object through the Java Proxy class. This proxy object implements all the interfaces of the original object by default.

It is more direct to illustrate the time-consuming example of statistical functions. Statistical function time-consuming – dynamic proxy implementation

interface IAFunc {
	void doA();
}
interface IBFunc {
	void doB();
}

class A implement IAFunc { ... }
class B implement IBFunc { ... }

class TimeConsumeProxy implements InvocationHandler {
	private Object realObject;
	public Object bind(Object realObject) {
		this.realObject = realObject;
		Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);
		return proxyObject;
	}
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentMillions();
        Object result = method.invoke(target, args);
        System.out.println("耗时:" + (System.currentMillions() - start));
        return result;
    }
}

When using it specifically:

public static void main(String[] args) {
	A a = new A();
	IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);
	aProxy.doA();
	B b = new B();
	IBFunc bProxy = (IBFunc) new TimeConsumeProxy().bind(b);
	bProxy.doB();	
}

The biggest difference here is that the proxy class and the delegate class are transparent and independent of each other, and the logic is not coupled in any way, and they are only bound together at runtime. This is the biggest difference between static proxy and dynamic proxy. The advantage is that no matter how many delegate classes there are, the proxy class is not affected in any way, and there is no need to know the specific delegate class at compile time.

Back to the dynamic proxy itself, the most important thing in the above code is:

Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);

Through the Proxy tool, the real delegate class is converted into a proxy class. At the beginning, the three elements of a proxy model are mentioned: functional interface, functional provider, and functional proxy; the corresponding ones here are: realObject.getClass().getInterfaces(), realObject, TimeConsumeProxy.

In fact, dynamic proxy is not complicated. Through a Proxy tool, a proxy object is automatically generated for the interface of the delegated class. Subsequent function calls are initiated through this proxy object, and will eventually be executed to the InvocationHandler#invokemethod . In this method, in addition to calling the real delegated class For the corresponding method, you can also do some other custom logic, such as the above running time statistics. #Explore the dynamic proxy implementation mechanism

Well, above we have explained the basic usage of dynamic proxy and why to use dynamic proxy. Many articles are almost here, but we are ready to explore further for interested readers.

A few questions are thrown:

What exactly is the proxy object Object proxyObject generated above? Why can it be cast to IAFunc and still call the doA() method? How is this proxyObject generated? Is it a class?

Let me give the answer first, and then step by step to explore how this answer came. Question 1: proxyObject What is it -> dynamically generated $Proxy0.classfile

Proxy.newInstanceAfter calling , Java will eventually generate a real class file for the delegate class A: $Proxy0.class, and proxyObjectis an instance of this class.

Guess what this $Proxy0.classclass looks like and what methods does it contain? Look back at the code just now:

IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);
aProxy.doA();

Under reasoning, obviously this $Proxy0.classimplements the IAFunc interface, and it also implements the doA()method internally, and the point is: this doA()method will be executed into the method at runtime TimeConsumeProxy#invoke().

Here comes the point! Let's take a look at this $Proxy0.classfile and put it into the IDE to decompile it. You can see the following content to verify the conjecture just now:

final class $Proxy0 extends Proxy implements IAFunc {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final boolean equals(Object var1) throws  {
        // 省略
    }
    public final void doA() throws  {
        try {
        	// 划重点
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final String toString() throws  {
        // 省略
    }
    public final int hashCode() throws  {
        // 省略
    }
    static {
        try {
        	// 划重点
            m3 = Class.forName("proxy.IAFunc").getMethod("doA", new Class[0]);
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

That's right, the guesses just now are all right! Implemented the IAFunc interface and the doA() method, but what the hell is in doA()?

super.h.invoke(this, m3, (Object[])null);

Looking back, what is the function signature of TimeConsumeProxythe invokemethod ?

public Object invoke(Object proxy, Method method, Object[] args);

That's right, doA()all it does is call the TimeConsumeProxy#invoke()method .

That is to say, the execution flow of the following code is as follows:

IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);
aProxy.doA();

Based on the incoming delegate class A, generate a $Proxy0.classfile; create an $Proxy0.classobject and transform it into an IAFuncinterface; aProxy.doA()when calling , TimeConsumeProxythe internal invokemethod is automatically called.

Question 2: How is proxyObject generated step by step -> $Proxy0.class file generation process

Having just looked at the results from the end, now let's go back to the beginning of the code:

Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);

Ready to start reading the source code. I will take important code and add comments.

First look Proxy.newInstance():

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    // 复制要代理的接口                                  	
	final Class<?>[] intfs = interfaces.clone();
	// 重点:生成 $Proxy0.class 文件并通过 ClassLoader 加载进来
	Class<?> cl = getProxyClass0(loader, intfs);
	// 对 $Proxy0.class 生成一个实例,就是 `proxyObject`
	final Constructor<?> cons = cl.getConstructor(constructorParams);
	return cons.newInstance(new Object[]{h});
}

Let getProxyClass0's look at the specific implementation: ProxyClassFactoryfactory class:

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
	// 参数为 ClassLoader 和要代理的接口
	Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
	// 1. 验证 ClassLoader 和接口有效性
	for (Class<?> intf : interfaces) {
		// 验证 classLoader 正确性
		Class<?> interfaceClass = Class.forName(intf.getName(), false, loader);
		if (interfaceClass != intf) {
            throw new IllegalArgumentException(
                intf + " is not visible from class loader");
        }
		// 验证传入的接口 class 有效
		if (!interfaceClass.isInterface()) { ... } 
		// 验证接口是否重复
		if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { ... }
	}
	// 2. 创建包名及类名 $Proxy0.class
	proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
	long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    // 3. 创建 class 字节码内容
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    // 4. 基于字节码和类名,生成 Class<?> 对象
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}

Look at the third step to generate class content ProxyGenerator.generateProxyClass:

// 添加 hashCode equals toString 方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 添加委托类的接口实现
for (int i = 0; i < interfaces.length; i++) {
    Method[] methods = interfaces[i].getMethods();
    for (int j = 0; j < methods.length; j++) {
         addProxyMethod(methods[j], interfaces[i]);
    }
}
// 添加构造函数
methods.add(this.generateConstructor());

The content of the class is constructed here: add the necessary functions, implement interfaces, constructors, etc. The following is to write what you saw in $Proxy0.classthe .

ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
dout.writeInt(0xCAFEBABE);
...
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
...
return bout.toByteArray();

At this point, the $Proxy0.classfile , the closed loop is completed, and the explanation is complete! #Dynamic proxy summary

It can be seen from the above explanation that dynamic proxy can proxy for any delegate class at any time, and can InvocationHandler#invokeget runtime information and do some aspect processing.

Behind the dynamic proxy, a proxy class is actually dynamically generated for $Proxy0.classa delegate class. The proxy class will implement the interface of the delegate class, forward the interface call to InvocationHandler#invoke, and finally call the corresponding method of the real delegate class.

The dynamic proxy mechanism isolates the delegate class from the proxy class and improves the expansibility. #Java dynamic proxies and Python decorators

This is an interesting language feature provided by the Java language. In fact, Python also provides a similar feature: decorators, which can achieve similar face-to-face programming ideas. Next time I have time, I will compare the two. Come here first.

Reprinted from: https://juejin.im/entry/5a82fd0f6fb9a0633757368c The article is inappropriate, welcome to correct me, you can also follow my WeChat public account: 好好学java, to obtain high-quality resources.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325390925&siteId=291194637