【设计模式——学习笔记】23种设计模式——代理模式Proxy(原理讲解+应用场景介绍+案例介绍+Java代码实现)

news2024/9/22 4:10:27

介绍

基础介绍

  • 代理模式为一个对象提供一个代理对象,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是:可以在不修改目标对象代码的基础上,增强额外的功能操作,即扩展目标对象的功能
  • 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
  • 代理模式有不同的形式,主要有静态代理动态代理Cglib代理三种形式

案例实现

静态代理

介绍

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

登场角色

在这里插入图片描述

  • Subject(主体):定义了使 Proxy角色 和 RealSubject角色 之间具有一致性的接口
  • RealSubiect(实际的主体):RealSubject角色(本人)会在Proxy角色(代理人)无法胜任工作时出场。它实现了在 Subject角色 中定义的接口(API)
  • Proxy(代理人):Proxy角色处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject 角色。Proxy角色实现 Subject角色中定义的接口(API)
  • Client(请求者):使用Proxy对象的角色

案例一

【应用实例】

  • 定义一个接口ITeacherDao
  • 目标对象TeacherDAO实现接口ITeacherDAO
  • 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
  • 调用的时候通过调用代理对象的方法来调用目标对象

在这里插入图片描述

【Client要做什么事情】

  • 创建TeacherDaoProxy代理对象
  • 创建TeacherDao 对象
  • 将TeacherDao对象交给TeacherDaoProxy对象
  • 执行方法

实现

【接口】

package com.atguigu.proxy.staticproxy;

/**
 * 接口
 */
public interface ITeacherDao {

    /**
     * 授课
     */
    void teach();
}

【目标对象】

package com.atguigu.proxy.staticproxy;

public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println(" 老师授课中  。。。。。");
    }

}

【代理对象】

package com.atguigu.proxy.staticproxy;

/**
 * 代理对象
 */
public class TeacherDaoProxy implements ITeacherDao {

    /**
     * 目标对象,通过接口来聚合
     */
    private ITeacherDao target;

    /**
     * 构造器
     * @param target
     */
    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("开始代理  完成某些操作。。。。。 ");
        // 中间还可以写一下其他代码,比如统计方法调用的次数
        target.teach();
        System.out.println("代理结束 。。。。。");
    }

}

【客户端】

package com.atguigu.proxy.staticproxy;

public class Client {

   public static void main(String[] args) {
      //创建目标对象(被代理对象)
      TeacherDao teacherDao = new TeacherDao();

      //创建代理对象, 同时将被代理对象传递给代理对象
      TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

      //通过代理对象,调用到被代理对象的方法
      //执行的是代理对象的方法,代理对象再去调用目标对象的方法
      teacherDaoProxy.teach();
   }

}

【运行】

开始代理  完成某些操作。。。。。 
 老师授课中  。。。。。
代理结束 。。。。。

Process finished with exit code 0

分析

  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标进行功能扩展
  • 缺点:因为代理对象需要与目标对象实现一样的接口,一旦接口增加方法,目标对象与代理对象都要维护

案例二

在这里插入图片描述

实现

【接口】

package com.atguigu.proxy.Sample;

public interface Printable {
    /**
     * 设置名字
     *
     * @param name
     */
    public abstract void setPrinterName(String name);

    /**
     * 获取名字
     *
     * @return
     */
    public abstract String getPrinterName();

    /**
     * 显示文字(打印输出)
     *
     * @param string
     */
    public abstract void print(String string);
}

【目标对象:打印机】

package com.atguigu.proxy.Sample;

/**
 * 打印机
 */
public class Printer implements Printable {
    private String name;

    public Printer() {
        heavyJob("正在生成Printer的实例");
    }

    /**
     * 构造函数
     *
     * @param name
     */
    public Printer(String name) {
        this.name = name;
        heavyJob("正在生成Printer的实例(" + name + ")");
    }

    /**
     * 设置打印机名字
     *
     * @param name
     */
    public void setPrinterName(String name) {
        this.name = name;
    }

    /**
     * 获取打印机名字
     *
     * @return
     */
    public String getPrinterName() {
        return name;
    }

