未曾设想过的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 分钟前
2025最新 SpringCloud 教程,Nacos-总结,笔记19
java·笔记·spring cloud
aiopencode4 分钟前
iOS 应用上架的工程实践复盘,从构建交付到审核通过的全流程拆解
后端
在逃热干面5 分钟前
(笔记)获取终端输出保存到文件
java·笔记·spring
爱笑的眼睛116 分钟前
深入理解MongoDB PyMongo API:从基础到高级实战
java·人工智能·python·ai
笃行客从不躺平16 分钟前
遇到大SQL怎么处理
java·开发语言·数据库·sql
q***876023 分钟前
Spring Boot 整合 Keycloak
java·spring boot·后端
Billow_lamb24 分钟前
Spring Boot2.x.x全局拦截器
java·spring boot·后端
上不如老下不如小34 分钟前
2025年第七届全国高校计算机能力挑战赛初赛 Java组 编程题汇总
java·计算机能力挑战赛
泉城老铁1 小时前
Springboot对接mqtt
java·spring boot·后端
源码_V_saaskw1 小时前
JAVA国际版同城跑腿源码快递代取帮买帮送同城服务源码支持Android+IOS+H5
android·java·ios·微信小程序