黑马程序员--09.动态与代理AOP--03【InvocationHandler接口】【通过反射创建动态代理类对象】

本文主要介绍了Java动态代理中的InvocationHandler接口及其invoke方法,以及如何通过反射创建动态代理类对象。讲解了InvocationHandler接口的作用和其实现,以及在创建动态代理类实例时的简化方法。同时,文章探讨了通过不同方式使用代理类实例的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理与AOP----3

InvocationHandler接口

通过反射创建动态代理类对象

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.    InvocationHandler接口

1). java.lang.reflect.InvocationHandler接口

(1).InvocationHandler接口基础知识

[1]. InvocationHandler接口中文翻译:方法调用句柄

[2]. InvocationHandler接口所在的位置

InvocationHandler位于java.lang.reflect反射子包中。

[3]. InvocationHandler接口的含义(API)

每一个动态代理类实例(proxyinstance)都有自身关联方法调用句柄(invocationhandler)

[4]. InvocationHandler接口的含义

代理类实例方法调用就会被指派到和这个代理类实例相关联的方法调用句柄中的invoke()去执行

简言之】对代理类实例方法调用就等价于(也就是)代理类实例相关联的方法调用句柄invoke方法调用

[5]. InvocationHandler接口源码

package java.lang.reflect;
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method,Object[] args)
    throws Throwable;
}

注意InvocationHandler接口中有且只有一个invoke方法没有其他的方法

2). InvocationHandler的invoke方法

(1). 源码声明

public Object invoke(Object proxy, Method method,Object[] args) throws Throwable;

(2). 输入参数

[1]. Object proxy:要调用哪个动态代理类对象的方法

[2]. Method method:要调用某个动态代理类对象的哪个方法

[3]. Object[] args:为要调用的该方法传入哪些实参

(3). 返回值类型 ---- Object类型

2.    通过反射创建动态代理类对象

1). 获取动态代理类和普通类的Class对象

使用反射的基石就是获取对应类的Class类对象

(1). 动态代理类普通类区别

[1]. 动态代理类是在程序运行JVM计算在内存中生成。再被类加载器加载,最终获取动态加载类的Class对象

[2]. 普通类事先通过javac编译文件系统中字节码文件然后JVM运行时类加载器文件系统的字节码文件进行加载,最终获得普通类的Class对象

(2). 动态代理类普通类的代码上获取Class对象的方式

{1}. 动态代理类获取Class对象 -----  只能通过Proxy的静态方法getProxyClass()方法

进行的一次关联的对类加载器要实现接口数组进行关联

【举例】

Class<?> proxyClass =
Proxy.getProxyClass(ArrayList.class.getClassLoader(), Collection.class);

{2}.动态代理类获取Class对象四种方式

【举例】

WAY_I静态方法获取Class对象(包括指定类加载器的重载方法)

Class clazz1 =Class.forName("java.lang.String");
Class clazz2 =Class.forName("java.lang.String", true, ClassLoader.getSystemClassLoader());

WAY_II静态属性获取Class对象

Class clazz3 =String.class;

WAY_III动态方法获取Class对象

Class clazz4 =new String().getClass();

WAY_IV自定义类加载器特殊的位置获取指定的Class对象

Class clazz5 =new MyClassLoader().loadClass("D:\\BlackHorse\\OuterClass");


2). 通过反射创建动态代理类实例对象

(1). 通过常规的反射方式实例化动态代理类对象

[1]. 示例代码

Class proxyClass =Proxy.getProxyClass(ClassLoader.getSystemClassLoader(),Collection.class);
 
Constructor con=proxyClass.getConstructor(InvocationHandler.class);
 
Object proxyInstance =con.newInstance(new InvocationHandler() {
    private ArrayList target =new ArrayList();
    @Override
    public Object invoke(Object proxy, Method method,Object[] args)
           throws Throwable {
       return method.invoke(target, null);
    }
});
 
System.out.println(proxyInstance.getClass()+"@"+ proxyInstance.hashCode());