    /**
     * 显示带打印机名字
     *
     * @param string
     */
    public void print(String string) {
        System.out.println("=== " + name + " ===");
        System.out.println(string);
    }

    /**
     * 重活 持续干5秒钟
     *
     * @param msg
     */
    private void heavyJob(String msg) {
        System.out.print(msg);
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.print(".");
        }
        System.out.println("结束。");
    }
}

【代理对象】

package com.atguigu.proxy.Sample;

public class PrinterProxy implements Printable {
    /**
     * 名字
     */
    private String name;
    /**
     * 目标对象
     */
    private Printer real;

    public PrinterProxy() {
    }

    /**
     * 构造函数
     * @param name
     */
    public PrinterProxy(String name) {
        this.name = name;
    }

    /**
     * 设置名字
     * @param name
     */
    public synchronized void setPrinterName(String name) {  
        if (real != null) {
            // 如果已经有 目标对象
            // 同时设置 目标对象 的名字
            real.setPrinterName(name);
        }
        this.name = name;
    }

    /**
     * 获取名字
     * @return
     */
    public String getPrinterName() {
        return name;
    }

    /**
     * 显示
     * @param string
     */
    public void print(String string) {
        realize();
        // 将真正打印的活委托给 目标对象
        real.print(string);
    }

    /**
     * 生成目标对象实例
     */
    private synchronized void realize() {
        if (real == null) {
            real = new Printer(name);
        }
    }
}

问:setPrinterName方法和realize方法都是synchronized方法。如果不使用synchronized会有什么问题呢?

答:如果不使用synchronized,可能会存在多个线程同时调用setPrinterName或realize方法,导致多个对象同时创建或设置名字,从而影响程序的正确性和稳定性。因此,使用synchronized关键字进行同步是为了保证线程安全性和正确性。

【主类】

package com.atguigu.proxy.Sample;

public class Main {
    public static void main(String[] args) {
        Printable p = new PrinterProxy("Alice");
        System.out.println("现在的名字是" + p.getPrinterName() + "。");
        p.setPrinterName("Bob");
        System.out.println("现在的名字是" + p.getPrinterName() + "。");
        p.print("Hello, world.");
    }
}

【运行】

现在的名字是Alice。
现在的名字是Bob。
正在生成Printer的实例(Bob).....结束。
=== Bob ===
Hello, world.

Process finished with exit code 0

总结

  • 使用代理模式可以提升处理速度:如果目标对象较庞大,代理模式只在真正需要执行目标对象的工作时,才生成目标对象的实例,可以避免系统一启动就需要生成所有实例,导致启动缓慢;打开一个文档时,也不会立即去生成所有图形对象,而是浏览至图形附近,才生成图形的实例,否则会导致文档打开卡顿
  • 在http代理中,通过 Web 浏览器访问 Web 页面时,并不会每次都去访问远程 Web 服务器来获取页面的内容而是会先去获取HTTP代理缓存的页面。只有当需要最新页面内容或是页面的缓存期限过期时,才去访问远程Web服务器。Web客户端扮演的是 Client角色,HTTP代理扮演的是Proxy角色(实现了缓存功能),而Web服务器扮演的则是RealSubiect角色

【划分代理人和本人的好处】

  • 隐藏实际对象的实现细节,保护真实对象不被直接访问,增加程序的安全性
  • 代理对象可以充当过滤器,在访问前进行权限校验、参数校验等操作,提高程序的健壮性和可靠性
  • 代理对象可以提供额外的功能,如实现缓存、懒加载等,增强了程序的扩展性和灵活性
  • 可以降低某些操作的耦合度,划分代理人和本人可以让客户端和服务端松耦合,方便修改和维护

动态代理

介绍

  • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  • 代理对象的生成,是利用JDK的API,动态地在内存中构建代理对象
  • 动态代理也叫做: JDK代理、接口代理

【JDK中生成代理对象的API】

  • API所在包:java.lang.reflect.Proxy
  • 使用方法来动态返回代理对象
static Object newProxylnstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

在这里插入图片描述

【getProxyInstance0方法需要做的事】

根据传入的目标对象,利用反射机制,返回一个代理对象,最后通过代理对象调用目标对象的方法

