玩命加载中 . . .

动态代理(01)


动态代理

概述

代理,在我们日常生活之中就有体现,代购,中介,换IP,商家等等,比如有一家美国的大学,可以对全世界招生留学中介(代理 )

留学中介(代理):帮助这家美国的学校招生,中介是学校的代理中介是代替学校完成招生功能。

代理特点:

  1. 中介和代理他们要做的事情是一致的:招生。
  2. 中介是学校代理,学校是目标。
  3. 家长——–>中介(学校介绍,办理入学手续)——–>美国学校。
  4. 中介是代理,收取费用。

为什么要找中介?

  1. 中介是专业的,方便。
  2. 家长现在不能自己去找学校,家长没有能力访问学校或美国学校不接收个人来访。
  3. 买东西都是商家卖,商家是某个商品的代理,你个人买东西,肯定不会让你接触到厂家的。

静态代理

动态代理结构图

代理模式作用

  1. 功能增强:在你原有的功能上,增加了额外的功能:新增加的功能,叫做功能增强。
  2. 控制访问:代理类不让你访问目标,例如商家不让用户访问厂家。

实现代理方式

  1. 静态代理:手动增加代理类,在原有代理类基础上增加功能。
  2. 动态代理:使用JDK反射实现动态添加代理的方法。

静态代理

例子:

模拟一个用户购买U盘的行为。

  • 用户:消费者、只能从商家购买商品。
  • 商家:代理类、代理U盘品牌的商品。
  • 厂家:目标类。
  • 三者之间的关系:用户(客户端)—>商家(代理)—>厂家(目标)。
  • 总结:商家和厂家都是卖某品牌的U盘,他们完成的功能是一致的。

实现步骤:

  1. 创建一个接口,定义卖U盘的方法,表示你的厂家和商家做的事情。
  2. 创建厂家类,实现一步骤的接口。
  3. 创建商家,就是代理,也需要实现一步骤中的接口。
  4. 创建客户端类,调用商家的方法买一个U盘。

静态代理创建

Package结构:

动态代理package结构

实现类:

// 接口类
public interface UsbSell {
    /**
     * 购买U盘的方法
     * @param amount 一次购买U盘的数量
     * @return 购买U盘的总价格
     */
    float sell(int amount);
}
// 工厂类
public class UsbKingFactory implements UsbSell {
    /**
     * 厂家的购买方法
     * @param amount 一次购买U盘的数量
     * @return 购买U盘的总价格
     */
    @Override
    public float sell(int amount) {
        return amount * 80.0f;
    }
}
// 商家类
public class pinxixi implements UsbSell {
    @Override
    public float sell(int amount) {
        UsbKingFactory usbKingFactory = new UsbKingFactory();
        // 向厂家发送订单,告诉厂家,我订购了U盘,让厂家发货,并返回报价
        float floorPrice = usbKingFactory.sell(amount);
        // 在厂家底价的基础上,没件加25元利润
        float price = amount * 25.0f + floorPrice;
        // 在调用sell()方法后,增强的功能
        System.out.println("拼兮兮给您返回优惠券或红包");
        return price;
    }
}
// 消费者类
public class shopMain {
    public static void main(String[] args) {
        pinxixi pinxixi = new pinxixi();
        float price = pinxixi.sell(2);
        System.out.println("拼兮兮U盘的价格是:" + price);
    }
}

总结:

  • 优点:
    1. 实现简单。
  • 缺点:当你的项目,目标类(厂家类)有很多时候,代理类(商家类)也会随之增多。
    1. 目标类增多、代理类也会随之增多。
    2. 接口中方法修规、增加后,众多的目标类和代理类都需要修改。

动态代理

概述

使用JDK的反射机制,创建对象的能力,创建的是代理类的的对象,而不用手动创建类文件更不用手动写具体的Java文件,且在程序执行时,调用JDK提供的方法自动创建代理类的对象。换句话说:动态代理是一种创建Java象的能力,让你不用创建代理类(商家类)就能创建代理类对象,除去了中间商。

静态代理和动态代理对比

  • 动态代理可以实现低耦合、高内聚,可以在修改、增加接口方法时,不会影响代理类对象。
  • 代理类数量可以很少,不用创建很多的代理方法。

动态代理介绍

动态代理介绍

  1. 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的,不需要定义代理类的java源文件。
  2. 动态代理其实就是JDK运行期间动态创建Class字节码并加载到JVM。

实现方式

JDK动态代理(理解)

  • 使用Java反射包(java.lang.reflect)中的反射包中类和接口实现动态代理的功能,会使用到以下三个类/接口:InvocationHandler、Method、Proxy。

