当前位置: 首页 > article >正文

【Java基础】代理

文章目录

  • 代理
  • 代理模式的优点
  • 代理模式类型
    • 基于JDK的静态代理
    • 基于JDK的动态代理

代理

一种设计模式,不允许用户直接访问核心功能,而是通过代理来访问核心类的核心功能

举个例子,如果我们现在需要一个银行转账系统,我们需要在一个Java类中实现:

  • 转账方法(核心)
  • 验证用户身份、验证金额
  • 一些后续服务等

初学者很容易将这些方法都写在同一个Java类中,然而以上三个方法其实每一个都是一个很复杂的流程,写在一起会导致当前类的功能量太大,所以我们想到了一个办法:将这些方法分开写,不再写到同一个方法中,将最核心的业务,也就是转账业务,由支付宝或者微信代理完成,而其他的服务由微信或者支付宝自身完成,用户通过访问支付宝代理来完成最核心的转账业务。——也就是代理模式,用户只能访问代理,而不能访问核心功能
在这里插入图片描述

代理模式的优点

  1. 防止用户直接访问核心方法带来一些不必要的危机(代码冗余等)
  2. 能够对核心方法进行功能的增强

代理模式类型

代理模式分为三类:基于JDK的静态代理、基于JDK的动态代理、基于CGLB的动态代理,下面我们详细说一下每一种代理模式:
在这里插入图片描述

基于JDK的静态代理

静态代理中,我们需要实现核心类与核心对象,代理类与代理对象,以及一个定义核心类中核心方法的接口,代理类与核心类中都需要对接口中核心方法的实现,并在代理类中创建核心对象,进行代理,在用户访问时,通过创建代理对象来访问核心对象,来完成我们的代理流程
在这里插入图片描述我们依然以上面的银行转账系统为例子,我们先定义一个Pay接口,其中定义我们的转账方法(核心方法),但不实现

public interface Pay {
    // 定义核心方法
    public void pay(String A, String B, Double money);
}

再定义Bank核心类,继承Pay接口并实现接口中的核心方法Pay()

public class Bank implements Pay {
    @Override
    public void pay(String A, String B, Double money) {
        System.out.println(A + "给" +B+ "转账了" +money+ "元");
    }
}

定义代理类,我们假定为支付宝AliPay,让代理类也继承接口并实现其中的核心方法,其中也包括对核心方法功能的增强,如验证功能以及后续服务功能:

public class AliPay implements Pay {

    // 1. 定义一个好被代理的类--->核心类
    private Bank bank = new Bank();

    public void check(String A, String B,Double money){
        System.out.println("对A进行了验证");
        System.out.println("对B进行了验证");
        System.out.println("对金额进行了验证");
    }

    private void service(){
        System.out.println("转账完成后进行的事后服务");
    }

    @Override
    public void pay(String A, String B, Double money) { // 核心方法的实现
        check(A,B,money);
        bank.pay(A,B,money);
        service();
    }
}

最后我们创建Test测试类,来测试我们以上的代理流程:

public class Test {
    public static void main(String[] args) {
        AliPay aliPay = new AliPay();
        aliPay.pay("zhangsan", "lisi", 100.12);
    }
}

我们可以看到,在用户界面,也就是Test类中,用户只能通过访问代理,也就是创建aliPay对象来对Bank核心类中的核心方法Pay()进行访问,而不是直接对核心类中的核心方法进行访问,这样就完成了代理,下面是内存图:
在这里插入图片描述

基于JDK的动态代理

先考虑一个问题,如果我们的代理需要同时代理很多个核心类的话,那么会出现什么情况?比如一个商店,对服装工厂、鞋子工厂、裤子工厂等等多个工厂同时进行代理,那么在这个代理类的代理对象中,我们就需要创建很多个核心对象,来完成我们的代理流程,并且每多代理一个工厂就要手动增加一个核心对象,这也是静态代理的一个缺陷:同一个代理类代理多个目标类是很难实现的

在这里插入图片描述

那么我们就需要用动态代理模式来完成这个功能。与静态代理不同的是,在动态代理中,每一个核心对象会由一个独立的代理对象进行代理,而不是全部都由同一个代理对象代理,也就是一对一模式,如下图

在这里插入图片描述

那我们如何实现这个一对一模式呢?

我们商店的例子中,商店作为动态代理,其中包含一个Object对象,以及一个将核心对象传入代理对象的构造器:

public class Shop {
    private Object object; 
    public Shop(Object o){ // 传入目标类的目标对象(代理类的代理对象)
        object = o;
    }
}

public class Test {
    public static void main(String[] args) {
        ClothesFactory clothesFactory = new ClothesFactory(); 
        Shop shop = new Shop(clothesFactory);
    }
}

这样在用户访问时,每创建一个代理对象shop,就会将一个核心对象的地址传给代理对象,进行代理,也就是完成了上面说的一对一模式

在这里插入图片描述

静态代理中,我们一一实现了每一个核心类的核心方法,继承了相对应的接口,动态代理中同样需要实现方法,但是并不像静态代理那样分别进行继承和实现,这里我们定义一个返回值为Object类型的方法getInstance()通过这个方法我们可以获取到核心类的类对象,从而完成对其中方法的实现

public Object getProxyInstance(){ // object.getClass() 获取目标类的类对象
    							  // object.getClass().getInterfaces() 获取目标类所继承的接口,告知代理类所代理的核心功能是什么
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
    } // 这里的返回值是一个对象

之后我们需要对核心类中的核心方法进行调用,在上一步我们已经知道了核心类的类对象信息,那么我们就可以通过反射来调用其中的方法,这里我们需要继承一个接口InvocationHandler,来对其中的invoke()方法进行重写,来调用核心类中的核心方法(暂时不做深究,和反射中的invoke()方法是一个方法)

public class Shop implements InvocationHandler { 
    //三个参数的讲解
    //1.Object:jdk创建的代理类,无需赋值
    //2.Method:目标类当中的方法,jdk提供,无需赋值
    //3.Object[]:目标类当中的方法的参数,jdk提供,无需赋值
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(object,args);
        service();
        return null;
    }
}

那么我们在Test类中获取到相对应的类信息用什么来接收呢?答案是需要用相关接口进行接收。观察整个流程,没有任何一个地方可以调用到其中的核心方法,如果我们直接创建子类ClothesFactory对象对其中的核心方法进行调用的,这种方式并没有通过任何代理,如下:

public class Test {
    public static void main(String[] args) {
        ClothesFactory clothesFactory = new ClothesFactory(); // 这里利用反射创建对象也可以
		clothesFactory.BuyClothes("XL");// 这种方式并没有走任何代理,只是直接调用了ClothesFactory中的BuyClothes()方法
    }
}

在这里插入图片描述

所以只能通过接口进行调用,这里是一个多态,接口Clothes为父类,创建父类引用,让他指向子类对象,然后在调用方法时,因为子类ClothesFactory对方法进行了重写,那么只能访问重写方法,这样就完成了整个代理流程,访问到了我们的核心方法

在这里插入图片描述


http://www.kler.cn/a/290255.html

相关文章:

  • 前端常用布局模板39套,纯CSS实现布局
  • jmeter常用配置元件介绍总结之定时器
  • Matlab: 生成对抗网络,使用Datastore结构输入mat格式数据
  • GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件
  • C++20 中最优雅的那个小特性 - Ranges
  • 系统架构设计师论文:大数据Lambda架构
  • Your Diffusion Model is Secretly a Zero-Shot Classifier论文阅读笔记
  • 农事管理系统
  • 守护夏日清凉:EasyCVR+AI视频智能管理方案为水上乐园安全保驾护航
  • 爬虫 可视化 管理:scrapyd、Gerapy、Scrapydweb、spider-admin-pro、crawllab、feaplat、XXL-JOB
  • Linux云计算学习笔记10 (打包压缩与解包)
  • CSS 中的element()函数
  • AVL树调整平衡及旋转详解
  • MATLAB-绘图系列(第一期)
  • 线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
  • 性能、成本与 POSIX 兼容性比较: JuiceFS vs EFS vs FSx for Lustre
  • ElasticSearch和Kibana的安全设置以及https设置
  • Vue 3 的性能提升具体体现在哪些方面?
  • 达梦数据库事务管理
  • 深入解析浏览器与Web服务器的通信机制:从URL输入到页面渲染的全过程
  • glsl着色器学习(七)
  • 多线程篇(基本认识 - 锁优化)(持续更新迭代)
  • HCIP笔记12-交换(1)
  • Mysql高级篇(上)—— Mysql架构介绍(二)
  • SprinBoot+Vue图书馆预约与占座微信小程序的设计与实现
  • C++string类相关OJ练习(2)