案例一

实现

【接口】

package com.atguigu.proxy.dynamic;

public interface ITeacherDao {

    /**
     * 授课
     */
    void teach();

    void sayHello(String name);
}

【目标对象:实现接口】

package com.atguigu.proxy.dynamic;

public class TeacherDao implements ITeacherDao {

   @Override
   public void teach() {
      System.out.println(" 老师授课中.... ");
   }

   @Override
   public void sayHello(String name) {
      System.out.println("hello " + name);
   }

}

【代理对象】

package com.atguigu.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    /**
     * 维护一个目标对象, Object
     */
    private Object target;

    /**
     * 构造器,对target进行初始化
     *
     * @param target
     */
    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 给 目标对象 生成一个代理对象
     * @return
     */
    public Object getProxyInstance() {
      /*
       *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

            //参数1. ClassLoader loader : 指定目标对象的类加载器,对象.getClass().getClassLoader()
            //参数2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型,获取该类的所有接口:target.getClass().getInterfaces()
            //参数3. InvocationHandler h : 事件处理,执行目标对象的方法时,会触发事件处理器方法, 会把当前执行的目标对象方法作为参数传入
       */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK代理开始~~");
                        //反射机制调用目标对象的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("JDK代理提交");
                        return returnVal;
                    }
                });
    }


}

【客户端】

package com.atguigu.proxy.dynamic;

public class Client {

   public static void main(String[] args) {
      //创建目标对象
      ITeacherDao target = new TeacherDao();

      //给目标对象,创建代理对象, 可以转成 ITeacherDao
      ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();

      // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
      System.out.println("proxyInstance=" + proxyInstance.getClass());

      //通过代理对象,调用目标对象的方法
      proxyInstance.teach();
      proxyInstance.sayHello(" tom ");
   }

}

【运行】

proxyInstance=class com.sun.proxy.$Proxy0
JDK代理开始~~
 老师授课中.... 
JDK代理提交
JDK代理开始~~
hello  tom 
JDK代理提交

Process finished with exit code 0

在这里插入图片描述

invoke那行代码就是执行方法的过程

如果方法有参数,会存在args中

在这里插入图片描述

返回值存储在returnVal中

Cglib代理

  • 静态代理动态代理都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理,也叫子类代理
  • Cglib代理是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书籍也将Cglib代理归属到动态代理
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展iava类与实现iava接口。它被许多AOP的框架使用,例如Spring AOP用来实现方法拦截
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

【在AOP编程中如何选择代理模式】

  • 目标对象要求实现接口,用动态代理
  • 目标对象不要求实现接口,用Cglib代理

【实现步骤】

  • 引入相关jar包

在这里插入图片描述

  • 在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.IllegalArgumentException
  • 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法(即这些方法不能通过代理调用)

在这里插入图片描述

案例一

实现

【目标对象】

package com.atguigu.proxy.cglib;

public class TeacherDao {

   public String teach() {
      System.out.println(" 老师授课中,我是cglib代理,不需要实现接口 ");
      return "hello";
   }
}

【代理对象】

package com.atguigu.proxy.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 需要实现MethodInterceptor的接口
 */
public class ProxyFactory implements MethodInterceptor {

    /**
     * 维护一个目标对象
     */
    private Object target;

    /**
     * 构造器,传入一个被代理的对象
     *
     * @param target
     */
    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 返回 target 对象的代理对象
     *
     * @return
     */
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * 必须重写 intercept 方法,来调用目标对象的方法
     *
     * @param arg0
     * @param method
     * @param args
     * @param arg3
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
        System.out.println("Cglib代理模式 ~~ 开始");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式 ~~ 提交");
        return returnVal;
    }

}

【客户端】

package com.atguigu.proxy.cglib;

public class Client {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao target = new TeacherDao();
        //获取到代理对象,并且将目标对象传递给代理对象
        TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法,触发 intecept 方法,从而实现对目标对象的调用,目标对象的方法有返回值就可以获取返回值
        String res = proxyInstance.teach();
        System.out.println("res=" + res);
    }

}

【运行】

Cglib代理模式 ~~ 开始
 老师授课中,我是cglib代理,不需要实现接口 
