JAVA基础-从代理模式再出发!Proxy.newProxyInstance的秘密

JAVA讲到代理模式就绕不开newProxyInstance方法,今天简单说一下。

Proxy静态方法newProxyInstance

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

一共有三个参数:

  • loader:用于指定用哪个类加载器,去加载生成的代理类;
  • interfaces:指定这个代理类能够代理目标对象的哪些方法和接口;
  • h:重要!!用来指定生成的代理对象在方法被调用时如何进行处理;

我们走读下里面重要的逻辑:

getProxyClass0

  1. 生成一个代理class
  2. 如果proxy class 之前有缓存,就不重复创建,不然走 ProxyClassFactory 创建
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
.......

        // 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 类的内容,可以在测试类中设置一个Propertie:

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

看生成的代理类之前先简单看看 InvocationHandler 唯一的方法注释。

public class InvocationHandler {
    /**
     * @param proxy:代理实例,即当前代理对象本身。当 invoke 方法被调用时,这个参数就是代理对象的一个引用。
     * @param method:被调用的方法对象。它表示代理实例上被调用的方法,在 invoke 方法中可以需要调用 method.invoke 以触发方法。
     * @param args:调用方法时传递的参数数组。若没有参数,则该数组为 null。
     */
    public Object invoke(Object proxy, Method method, Object[] args);
}

代理类 $Proxy0.class

最后生成的代理类总结:

  1. $Proxy0 实现了我们给的接口 Sourceable 并且继承了 Proxy ,构造类要传入我们使用newProxyInstance 时用的 InvocationHandler 变量。
  2. static静态初始化代码块,把 Sourceable 接口的各个方法 Method 初始化完成(包括equals,toString 等方法)。
  3. **真正执行方法的时候实际上是执行 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框架就是通过拼接字节码来实现非接口类的代理。

newProxyInstance 方法是 Java 中的一个静态方法,它可以用来创建一个动态代理对象。动态代理是一种非常强大的技术,它可以在运行时生成一个代理对象,这个代理对象可以拦截被代理对象的所有方法调用,并进行自定义的处理。 该方法的签名如下: ``` public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ``` 其中,loader 参数是一个 ClassLoader 对象,用于加载动态生成的代理类;interfaces 参数是一个 Class 对象数组,表示被代理类实现的接口;h 参数是一个 InvocationHandler 对象,用于处理代理对象的方法调用。 使用 newProxyInstance 方法创建动态代理对象的步骤如下: 1. 创建一个 InvocationHandler 对象,实现 invoke 方法来处理代理对象的方法调用逻辑; 2. 调用 newProxyInstance 方法创建代理对象,并将 InvocationHandler 对象作为参数传入; 3. 使用代理对象调用方法。 示例代码: ``` public interface Hello { void sayHello(); } public class HelloImpl implements Hello { @Override public void sayHello() { System.out.println("Hello, world!"); } } public class HelloInvocationHandler implements InvocationHandler { private Object target; public HelloInvocationHandler(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) { Hello hello = new HelloImpl(); InvocationHandler handler = new HelloInvocationHandler(hello); Hello proxyHello = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler); proxyHello.sayHello(); } } ``` 输出结果: ``` Before method sayHello Hello, world! After method sayHello ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值