关于后端:java开发两年连JDK动态代理使用及原理都不知道你加薪无望

JDK动静代理

为了引出动静代理,咱们看看一个案列!

广东广州,早上9:00,一位靓仔衣着人字拖、提着鸟笼,走进了早茶店。没错,这就是广州典型的包租公!名下几栋楼,只收租为生,没工作,这人身真是无趣至极!

这里就得出一个问题:收租不算工作?好吧,其实正真的包租公不会本人去收租,都是委托给中介去做。为什么呢?这其中能够说牵扯到平安、隐衷等等。想一下,如果包租公本人收租,当下租客很多,其余包租公就不爽了,罗唆找人去捣鬼,比方只问不租,节约包租公工夫。当然不仅仅是这样…

简略应用

好的,租房中介就进去了,租客看房、谈价、签合同、交付钥匙等等都让中介(代理)去做,房东就坐等收钱就行了。

代码中应用输入语句代替真正的业务!

首先,咱们定义一个房东接口,和一个房东实现类。(为什么须要接口?前面代理类是依据接口失去的,而且房东类不应该裸露进来的)

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("出现异常,终止");
        }
    }
}

接下来创立一个类,实现java.lang.reflect.InvocationHandler接口,该类用来对原来的办法加强。(留神包,被导错)

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;
    }
}

最初写测试类(租客)

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();
    }
}
执行后果:
交付前与中介的交谈
告知房东出租胜利,房东收钱
租房胜利
给钥匙

此时很多人就纳闷了,怎么创立代理类的?又怎么会调用加强类的invoke办法的?那就接下去看看原理吧!倡议截图看,代码有点多!

原理

测试类中的newProxyInstance办法,点进去

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

到proxy类的newProxyInstance办法,省去不必要的代码

@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);
}

往newProxyInstance办法中走,省去不必要的代码,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});
}

应用以下代码获取代理类字节码文件

ProxyGenerator 在 JDK 11 曾经不是私有的了,我这里的源码是JKD 11
上面获取代理类字节码用的是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();
    }
}

代理类生成的class文件是这样的,deliver()办法是依据咱们定义的类得来的,省去几个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;

最终Proxy.newProxyInstance()返回的是代理类(中介)

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

而后咱们调用deliver()办法,进入的是代理类的deliver()办法

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在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());
    }
}

最初

能够看出,调用代理类最终会调用加强类的invoke办法。感激你看到这里,文章有什么有余还请斧正,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理