Cglib代理模式 ~~ 提交
res=hello

Process finished with exit code 0

代理模式的变体

  • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问
  • 缓存代理:当请求图片文件等资源时,先到缓存代理中取,如果取不到资源,再到公网或者数据库取,然后缓存
  • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息和协作

在这里插入图片描述

  • 同步代理:主要使用在多线程编程中,完成多线程间同步工作(比如很多人买票,不是直接去访问买票接口,而是访问代理,代理来做同步,最后再去访问买票接口)

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/815284.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day06-作业(MySQL)

备注&#xff1a;本次作业最终需要提交的是对应的SQL语句。 软件安装&#xff1a;安装数据库可视化工具Navicat Navicat下载&#xff0c;提取码&#xff1a;5555https://pan.baidu.com/s/1GtKdXu9Tx0G3ejgSwKVWZg 作业1&#xff1a;参照资料中提供的页面原型及需求描述&#x…

项目中使用过的线程池ThreadPoolTaskExecutor

创建线程池的实例应用 package com.youming.shuiku.datacenter.provider.utils;import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;Slf4j public class AsyncMana…

UnixBench 5.1.3 银河麒麟桌面操作系统V10 (SP1) ARM64 aarch64 图形性能测试 2d 3d, glmark2 3d测试

编译 安装libgl sudo apt install libgl-dev yeqiangyeqiang-greatwall:~/Downloads/UnixBench$ sudo apt install libgl-dev [sudo] yeqiang 的密码&#xff1a; 正在读取软件包列表... 完成 正在分析软件包的依赖关系树 正在读取状态信息... 完成 下列软件包…

安卓耗电量分析

这里写自定义目录标题 耗电原因分析分析类型 生成分析数据batterystats操作步骤:生成report报告 battery-historian手动编译安装容器安装内容解析 耗电原因分析 下文有阐述&#xff0c;很详细 https://www.cnblogs.com/SA226343/p/6047543.html https://www.cnblogs.com/mytec…

美颜SDK开发指南:优化直播人像表现

在当今社交媒体和直播平台的流行趋势下&#xff0c;越来越多的用户渴望在直播中展现更加自信和美丽的一面。本文将探讨美颜SDK的开发指南&#xff0c;介绍其优化直播人像表现的重要性以及关键的技术要点。 一、用户为什么离不开美颜&#xff1f; 美颜SDK作为一种集成在直播应用…

javascript运算符与流程控制

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/front-end-tutorial 】或者公众号【AIShareLab】回复 javascript 也可获取。 文章目录 运算符的分类算数运算符浮点数的精度问题 递增和递减运算符比较运算符逻辑运算符短路运算&#xff08;逻辑中断…

报表工具有哪些?奥威BI+方案,快速搞定数据分析

报表工具有很多&#xff0c;如Excel、 Tableau、Power BI、帆软BI、思迈特BI等都是中国企业常用的报表工具&#xff0c;但要说能够成熟使用“BI方案”&#xff0c;更快地完成部署&#xff0c;推动企业大数据分析的却寥寥无几。“奥威BI方案”&#xff0c;低风险、高效率、高性价…

首期华为云ROMA Connect《企业集成战略与华为数字化之道》高研班在东莞圆满举办

7月25日&#xff0c;首期华为云ROMA Connect《企业集成战略与华为数字化之道》高研班在东莞华为制造业数字化转型中心圆满举办。 20多家东莞精密机械、电子、环保等领域的先进企业董事长、总经理、CIO、总监等高管参加培训。 本次高研班邀请到华为数字化转型专家陈劲、马兵东…

paddle实现获取pdf的内容

paddle实现获取pdf的内容 1. 环境安装2. 实现代码 源码链接 1. 环境安装 安装paddlepaddle gpu版本python -m pip install paddlepaddle-gpu -i https://pypi.tuna.tsinghua.edu.cn/simplecpu版本&#xff1a;python -m pip install paddlepaddle -i https://pypi.tuna.tsing…

NeRF知识点:不同Contraction的数学表达

