JDK dynamic proxy

To introduce dynamic proxies, let’s look at a case column!

Guangzhou, Guangdong province, 9 am, a pretty boy wearing flip-flops, carrying a bird cage into the morning tea shop. Yes, this is a typical charter company in Guangzhou! He owns several buildings, only collects the rent for a living, has no job, this person is really boring!

Here comes a question: rent collection is not a job? Well, in fact, the real charterers do not collect rent themselves, they are entrusted to the intermediary to do. Why is that? There are security, privacy and so on. Think about, if charter public oneself collect rent, rent a guest a lot of at present, other charter public not happy, flat look for a person to go making trouble, ask not to rent only for instance, waste charter public time. It’s not just that…

Simple to use

Ok, the rental agent comes out, the tenant looks at the house, negotiates the price, signs the contract, delivers the key and so on to let the agent (agent) do, the landlord just sits and waits to collect the money.

Use output statements in code instead of real business!

First, we define a host interface and a host implementation class. (Why do you need interfaces? The agent class is derived from the interface, and the host class should not be exposed.

public interface ILandlordService {
    void deliver();
}

Copy the code
public class LandlordServiceImpl implements ILandlordService { public void deliver() { try { System.out.println(" notify landlord of success, landlord of money "); } catch (Exception e) { e.printStackTrace(); System.out.println(" error, terminate "); }}}Copy the code

Then create a class that implement the Java. Lang. Reflect. InvocationHandler interface, the class used to increase the original method. (Note package, misdirected)

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; Public class TransactionInvocationHandler implements InvocationHandler {/ / objects that need to be agent (the landlord) private Object target; public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } /* Implement the interface to override the method proxy: proxy Object method: call the method of the host interface args: call the method of the host interface parameters */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object retVal = null; Try {system.out.println (" Pre-delivery conversation with the mediation "); RetVal = method.invoke(target, args); System.out.println(" rent success "); System.out.println(" give the key "); } catch (Exception e) { e.printStackTrace(); System.out.println(" exception occurred, delivery terminated "); } return retVal; }}Copy the code

Finally write the test class (tenant)

import org.junit.Test; import java.lang.reflect.Proxy; Public class TenantTest {@test public void Test () {// These two create objects and set values should be configured in Spring, // Create a host object ILandlordService landlordService = new LandlordServiceImpl(); / / create the enhancement class object TransactionInvocationHandler TransactionInvocationHandler = new TransactionInvocationHandler (); / / set the landlord objects to enhance transactionInvocationHandler used in the class object. The setTarget (landlordService); /* The newProxyInstance method takes three arguments: ClassLoader () Class<? >[] interfaces: InvocationHandler h: */ ILandlordService proxy = (ILandlordService) proxy.newProxyInstance (this.getClass().getClassLoader(), / / here landlordService objects created in the test class temporarily, / / use the spring after injected into TransactionInvocationHandler (enhancement) field (target), / / directly to enhance class, This test class (tenant) don't know the implementation class (the landlord) landlordService. GetClass () getInterfaces (), transactionInvocationHandler); // Call the proxy object's Deliver (), which executes the invoke method of the enhanced class. Method is the method proxy.Deliver () called by the proxy object. }}Copy the code
Execution result: the landlord was informed of the success of renting by talking with the agent before delivery, and the landlord gave the key to the success of renting by collecting moneyCopy the code

How do you create a proxy class? How did you call the invoke method of the enhanced class? Then take a look at the principle! Suggest screenshots, code a bit much!

The principle of

To test the newProxyInstance method in your class, click

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
    this.getClass().getClassLoader(),
    landlordService.getClass().getInterfaces(),
    transactionInvocationHandler
);

Copy the code

Go to the newProxyInstance method of the Proxy class and save unnecessary code

@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) {/* * Look up or generate the designated Proxy class and its constructor /* Get the Constructor of the generated class. > cons = getProxyConstructor(caller, loader, interfaces); return newProxyInstance(caller, cons, h); }Copy the code

Go to the newProxyInstance method, save unnecessary code, save try-catch

private static Object newProxyInstance(Class<? > caller, Constructor<? < div style = "text-align: center;" /* Reflection calls the constructor to create the Object of this class cons: proxy class constructor h: enhancement class */ return cons.newinstance (new Object[]{h}); }Copy the code

Get the proxy class bytecode file using the following code

ProxyGenerator is no longer public in JDK 11. The source code here is JKD 11

import sun.misc.ProxyGenerator; import java.io.FileOutputStream; Public class DynamicProxyClassGenerator {public static void main (String [] args) throws the Exception {/ / implementation class class object generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy"); } public static void generateClassFile(Class<? > targetClass, String proxyName) throws Exception {// Based on the class information and provided proxy class name, Generate bytecode byte [] classFile = ProxyGenerator. GenerateProxyClass (proxyName, targetClass getInterfaces ()); String path = targetClass.getResource(".").getPath(); System.out.println(path); FileOutputStream out = null; Out = new FileOutputStream(path + proxyName + ".class"); out.write(classFile); out.close(); }}Copy the code

The class file generated by the proxy class looks like this, and the Deliver () method is derived from the class we defined, leaving out some method content from the Object class

Call the parameter constructor to create the object

import com.hao.service.ILandlordService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class LandlordServiceProxy extends Proxy implements ILandlordService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; Public LandlordServiceProxy(InvocationHandler VAR1) throws {// Call the superclass (Proxy class) constructor super(var1); } public final boolean equals(Object var1) throws { } public final String toString() throws { } public final void deliver() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code
protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); This. h = h; // Set the enhancement class to the proxy field. }Copy the code
protected InvocationHandler h;

Copy the code

Finally proxy.newProxyInstance () returns the Proxy class (mediation)

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
);
proxy.deliver();

Copy the code

We then call the Deliver () method, which goes into the proxy class’s Deliver () method

Public final void Deliver () throws {try {// Invoke the h field (enhanced class) of the parent class. M3 obtains super.hvoke (this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); }}Copy the code

The m3 in static {}

static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); / / m3 = Class deliver method is obtained by reflection class.forname (" com. Hao. Service. ILandlordService "). The getMethod (" deliver "); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}Copy the code

The last

As you can see, calling the proxy class ends up calling the invoke method of the enhanced class. Thank you for reading here, the article has any shortcomings please correct, feel the article is helpful to you remember to give me a thumbs up, every day will share Java related technical articles or industry information, welcome to pay attention to and forward the article!