Two years of java development! Even if you don’t know how to use JDK dynamic proxy, you have no hope of raising your salary!

JDK dynamic proxy

In order to elicit dynamic agents, let's take a look at a case!

Guangzhou, Guangdong, at 9:00 in the morning, a pretty boy walked into the morning tea shop wearing flip flops and carrying a bird cage. Yes, this is a typical charter company in Guangzhou! A few buildings under his name, only renting for a living, no work , this person is really boring!

Here comes the question: Does rent collection count as work? Well, in fact, the real charterers don't collect rent on their own , they are entrusted to intermediaries to do it. why? This can be said to involve security, privacy and so on. Think about it, if the charterer collects the rent by himself, there are many tenants at the moment, and other charterers are upset. They simply ask someone to make trouble, such as just asking whether to rent, and wasting time for the charterer. Of course not only that...

Simple to use

Okay, the renting agent will come out, and the tenant will look at the house, negotiate the price, sign the contract, deliver the keys, etc., and let the agent (agent) do it, and the landlord will just wait for the money.

Two years of java development!  Even if you don’t know how to use JDK dynamic proxy, you have no hope of raising your salary!

Use output statements in the code instead of real business!

First, we define a landlord interface and a landlord implementation class . (Why do you need an interface? The proxy class is obtained based on the interface, and the landlord class should not be exposed)

public interface ILandlordService {
    void deliver();
}
public class LandlordServiceImpl implements ILandlordService {
    public void deliver() {
        try {
            System.out.println("告知房东出租成功,房东收钱");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止");
        }
    }
}

Next, create a class to implement the java.lang.reflect.InvocationHandler interface, which is used to enhance the original method. (Pay attention to the package, it is misguided)

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

public class TransactionInvocationHandler implements InvocationHandler {
    // 需要被代理的对象(房东)
    private Object target;

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /*
        实现接口需要重写的方法
        proxy:代理对象
        method:房东接口中被调用的方法
        args:房东接口中被调用的方法需要的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retVal = null;
        try {
            System.out.println("交付前与中介的交谈");
            // 房东收钱
            retVal = method.invoke(target, args);
            System.out.println("租房成功");
            System.out.println("给钥匙");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("出现异常,终止交付");
        }
        return retVal;
    }
}

Finally write the test class (tenant)

import org.junit.Test;
import java.lang.reflect.Proxy;

public class TenantTest {

    @Test
    public void test() {

        //这两个创建对象和设置值应在spring中配置,让spring创建、设值
        // 创建一个房东对象
        ILandlordService landlordService = new LandlordServiceImpl();
        // 创建增强类对象
        TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler();
        // 把房东对象设置到增强类对象中使用
        transactionInvocationHandler.setTarget(landlordService);

        /*
             使用强转得到房东类代理对象(中介)
             newProxyInstance方法需要三个参数
             ClassLoader loader:类加载器,直接使用本类的类加载器(系统类加载器)
             Class<?>[] interfaces:接口数组
             InvocationHandler h:增强类
         */
        ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                // 这里landlordService对象暂时在测试类中创建,
                // 使用spring后,注入到TransactionInvocationHandler(增强类)的字段(target)中,
                // 直接到增强类中获取,这样测试类(租客)就不知道实现类了(房东)
                landlordService.getClass().getInterfaces(),
                transactionInvocationHandler
        );
        // 调用代理对象的deliver(),会执行增强类的invoke方法,参数method为代理对象调用的方法
        proxy.deliver();
    }
}
执行结果:
交付前与中介的交谈
告知房东出租成功,房东收钱
租房成功
给钥匙

At this time, many people are puzzled, how to create a proxy class? How can you call the invoke method of the enhanced class? Then let's look at the principle! It is recommended to look at the screenshot, the code is a bit too much!

principle

Test the newProxyInstance method in the class, click into it

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

Go to the newProxyInstance method of the proxy class to 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.
    * 查找或生成 指定的代理类和它的构造函数
    */
    /*
        获取生成类的构造器
        loader:类加载器
        interfaces:接口数组
    */
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

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

private static Object newProxyInstance(Class<?> caller,
                                           Constructor<?> cons,
                                           InvocationHandler h) {
    // caller有关安全的,这里忽略
    checkNewProxyPermission(caller, cons.getDeclaringClass());
     /* 
        反射调用构造器创建这个类的对象
        cons:代理类构造器
        h:增强类
    */
    return cons.newInstance(new Object[]{h});
}

Use the following code to get the proxy class bytecode file

ProxyGenerator is no longer public in JDK 11. The source code I used here is JKD 11 to
get the proxy class bytecode using JDK 8.

import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;

public class DynamicProxyClassGenerator {
    public static void main(String[] args) throws Exception {
        // 实现类的class对象
        generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy");
    }

    public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception {
        // 根据类信息和提供的代理类名称,生成字节码
        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();
    }
}

The class file generated by the proxy class is like this, the deliver() method is based on the class we defined, omitting the method content obtained by the Object class

Call the parameterized constructor to create an 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  {
        // 调用父类(proxy类)构造器
        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());
        }
    }
}
protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    // 把增强类设置到proxy类的字段上
    this.h = h;
}
protected InvocationHandler h;

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

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

Then we call the deliver() method and enter the deliver() method of the proxy class

public final void deliver() throws  {
    try {
        // 调用父类的h字段(增强类)中的invoke方法,m3在本类static{}中获取
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

m3 is 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");
        // 通过反射得到deliver方法
        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());
    }
}

At last

It can be seen that calling the proxy class will eventually call the invoke method of the enhanced class. Thank you for seeing here. Please correct me if there are any shortcomings in the article. If you think the article is helpful to you, remember to give me a thumbs up. You will share java-related technical articles or industry information every day. Welcome everyone to follow and forward the article!

Guess you like

Origin blog.51cto.com/14966465/2542738