从代理模式再出发!Proxy.newProxyInstance的秘密

上一节简单介绍了一下ClassLoader,我们知道,在我们用Proxy.newProxyInstance实现动态代理的时候,有三个参数,第一个便是classloader,这一篇文章就是简单看一下Proxy.newProxyInstance是如何实现动态代理的,因为我们后面可以以此为参考,实现自己的动态代理工具。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

Proxy静态方法newProxyInstance

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    // 检查 h 不为空,否则抛异常
        Objects.requireNonNull(h);
    // 对传入的接口做安全检查
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 实现动态代理的核心方法,动态代理的思路便是生成一个新类,刚刚getProxyClass0便成为了生成新类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 根据生成的class通过反射获取构造函数对象并生成代理类实例
         */
        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);
        }
    }

proxyClassCache

getProxyClass0方法里其实直接取proxyClassCache缓存,proxyClassCache是一个代理类的缓存变量,如果这个缓存里有这个代理类,就直接返回代理类,如果没有,就会通过ProxyClassFactory创建代理对象。

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

ProxyClassFactory

ProxyClassFactory是Proxy里的一个内部类,用来生成代理类,apply()方法 最后调用ProxyGenerator.generateProxyClass()方法来完成生成字节码的操作,这里不讲它是如何生成的,我们只要知道它可以返回一个全新的class类,如果想看这个class类的内容,可以在测试类中这么写:

public class Test {
    public static void main(String[] args) {
//加入这一段可以在磁盘中生成 代理类,让我们看到代理类的真面目        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        Sourceable source = new Source();
        Sourceable test = (Sourceable) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{Sourceable.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("test");
                if ("method".equals(method.getName())) {
                    return method.invoke(source, null);
                }
                return null;
            }
        });
        test.method();
    }

代理类 $Proxy0.class

可以看到$Proxy0 实现了我们给的接口Sourceable 并且继承了Proxy,构造类要传入我们使用newProxyInstance时用的InvocationHandler变量,并且在static静态初始化中把Sourceable接口的各个方法Method初始化完成(包括equals,toString等方法),并且真正执行方法的时候实际上是执行InvocationHandler对象的invoke()方法(super.h就是保存在父类Proxy中的InvocationHandler对象),各个方法的区别就是Method对象不同而已,所以真正方法的执行是在InvocationHandler的invoke()方法中完成的。 这也说明了代理逻辑 和 动态代理本身是代码分离的,程序员只需要关注好自己的代理逻辑就行,动态代理本身就交给jdk本身去处理。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import cn.xxywithpq.proxy.jdkproxy.bean.Sourceable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Sourceable {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    private static Method m4;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void method() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void method1(int var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method1", Integer.TYPE);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结

在jdk动态代理中,美中不足就是整个设计都是针对接口做的代理,如果是普通的类,我们无法通过这个方式代理对象(通过生成的代理类也知道没有接口是不行的),但是我们知道 通过拼接字节码生成新的类 自由度是十分大的,这也就启示我们 设计不管是针对接口类还是普通类的代理类 是完全可行的,比如cglib框架就是通过拼接字节码来实现非接口类的代理。 后面会介绍如何 实现这种操作,我在自己的Simplify-Core项目中已经尝试通过asm框架(字节码读写框架)实现代理操作。有兴趣的可以看看。

项目地址:

https://github.com/lovejj1994/Simplify-Core
`Proxy.newProxyInstance` 是 Java 中的一个方法,用于创建一个动态代理对象。它接受三个参数:一个类加载器,一个接口数组和一个 `InvocationHandler` 对象。动态代理对象可以在运行时动态地创建一个实现指定接口的代理类,并将方法调用委托给 `InvocationHandler` 来处理。 使用 `Proxy.newProxyInstance` 可以实现一些横切关注点(cross-cutting concerns)的功能,比如日志记录、性能监控、事务管理等。通过动态代理,可以在不修改原始类代码的情况下,为其增加额外的行为。 以下是一个简单的示例代码,演示如何使用 `Proxy.newProxyInstance` 创建一个动态代理对象: ```java public interface Foo { void bar(); } public class RealFoo implements Foo { @Override public void bar() { System.out.println("RealFoo: executing bar()"); } } public class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method: " + method.getName()); Object result = method.invoke(target, args); System.out.println("After method: " + method.getName()); return result; } } public class Main { public static void main(String[] args) { Foo realFoo = new RealFoo(); InvocationHandler handler = new LoggingHandler(realFoo); Foo proxy = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(), new Class[]{Foo.class}, handler); proxy.bar(); // 通过代理对象调用方法 } } ``` 运行上述代码,将会在控制台输出以下内容: ``` Before method: bar RealFoo: executing bar() After method: bar ``` 这说明 `LoggingHandler` 在方法调用前后添加了额外的日志输出行为。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值