JDK动态代理


JDK 动态代理只能代理实现了 接口的类,说到 JDK 动态代理,就不得不说一下 java.lang.reflect.InvocationHandlerjava.lang.reflect.Proxy

InvocationHandler 接口

在看源码时,最重要的是先看下官方给的注释,注释会给我们看懂源码提供很大的帮助。

/**
 * {@code InvocationHandler} is the interface implemented by
 * the <i>invocation handler</i> of a proxy instance.
 *
 * <p>Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method
 * invocation is encoded and dispatched to the {@code invoke}
 * method of its invocation handler.
 * /

注释中有三个关键信息。

  • InvocationHandler 是由代理实例调用程序实现的接口
  • 每个 代理实例 都会关联一个InvocationHandler 接口。
  • 调用 代理实例 中的方法时,最终会调用到 InvocationHandler 中的 invoke() 方法

这里请记住这三个关键信息,后面我会结合代码跟大家解释。

Proxy

同样的先看下官方注释

/**
 * {@code Proxy} provides static methods for creating dynamic proxy
 * classes and instances, and it is also the superclass of all
 * dynamic proxy classes created by those methods.
 * /

中文翻译: Proxy 提供了用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

我们可以提取到两个关键信息

  • Proxy可以通过它的静态方法创建动态代理类(其实这个方法是newProxyInstance()
  • Proxy是由这些方法创建的所有动态代理类超类

案例分析

我们为什么需要代理呢,有了代理,我们能做的事,代理也能帮我们做。换句话说,代理类有着与我们相同的功能,也就是相同的方法

在 Java 中,接口是功能的抽象。既然代理类有着我们相同的功能,那我们先将这个功能定义出来。也就是先把接口定义出来。

我们这里以找中介买房子为例。

定义一个接口,该接口中的功能为买房子

/**
 * 买房子功能的接口
 */
public interface BuyHouse {
    /**
     * 买房子
     *
     * @param money 定金
     * @return 
     */
    String buy(Integer money);
}

定义一个目标类,买房子的人

/**
 * 目标类 | 被代理的类
 */
public class People implements BuyHouse{
    @Override
    public String buy(Integer money) {
        return "buy house";
    }
}

现在买房的人和功能有了,我们需要去找中介了(中介指的就是代理类)。上面我们已经说了,Proxy就是用于创建代理类的。但是仅仅有Proxy还不行,Proxy确实有创建代理类的功能,它就好比一个机器,你需要一个人或一个程序去使用它呀,Proxy才能为我们创建代理类。

还记得 InvocationHandler 中的那三个关键信息吗?其中有一个是

InvocationHandler 是由代理实例调用程序实现的接口

所以我们要去创建这个调用程序并实现 InvocationHandler 接口,通过它来调用Proxy.newProxyInstance()方法去创建代理类

创建调用程序

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * 代理实例的调用程序
 * 
 * 通过调用 {@link Proxy} 中的静态方法 newProxyInstance() 创建代理类
 *
 * @see Proxy
 * @see InvocationHandler
 */
public class PeopleProxy implements InvocationHandler {
    /**
     * 目标对象 | 被代理的对象 | 想找中介买房子的人
     */
    private Object target;

    public PeopleProxy(Object target) {
        this.target = target;
    }

    /**
     * 通过调用 Proxy.newProxyInstance 方法创建代理类
     *
     * @return 代理类的实例
     */
    public Object getProxy() {
        // this.getClass().getClassLoader():获取一个 ClassLoader
        // target.getClass().getInterfaces():获取目标类的所有接口
        // this:InvocationHandler 的实例对象
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }

