《设计模式》8.JDK动态代理(结构型)

JDK动态代理基于接口实现,代理类必须实现接口

public class ProxyTarget implements ITarget {
    public void doSomething() {
        System.out.println("do something");
    }
}

通过实现 java.lang.reflect.InvocationHandler 接口创建动态代理处理句柄:

/**
 * proxy: 动态生成代理类的实例对象
 * method:目标类实现接口的接口方法(ITarget::doSomething)
 * args: 调用目标类方法的实际调用参数
 **/
public Object invoke(Object proxy, Method method, Object[] args)

InvocationHandler::invoke 中:
正确写法:method.invoke(target, args);
错误写法:method.invoke(proxy, args); --实际调用中 Proxy.newProxyInstance 返回的就是动态代理类的实例对象,这里再使用 proxy 将会造成循环调用导致堆栈溢出,抛出 StackOverflowError。

JdkDynamicProxyHandler

public class JdkDynamicProxyHandler<T> implements InvocationHandler {
    private T target;
    public JdkDynamicProxyHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before " + method.getName() + " execution...");
        Object result = method.invoke(target, args);
        System.out.println("after " + method.getName() + " execution...");
        return result;
    }
}

测试类:

public class JdkDynamicProxyTester {
    public static void main(String args[]) throws Exception {
        //通过设置系统属性:sun.misc.ProxyGenerator.saveGeneratedFiles=true 将生成 Class 对象写入项目根目录:
        //1.接口修饰符为 public:com/sun/proxy/$Proxy{num}.class
        //2.接口修饰符不为public:接口所在包名同样的目录结构,如接口为:com.designpatterns.proxy.ITarget.java,Class文件将生成在:com/designpatterns/proxy/$Proxy{num}.class
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        
        ProxyTarget target = new ProxyTarget();
        Class[] interfaces = new Class[] { ITarget.class };
        InvocationHandler handler = new JdkDynamicProxyHandler(target);
        ITarget proxy = (ITarget) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), interfaces, handler);
        proxy.doSomething();
    }
}

结果输出:

before doSomething execution...
do something
after doSomething execution...

java.lang.reflect.Proxy::newProxyInstance

从当前代理类缓存中查找代理类是否加载过,判断Class是否加载过,如果已加载直接从 Proxy 的缓存 proxyClassCache 中直接获取返回,如果没有加载过,则通过 ClassLoader 加载,并更新 Proxy 缓存。

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

java.lang.reflect.Proxy::getProxyClass0

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

WeakCache::get

根据 key(ClassLoader)和 parameter(interfaces[])生成代理类的 Class 文件

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

如果缓存中存在则直接返回

            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }

java.lang.reflect.Proxy$ProxyClassFactory::apply

  1. 生成代理类名
    1.接口修饰符为 public:com/sun/proxy/$Proxy{num}.class
    2.接口修饰符不为public:接口所在包名同样的目录结构,如接口为:com.designpatterns.proxy.ITarget.java,Class文件将生成在:com/designpatterns/proxy/$Proxy{num}.class
  2. 通过代理类名 proxyName,接口 interfaces,生成代理类字节码 proxyClassFile(根据系统属性:sun.misc.ProxyGenerator.saveGeneratedFiles 的值决定是否生成 Class 文件)
  3. 通过字节码 proxyClassFile 调用 native 方法 defineClass0 生成代理类 Class 对象
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }

sun.misc.ProxyGenerator::generateProxyClass

根据代理类名 proxyName,接口 interfaces,生成代理类字节码 classFile,通过字节码生成 Class 文件

    /**
     * Generate a proxy class given a name and a list of proxy interfaces.
     *
     * @param name        the class name of the proxy class
     * @param interfaces  proxy interfaces
     * @param accessFlags access flags of the proxy class
    */
    public static byte[] generateProxyClass(final String name,
                                            Class<?>[] interfaces,
                                            int accessFlags)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();

        if (saveGeneratedFiles) {
            java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf('.');
                        Path path;
                        if (i > 0) {
                            Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                        } else {
                            path = Paths.get(name + ".class");
                        }
                        Files.write(path, classFile);
                        return null;
                    } catch (IOException e) {
                        throw new InternalError(
                            "I/O exception saving generated file: " + e);
                    }
                }
            });
        }

        return classFile;
    }

通过代理的Class对象反射调用构造函数生成代理对象实例

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

测试生成代理类 Class 文件:D:/$Proxy0.class

public class JdkDynamicProxyTester {
    public static void main(String args[]) throws Exception {
        byte[] bytes = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy0", interfaces);
        new FileOutputStream(new File("D:/$Proxy0.class")).write(bytes);
    }
}

通过 jad 反编译生成的代理类 Class 文件,当调用 doSomething 时执行:super.h.invoke(this, m3, null); 其中:

  • h 为 自定义的 JdkDynamicProxyHandler 对象
  • this 为生成的代理类对象
  • m3 为 ITarget::doSomething 方法
  • null 为方法入参数

所以JDK动态代理实际上是通过反射接口方法实现,这就解释了为什么JDK动态代理必须要实现接口。在生成动态代理类时,还自动生成了 equals,hashCode,toString 反射调用方法。

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 

package com.sun.proxy;

import com.designpatterns.proxy.ITarget;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements ITarget
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void doSomething()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("com.designpatterns.proxy.ITarget").getMethod("doSomething", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值