动态代理主要需要理解,其实现是基于反射机制。
代理模式
- 当一个对象不能直接使用,可以在客户端和目标对象之间直接创建一个中介,这个中介就是代理。
动态代理作用
1、控制访问
- 在代理中,控制是否可以调用目标对象的方法
2、功能增强
- 可以在完成目标对象的调用时,附加一些额外的功能,这些额外的功能就叫做功能增强。
代理的实现方式
1、 静态代理
- 代理类是手工实现的 Java 文件,同时代理的对象是固定的。
优点:容易理解,使用方便
缺点:在目标类比较多的情况下,会产生大量的代理类;当接口改变时,影响的目标类成倍增加
2、动态代理
- 使用反射机制,在程序执行中,动态创建代理类对象。
特点:不用创建类文件,代理的目标类是活动的,可设置的。
优点:不用创建代理类;可以给不用的目标随时创建代理。
动态代理实现方式
1、JDK 动态代理(理解)
- 使用 java 反射包中的类和接口实现动态代理的功能
- 反射包 java.lang.reflect ,里面有三个类 : InvocationHandler ,Method,Proxy.
1)InvocationHandler 接口(调用处理器):就一个方法invoke()
invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在 invoke() 方法中。
代理类完成的功能:
1. 调用目标方法,执行目标方法的功能
2. 功能增强,在目标方法调用时,增加功能。
方法原型:
public Object invoke(Object proxy, Method method, Object[] args)
参数:
Object proxy:jdk创建的代理对象,无需赋值。
Method method:目标类中的方法,jdk提供method对象的
Object[] args:目标类中方法的参数, jdk提供的。
InvocationHandler 接口:表示你的代理要干什么
怎么用:
1.创建类实现接口InvocationHandler
2.重写invoke()方法, 把原来静态代理中代理类要完成的功能,写在这。
2)Method类:表示方法的, 确切的说就是目标类中的方法。
作用:通过Method可以执行某个目标类的方法,Method.invoke();
method.invoke(目标对象,方法的参数)
Object ret = method.invoke(service2, "李四");
说明:
method.invoke()就是用来执行目标方法的,等同于静态代理中的
//向厂家发送订单,告诉厂家,我买了u盘,厂家发货
float price = factory.sell(amount); //厂家的价格。
3)Proxy类:核心的对象,创建代理对象。之前创建对象都是 new 类的构造方法()
现在我们是使用Proxy类的方法,代替new的使用。
方法: 静态方法 newProxyInstance()
作用是: 创建代理对象, 等同于静态代理中的TaoBao taoBao = new TaoBao();
方法原型:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数:
1. ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader
类a , a.getCalss().getClassLoader(), 目标对象的类加载器
2. Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。
3. InvocationHandler h : 我们自己写的,代理类要完成的功能。
返回值:就是代理对象
jdk 动态代理,必须有接口,目标类必须实现接口,没有接口时,需要使用 cglib 动态代理。
实现步骤:
-
创建接口,定义目标类要完成的功能
public interface UsbSell { float sell(int amount); }
-
创建目标类,实现接口
public class UsbKingFactory implements UsbSell { @Override public float sell(int amount) { System.out.println("目标方法执行:出厂价89"); return 89f; } }
-
创建 InvocationHandler 接口的实现类,重写 invoke 方法,在其 invoke 方法中完成代理类的功能
- 调用目标方法
- 增强功能
public class MySellHandler implements InvocationHandler { // 目标对象 private Object target; // 通过无参构造赋值 public MySellHandler(Object target) { this.target = target; } @Override // 在此方法中调用目标方法,并附加额外功能 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 通过动态代理,调用目标对象的目标方法 Object res = method.invoke(target, args); // 在此添加额外功能 if (res != null) { Float price = (Float) res; price += 10; res = price; System.out.println("功能增强:中介加价后价格" + price); } return res; } }
-
使用 Proxy 类的静态方法,创建代理对象,并把返回值转为接口类型
public class SellMain { public static void main(String[] args) { // 创建目标对象 UsbSell usbKingFactory = new UsbKingFactory(); // 创建完成了目标方法调用和功能增强的自定义InvocationHandler接口实现类 MySellHandler handler = new MySellHandler(usbKingFactory); // 创建动态代理对象 UsbSell proxy = (UsbSell) Proxy.newProxyInstance( usbKingFactory.getClass().getClassLoader(), usbKingFactory.getClass().getInterfaces(), handler); // 通过动态代理对象,调用指定方法 proxy.sell(1); } }
2、cglib 动态代理(了解)
- cglib 是第三方的工具库,创建代理对象
- 原理是继承,cglib 通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改
- 因为 cglib 是继承,重写方法,所以要求目标类不能是 final 的,方法也不能是 final 的
- cglib 的要求目标类比较宽松,只要能继承即可
- cglib 在很多的框架中使用,如 mybatis 和 spring