likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 11

前言

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

正文

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

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如下所示。

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方法的实现,看一下在创建动态代理对象的过程中,其为我们做了什么。

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

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

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

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

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

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

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动态代理写法本质是一样的,但是了解这种写法,能够在阅读一些源码时能反映过来这里使用了动态代理。

转载自:https://juejin.cn/post/7313753156520607755
评论
请登录