    /**
     * 调用代理类中的方法时,最终会调用到该方法
     *
     * @param proxy  代理类 com.sun.proxy.$Proxy0
     * @param method 目标类中的方法
     * @param args   目标类中的方法参数
     * @return 目标类中的方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	// 打印代理实例的 Class
        System.out.println("proxy = " + proxy.getClass());
        // 打印方法名
        System.out.println("method = " + method.getName());
        // 打印出所有参数
        System.out.println("args = " + Arrays.toString(args));
        // 调用目标类中的方法
        Object res= method.invoke(target, args);
      	// 返回方法返回值
        return res;
    }
}

PeopleProxy是我们的调用程序,我们通过构造器的方式将我们目标类(被代理的类)传进来,然后调用Proxy.newProxyInstance 为目标类创建代理对象。

Proxy.newInstance(ClassLoader, Class<?>[], InvocationHandler )

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

这个方法有三个参数

  • ClassLoader:类加载器,用于创建 Class 对象
  • interfaces:接口的Class数组
  • InvocationHandler:含有 invoke() 方法

为什么创建一个代理类需要这三个参数呢?

ClassLoader 不必多说。代理类具有目标类(被代理的类)相同的功能 ,而接口是功能的抽象,所以我们传入目标类实现的所有接口也就能将它所有的功能赋予代理类了(在本例中,指的是将买房子的功能赋予中介)。而调用 代理实例 中的方法时,最终会调用到 InvocationHandler 中的 invoke() 方法 ,所以我们也要将它传给代理类。

测试类


/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        // 创建目标对象,他想买一栋房子
        People people = new People();

        // 本人不想去找房子,想找个中介帮我买
        // 通过调用程序去创建代理类
        PeopleProxy peopleProxy = new PeopleProxy(people);
        BuyHouse proxy = (BuyHouse) peopleProxy.getProxy();

        // 中介买房子
        String res = proxy.buy(1000);
        System.out.println(res);
    }
}

执行结果

proxy = class com.sun.proxy.$Proxy0
method = buy
args = [1000]
buy house

通过执行结果我们可以看出,调用 代理实例 中的方法时,最终会调用到 InvocationHandler 中的 invoke() 方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 打印代理实例的 Class
    System.out.println("proxy = " + proxy.getClass());
    // 打印方法名
    System.out.println("method = " + method.getName());
    // 打印出所有参数
    System.out.println("args = " + Arrays.toString(args));
    // 调用目标类中的方法
    Object res= method.invoke(target, args);
    // 返回方法返回值
    return res;
}

代理类

class com.sun.proxy.$Proxy0 就是我们生成的代理类。有个地方不知道大家注意到没有,在上面有这么一段代码。

BuyHouse proxy = (BuyHouse) peopleProxy.getProxy();

这里将生成的代理对象转换成了接口,那么为什么能将代理对象转换成接口呢。我们来看一下这个代理类究竟是什么。我们这里来生成一下这个代理类。

public class Test {
    public static void main(String[] args) {
        People people = new People();
        PeopleProxy peopleProxy = new PeopleProxy(people);
        BuyHouse proxy = (BuyHouse) peopleProxy.getProxy();

		// 生成代理类的 .class 文件,文件生成在项目的 src 目录下
		// 通过前面的打印结果,我们已经知道这个代理类为 com.sun.proxy.$Proxy0
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", proxy.getClass().getInterfaces());
        try (FileOutputStream fos = new FileOutputStream(new File("src/$Proxy0.class"))) {
            fos.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这里我通过 ProxyGenerator.generateProxyClass 方法生成了 .class 文件并反编译(通过 idea 或 反编译软件)

import com.jayce.jdkproxy.BuyHouse;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
	implements BuyHouse
{

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

	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 ) { }
		catch (Throwable throwable)
		{
			throw new UndeclaredThrowableException(throwable);
		}
	}

	public final String buy(Integer integer)
	{
		try
		{
			return (String)super.h.invoke(this, m3, new Object[] {
				integer
			});
		}
		catch (Error ) { }
		catch (Throwable throwable)
		{
			throw new UndeclaredThrowableException(throwable);
		}
	}

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

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

	static 
	{
		try
		{
			m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
				Class.forName("java.lang.Object")
			});
			m3 = Class.forName("com.jayce.demo.BuyHouse").getMethod("buy", new Class[] {
				Class.forName("java.lang.Integer")
			});
			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());
		}
	}
}

可以看到,生成的代理类继承了 Proxy 类,这也验证了 Proxy 类中的注释 it is also the superclass of all dynamic proxy classes created by those methods.

同时,代理类还实现了目标类的接口,重写了接口中的方法,因此拥有了目标类的功能,所以才能完成代理功能。

在重写的方法中,我们可以看到,实际上它调用了 InvocationHandler.invoke()方法,也验证了InvocationHandler中的注释This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.

总结

  • Proxy 通过静态方法 newProxyInstance() 创建代理类
  • 调用代理类的方法,最终会调用 InvocationHandler 中的 invoke() 方法
  • 创建的代理类会继承 Proxy 并实现 目标类的接口
  • JDK 动态代理通过让代理类实现目标类的接口,使代理类具有目标类相同的功能。因此,JDK 动态代理只能代理实现了接口的类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值