CGLlB动态代理(了解)

  • CGLlB原理是继承,CGLlB通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。因为CGLlB使用的是继承、重写方法,所以要求目标类何方法均不能使用final。CGLlB的要求目标类比较宽松,只要能继承就可以了。CGLlB在很多的框架中使用,比如Mybatis,Spring框架中都有使用。
  • InvocationHandler(调用处理器接口):
    • 方法:invoke()抽象方法、需要实现。
    • 说明:代理对象要执行的功能代码,即代理类要完成的功能就写在invoke()方法中。
  • Method(类):表示目标类中所要调用的方法,通过Method可以执行某个目标类中的方法、 JDK会自动提供目标类方法,无需手动指定。
    • 方法:invoke(目标类对象,方法的参数)
    • 说明:目标类对象:执行某个目标类中的方法、方法的参数:调用目标类方法的参数。
    • 例如:Object obj = method.invoke(service,”参数”);
    • 注意:Method.invok()与InvocationHandler接口中invoke()方法是两个方法,只不过是重名,不是一个方法。
  • Proxy(类):核心对象,创建代理对象。之前创建对象都是new类的构造方法(),现在可以使用Proxy类的方法,代理new使用。
    • 方法(静态方法):public static Object newProxyInstance(Classloader loader , Class<?>[] interfaces , InvocationHandler h);
    • 说明:创建代理对象,等同于静态代理中的new UsbKingFactory();
    • 参数说明:
      1. Classloader loader:a.getCalss().getClassLoader()、目标对象类加载器,负责向内存中加载对象(使用反射获取对象)
      2. Class<?>[] interfaces:目标对象实现的接口,也是通过反射要获取的。
      3. InvocationHandler h:代理类要完成的功能。
      4. 返回值:目标类的代理对象。

JDK动态代理创建

目标类实现功能:

  1. 实现业务逻辑的功能。

代理类实现功能:

  1. 调用目标类方法并执行目标方法所有功能。
  2. 基于目标类方法功能调用时,增加功能。

创建步骤:

  1. 创建接口,定义目标类要完成的功能。
  2. 床架目标类实现接口。
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能。
    1. 调用目标方法。
    2. 增强功能。
  4. 使用Proxy;类的静态方法,创建代理对象,并将返回值转为接口类型。

Package结构:

动态代理包结构

实现类:

// 接口类
public interface UsbSell {
    /**
     * 购买U盘的方法
     * @param amount 一次购买U盘的数量
     * @return 购买U盘的总价格
     */
    float sell(int amount);
}
// 代理类
public class UsbKingFactory implements UsbSell {
    /**
     * 厂家的购买方法(目标类)
     * @param amount 一次购买U盘的数量
     * @return 购买U盘的总价格
     */
    @Override
    public float sell(int amount) {
        System.out.println("目标类中,执行sell目标方法");
        float price =  amount * 80.0f;
        return price;
    }
}
// 动态代理类
// 必须实现InvocationHandler接口,完成代理类要做的功能
// 1.调用目标类中的方法
// 2.功能增强啊
public class MysellHander implements InvocationHandler {

    /*
        动态代理:目标代理是活动的,不是固定的,需要传进来,穿的是什么就是给谁创建代理。
    */
    private Object target = null;
    public MysellHander(Object target) {
        // 给目标对象赋值
        this.target = target; // == float price = amount * 25.0f + floorPrice;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 向厂家发送订单,告诉厂家,我订购了U盘,让厂家发货,并返回报价
        Object res  = null;
        // 执行目标类方法
        res = method.invoke(target, args); // == float floorPrice = usbKingFactory.sell(amount);

        // 在厂家底价的基础上,没件加25元利润
        if (res!=null) {
            Float price = (Float) res;
            price = 25.0f * (Integer)args[0] + price;
            res = price;
        }

        // 在调用sell()方法后,增强的功能
        System.out.println("拼兮兮给您返回优惠券或红包");

        // 增加后的价格
        return res;

    }
}
// 测试类
public class shopMain {
    public static void main(String[] args) {
        // 1.创建目标对象

        UsbSell factory =  new UsbKingFactory();
        // 2.创建InvocationHandler对象
        MysellHander handler = new MysellHander(factory);
        // 3.创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);
        // 4.通过代理执行方法
        float price = proxy.sell(1);
        System.out.println(price);
    }
}

注意:

动态代理,必须要有接口出现,如果没有,可以使用CGLlB实现。


文章作者: 小靳同学
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小靳同学 !
评论
 上一篇
JDBC学习笔记(01) JDBC学习笔记(01)
初始JDBC概念:Java DataBase Connectivity :Java数据库连接,Java语言操作数据库。官方定义一套操作所有关
2021-10-31
下一篇 
数据库笔记(01) 数据库笔记(01)
初识MySQL数据库结构 DB DBMS SQL DataBase(数据库,数据库实际上在硬盘上以文件的形式存在) DataBas
2021-10-30
  目录