未曾设想过的JDK动态代理写法

前言

在阅读TomcatJdbc 源码时,发现了一个很四倍秀的JDK 动态代理的写法,我当即就把这个写法分享给了隔壁组可爱的女同事,她在惊叹不已的同时向我问道:这么写有啥用呢?我随即陷入了沉思,气氛一时间竟有点尴尬,感觉相较于传统的使用Proxy#newProxyInstance来创建动态代理对象,这个四倍秀的不怎么常见的写法,大有一种先脱裤子再放屁的感觉,今天就来看下是不是这么回事儿。

正文

在给出四倍秀JDK 动态代理的写法前,我们先回顾一下穿着裤子放屁的通常的JDK动态代理的写法。

java 复制代码
public class MainTest {

    public static void main(String[] args) {
        // 创建被代理类对象
        RealObject realObject = new RealObject();
        // 获取被代理类的类加载器
        ClassLoader classLoader = realObject.getClass()
                .getClassLoader();
        // 获取被代理类实现的接口的Class对象
        Class<?>[] interfaces = realObject.getClass()
                .getInterfaces();
        // 以被代理类对象作为入参创建InvocationHandler
        InvocationHandler invocationHandler
                = new MyInvocationHandler(realObject);
        // 通过调用Proxy的newProxyInstance()方法创建动态代理对象
        Object proxyInstance = Proxy.newProxyInstance(
                classLoader, interfaces, invocationHandler);

        ((TestServiceA) proxyInstance).executeTestA();
        ((TestServiceA) proxyInstance).submitTestA();
        ((TestServiceB) proxyInstance).executeTestB();
        ((TestServiceB) proxyInstance).submitTestB();
    }

}

相关接口,被代理类和InvocationHandler如下所示。

java 复制代码
public interface MyServiceA {

    void executeTestA();
    void submitTestA();

}

public interface MyServiceB {

    void executeTestB();
    void submitTestB();

}

public class MyRealObject implements MyServiceA, MyServiceB {

    @Override
    public void executeTestA() {
        System.out.println("Test A execute.");
    }

    @Override
    public void submitTestA() {
        System.out.println("Test A submit.");
    }

    @Override
    public void executeTestB() {
        System.out.println("Test B execute.");
    }

    @Override
    public void submitTestB() {
        System.out.println("Test B submit.");
    }

}

public class MyInvocationHandler implements InvocationHandler {

    private final Object realObject;

    public MyInvocationHandler(Object realObject) {
        this.realObject = realObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object invokeResult = method.invoke(realObject, args);
        after();
        return invokeResult;
    }

    private void before() {
        System.out.println("Begin to do.");
    }

    private void after() {
        System.out.println("Finish to do.");
    }

}

上述是很标准的使用JDK 动态代理的写法,那么下面再稍微一探Proxy#newProxyInstance方法的实现,看一下在创建动态代理对象的过程中,其为我们做了什么。

java 复制代码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException {
    
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    // 生成代理类的Class对象
    Class<?> cl = getProxyClass0(loader, intfs);

    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 获取代理类的构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 生成代理对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

其实主要步骤就是如下三步。

java 复制代码
// 1. 生成代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);

// 2. 获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);

// 3. 生成代理对象
cons.newInstance(new Object[]{h});

而我口中说的四倍秀写法,其实就是自己完成上述三个步骤,示例如下。

java 复制代码
public class MainTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        // 获取代理类的Class对象且代理类实现了TestServiceA和TestServiceB接口
        Class<?> proxyClass = Proxy.getProxyClass(MyServiceA.class.getClassLoader(),
                MyServiceA.class, MyServiceB.class);
        // 获取代理类的构造器
        Constructor<?> proxyClassConstructor = proxyClass.getConstructor(InvocationHandler.class);
        // 创建一个被代理类对象
        MyRealObject myRealObject = new MyRealObject();
        // 基于被代理类对象创建InvocationHandler
        InvocationHandler invocationHandler = new MyInvocationHandler(myRealObject);
        // 创建动态代理对象
        Object proxyInstance = proxyClassConstructor.newInstance(invocationHandler);

        ((MyServiceA) proxyInstance).executeTestA();
        ((MyServiceA) proxyInstance).submitTestA();
        ((MyServiceB) proxyInstance).executeTestB();
        ((MyServiceB) proxyInstance).submitTestB();
    }

}

乍一看还真的挺唬人的,但是无论是哪种写法,底层原理都是一样的,两者没有啥本质区别。

总结

本文介绍的这种四倍秀JDK 动态代理写法,和通常我们看见的JDK动态代理写法本质是一样的,但是了解这种写法,能够在阅读一些源码时能反映过来这里使用了动态代理。

相关推荐
舒一笑1 小时前
PandaCoder:致敬MyBatis Log Plugin,但我们做得更极致!
后端·程序员·intellij idea
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 基于Android的校园快递互助APP为例,包含答辩的问题和答案
java·eclipse
damo012 小时前
stripe 支付对接
java·stripe
麦麦鸡腿堡3 小时前
Java的单例设计模式-饿汉式
java·开发语言·设计模式
简单点了3 小时前
go前后端项目的启动 、打包和部署
开发语言·后端·golang
假客套3 小时前
Request method ‘POST‘ not supported,问题分析和解决
java
傻童:CPU3 小时前
C语言需要掌握的基础知识点之前缀和
java·c语言·算法
爱吃山竹的大肚肚3 小时前
@Valid校验 -(Spring 默认不支持直接校验 List<@Valid Entity>,需用包装类或手动校验。)
java·开发语言
雨夜之寂4 小时前
mcp java实战 第一章-第一节-MCP协议简介.md
java·后端
摇滚侠4 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 核心语法 笔记39
spring boot·笔记·后端·thymeleaf