NeRF知识点&#xff1a;不同Contraction的数学表达 目录 Foward-facing 场景&#xff1a;NDC&#xff08;Normalized Device Coordinate&#xff09;1 360&#xff0c;object-centric 场景&#xff1a; inverse-sphere warping1 MeRF: Piecewise-projective Contraction1 …

基于以太坊+IPFS的去中心化数据交易方法及平台

自己的论文&#xff0c;哎费事 目录 基于以太坊IPFS的去中心化数据交易方法及平台 基于以太坊IPFS的去中心化数据交易方法及平台 摘要&#xff1a; 数据交易过程中存在数据权属不明和数据安全问题。本文开发了一种基于以太坊IPFS的去中心化数据交易方法及平台。方法包括&am…

函数指针数组

前面学习过数组 指针数组&#xff1a;用来存放数组指针&#xff08;地址&#xff09;的数组 int main() {int arr1[] { 0 };int arr2[] { 0 };int arr3[] { 0 };int* p[3] { arr1,arr2,arr3 };//指针数组return 0; }那么函数指针数组&#xff0c;就是用来存放几个类型相同…

application.yml 或 application.properties 配置文件乱码问题

一、idea写application.yml配置文件&#xff0c; 用到了中文注释&#xff0c;写的时候好好地&#xff0c;但是运行项目后&#xff0c;出现乱码情况&#xff0c;如下&#xff1a; 二、解决方法 将文件编码方式 设置成UTF-8即可 File-Setting-Editor-File Encodings 再次运行项…

「2024」预备研究生mem-一元二次不等式恒成立高次、分次不等式(包含穿根法)

一、一元二次不等式恒成立 二、高次不等式&#xff08;包含穿根法&#xff09; 三、分次不等式 四、课后习题

蓝牙资讯|谷歌推迟上线Find My Device,需等到苹果同步跟进

谷歌今年 5 月初宣布推出“寻找我的设备”&#xff08;Find My Device Network&#xff09;&#xff0c;目标是为全球超过 10 亿台安卓设备服务&#xff0c;帮助用户寻找丢失的设备。 谷歌在今年 5 月宣布&#xff0c;Find My Device 网络将兼容 Tile、Pebblebee 和 Chipolo …

aws中opensearch 日志通(Centralized Logging with OpenSearch)2.0(一)

aws日志通2.0 实现全面的日志管理和分析功能 一体化日志摄取 &#xff1a;把aws服务器日志和应用日志传输到opensearch域中无代码日志处理 &#xff1a;在网页控制台中就可以实现数据处理开箱即用 &#xff1a;提供可视化模版&#xff08;nginx、HTTP server &#xff09; 架构…

方法的定义和格式

方法 什么是方法&#xff1f; 方法是程序中最小的执行单元 定义&#xff1a;把一些代码打包在一起&#xff0c;该过程称为方法 实际开发过程中&#xff0c;什么时候用到方法&#xff1a; 重复的代码&#xff0c;具有独立功能的代码可以抽取到方法中 实际开发中&#xff0c;方…

【容器编排】初识 Kubernetes

目录 1.简介 2.为什么需要 k8s 3.k8s 能做什么? 4.k8s 不是什么? 1.简介 摘取官网: 概述 | Kubernetes Kubernetes 这个名字源于希腊语&#xff0c;意为舵手或飞行员。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kub…

Vue3自定义封装音频播放器

&#x1f680; 本片主要讲解如何使用Vue3去封装一个音频播放器&#xff0c;以及解决在ios微信浏览器中 无法获取音频时长问题 一、效果图 二、解决在微信环境中ios无法获取音频时长问题 1、安装weixin-js-sdk cnpm i weixin-js-sdk --save2、使用&#xff0c;通过wx.ready中重…

【CAS6.6源码解析】深入解析TGT和ST的唯一ID是怎样生成的-探究ID生成器的设计

CAS作为一款企业级中央认证服务系统&#xff0c;其票据的生成是非常重要的一环&#xff0c;在票据的生成中&#xff0c;有一个比较重要的点就是为票据生成唯一ID&#xff0c;本文将深入解析CAS系统中的TGT和ST的唯一ID是怎样生成的。 文章重点分析源码的过程&#xff0c;不想看…