动态代理

动态代理主要需要理解,其实现是基于反射机制。

代理模式

  • 当一个对象不能直接使用,可以在客户端和目标对象之间直接创建一个中介,这个中介就是代理。

动态代理作用

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 动态代理。

实现步骤:

  1. 创建接口,定义目标类要完成的功能

    public interface UsbSell {
        float sell(int amount);
    }
    
  2. 创建目标类,实现接口

    public class UsbKingFactory implements UsbSell {
        @Override
        public float sell(int amount) {
            System.out.println("目标方法执行:出厂价89");
            return 89f;
        }
    }
    
  3. 创建 InvocationHandler 接口的实现类,重写 invoke 方法,在其 invoke 方法中完成代理类的功能

    1. 调用目标方法
    2. 增强功能
    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;
        }
    }
    
  4. 使用 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
posted @ 2022-11-01 23:08  luis林  阅读(49)  评论(0编辑  收藏  举报