未曾设想过的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 小时前
「Java EE开发指南」如何用MyEclipse创建一个WEB项目?(三)
java·ide·java-ee·myeclipse
ai小鬼头1 小时前
Ollama+OpenWeb最新版0.42+0.3.35一键安装教程,轻松搞定AI模型部署
后端·架构·github
idolyXyz1 小时前
[java: Cleaner]-一文述之
java
一碗谦谦粉2 小时前
Maven 依赖调解的两大原则
java·maven
萧曵 丶2 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
netyeaxi2 小时前
Java:使用spring-boot + mybatis如何打印SQL日志?
java·spring·mybatis
收破烂的小熊猫~2 小时前
《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
java·开发语言·设计模式
猴哥源码2 小时前
基于Java+SpringBoot的动物领养平台
java·spring boot
老任与码3 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
小兵张健3 小时前
武汉拿下 23k offer 经历
java·面试·ai编程