[2]. 打印结果:class $Proxy0@1

(2). 动态代理类实例化的简化

为了简化创建动态代理类实例,Proxy将以上通过反射的来获取动态类的实例的步骤进行了简单的封装。Proxy中的静态方法newProxyInstance()就是对上面步骤的封装。

[1]. 方法原型

public static ObjectnewProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throwsIllegalArgumentException;

{1}. 输入参数I: ClassLoaderloader, Class<?>[] interfaces

说明1】这两个参数用来构建动态代理类的Class对象

说明2】这两个参数是用来向内部封装的Proxy.getProxyClass()传参

注意】这里面的interfaces的参数就不再是getProxyClass(ClassLoader,Class<?>...)后面的可变参数数组类型了。因为这个参数不在位于方法的参数列表的最后位置,所以可变参数数组的使用前提不成立

{2}. 输入参数II: InvocationHandlerh

说明1】这个参数用来创建动态代理类的实例对象

说明2】这个参数是通过动态代理类的构造方法类Constructor对象在调用newInstance()的时候,向这个方法传参

[2]. 方法源码

public class Proxy implements java.io.Serializable {
  //...
  //对Proxy的动态代理类,由于只有一个构造方法,并且方法的参数只有一个
//InvocationHandler类型的,所以这里面就直接把这个形参固定住,不在要求用
//户关联形参
private final static Class[] constructorParams = {InvocationHandler.class};
//...
public static Object newProxyInstance(ClassLoaderloader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  if (h == null) {
     throw new NullPointerException();
  }
  Class cl = getProxyClass(loader, interfaces);
  try {
     Constructor cons = cl.getConstructor(constructorParams);
     return (Object) cons.newInstance(new Object[] { h });
  } catch (NoSuchMethodException e) {
     throw new InternalError(e.toString());
  } catch (IllegalAccessException e) {
     throw new InternalError(e.toString());
  } catch (InstantiationException e) {
     throw new InternalError(e.toString());
  } catch (InvocationTargetException e) {
     throw new InternalError(e.toString());
  }
}
//...
}

[3]. 应用举例

ClassLoader loader =Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces ={Collection.class};
Object proxyInstance =Proxy.newProxyInstance(loader,interfaces,
new InvocationHandler() {
    private ArrayList target =new ArrayList();
    @Override
    public Object invoke(Object proxy, Method method,Object[] args)
           throws Throwable {
       return method.invoke(target, args);
    }
});
 
System.out.println(proxyInstance.getClass()+"@"+ proxyInstance.hashCode());


打印结果】class $Proxy0@1

(3). 通过传统方式使用代理类实例的可行性

[1]. 直接按照Object类型使

【局限性】---- 这种方式局限性比较大  不能直接调用接口类的方法


此时想使用代理类实例proxyInstance直接访问Collection中的方法不能办到。

[2]. 强转成直接父类java.lang.reflect.Proxy----这种方式的局限性也比较大

【局限性】只能使用Object和Proxy的方法,失去了动态代理类的在代理方面的意义。


此时无法使用Collection接口中的方法

[3]. 强转成某一个实现的接口类型----这种办法可行 但是麻烦

【局限性】如果动态代理类对象实现多个接口,此时想调用不同接口的不同方法,这个时候必须要进行显示强转,使用起来非常麻烦。

[4]. 强转成本类类型$Proxy数字

这种办法是不可行的!!!

因为要想直接在java源代码写出$Proxy0这个类型,要求要有对应的字节码文件存在于文件系统并能被类加载器到指定的位置进行加载。但是我们知道$Proxy0是在JVM运行时,JVM在内存中动态合成的动态代理类的类名。所以在编译的时候,$Proxy0并不存在的。所以如果在程序中直接进行($Proxy0)强转的话,会编译报错!!!

结论】通过传统形式操作动态代理类对象总是或多或少存在着局限性

启发通过反射形式操作动态代理类对象

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值