深入解析代理模式:静态代理、JDK 动态代理和 CGLIB 的全方位对比!

news2024/11/15 10:02:02

代理模式(Proxy Pattern)是一种结构型设计模式,它提供了对象的替身,即代理对象来控制对实际对象的访问。通过代理对象,可以在不修改目标对象的情况下,扩展控制其功能。例如,代理模式可以用于延迟加载权限控制日志记录等场景。

🎯 核心要点:

  • 代理对象:代理模式通过代理对象替代实际对象进行控制,代理对象和实际对象实现相同的接口。
  • 控制访问:代理对象可以控制客户端与实际对象的交互,甚至对客户端的请求进行预处理或后处理。
  • 延迟初始化:代理对象可以在需要的时候才创建实际对象,节省资源。

UML类图

在这里插入图片描述

Subject:这是接口,定义了代理对象和实际对象都要实现的公共接口,包含方法 request()

RealSubject:实现 Subject 接口的类,表示真正执行操作的对象。

Proxy:同样实现了 Subject 接口,代理 RealSubject 对象,控制对 RealSubject 的访问

静态代理

静态代理是指在编译期就已经确定了代理类。我们必须手动创建代理类,并明确代理哪个对象。代理类与被代理类实现相同的接口,通过代理类来控制对实际对象的访问。

静态代理案例:银行账户管理

假设我们有一个银行账户管理系统,用户通过 BankAccount 类管理账户余额,BankAccountProxy 作为代理类,添加了权限控制功能,只有拥有特定权限的用户才能执行账户操作。

案例场景

  • 实际对象BankAccount 负责执行账户的具体操作(如查询余额)。
  • 代理对象BankAccountProxy 负责控制对 BankAccount 的访问,确保只有权限用户可以操作账户。

静态代理代码实现

Step 1: 定义接口
// Subject 接口
public interface BankAccount {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}

Step 2: 实现具体的银行账户类
// RealSubject 实现类
public class RealBankAccount implements BankAccount {
    private double balance;

    public RealBankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    @Override
    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", new balance is " + balance);
    }

    @Override
    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", new balance is " + balance);
        } else {
            System.out.println("Insufficient funds.");
        }
    }

    @Override
    public double getBalance() {
        return balance;
    }
}

Step 3: 实现代理类
// Proxy 类
public class BankAccountProxy implements BankAccount {
    private RealBankAccount realBankAccount;
    private String userRole;

    public BankAccountProxy(RealBankAccount realBankAccount, String userRole) {
        this.realBankAccount = realBankAccount;
        this.userRole = userRole;
    }

    @Override
    public void deposit(double amount) {
        if (userRole.equals("Admin")) {
            realBankAccount.deposit(amount);
        } else {
            System.out.println("Access denied: You don't have permission to deposit.");
        }
    }

    @Override
    public void withdraw(double amount) {
        if (userRole.equals("Admin")) {
            realBankAccount.withdraw(amount);
        } else {
            System.out.println("Access denied: You don't have permission to withdraw.");
        }
    }

    @Override
    public double getBalance() {
        return realBankAccount.getBalance();
    }
}

Step 4: 测试代理类
public class Client {
    public static void main(String[] args) {
        // 创建真实对象和代理对象
        RealBankAccount realAccount = new RealBankAccount(1000);
        BankAccount proxyAccount = new BankAccountProxy(realAccount, "User");

        // 测试代理访问
        proxyAccount.deposit(500);  // 访问受限
        proxyAccount.withdraw(300); // 访问受限

        // 以 Admin 身份访问
        BankAccount adminAccount = new BankAccountProxy(realAccount, "Admin");
        adminAccount.deposit(500);  // 成功存款
        adminAccount.withdraw(300); // 成功取款
    }
}

输出结果

Access denied: You don't have permission to deposit.
Access denied: You don't have permission to withdraw.
Deposited 500.0, new balance is 1500.0
Withdrew 300.0, new balance is 1200.0
解释:
  • 权限控制BankAccountProxy 控制了对 RealBankAccount 的访问,只有拥有 Admin 权限的用户才能操作账户。
  • 灵活扩展:通过代理类,我们可以在不修改 RealBankAccount 的前提下,灵活地添加权限控制功能。

动态代理(JDK 动态代理)

动态代理是在运行时动态生成代理类,而不是在编译时确定。动态代理可以通过反射机制自动生成代理对象,而无需手动编写代理类。

动态代理案例:银行账户管理(JDK 动态代理)

动态代理 中,代理类是在运行时动态生成的。Java 提供了 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实现动态代理。

案例场景

和静态代理案例类似,我们还是使用 BankAccount 管理账户,但是通过 JDK 动态代理 来动态生成代理类,代理类控制用户的操作权限,并记录日志。

动态代理代码实现

Step 1: 定义接口(与静态代理相同)
// Subject 接口
public interface BankAccount {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}
Step 2: 实现具体的银行账户类(与静态代理相同)
// RealSubject 实现类
public class RealBankAccount implements BankAccount {
    private double balance;

    public RealBankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    @Override
    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", new balance is " + balance);
    }

    @Override
    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", new balance is " + balance);
        } else {
            System.out.println("Insufficient funds.");
        }
    }

    @Override
    public double getBalance() {
        return balance;
    }
}

Step 3: 实现 InvocationHandler 接口

InvocationHandler 是动态代理的核心,通过 invoke() 方法拦截对目标对象的方法调用。

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

public class BankAccountInvocationHandler implements InvocationHandler {
    private Object target;
    private String userRole;

    public BankAccountInvocationHandler(Object target, String userRole) {
        this.target = target;
        this.userRole = userRole;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (userRole.equals("Admin")) {
            System.out.println("Admin access granted.");
            return method.invoke(target, args);  // 调用目标对象的方法
        } else {
            System.out.println("Access denied: You don't have permission to " + method.getName());
            return null;
        }
    }
}

Step 4: 动态代理测试
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        // 创建真实对象
        RealBankAccount realAccount = new RealBankAccount(1000);

        // 创建动态代理对象
        BankAccount proxyAccount = (BankAccount) Proxy.newProxyInstance(
            realAccount.getClass().getClassLoader(),
            new Class[]{BankAccount.class},
            new BankAccountInvocationHandler(realAccount, "User")
        );

        // 测试代理访问
        proxyAccount.deposit(500);  // 访问受限
        proxyAccount.withdraw(300); // 访问受限

        // 以 Admin 身份访问
        BankAccount adminAccount = (BankAccount) Proxy.newProxyInstance(
            realAccount.getClass().getClassLoader(),
            new Class[]{BankAccount.class},
            new BankAccountInvocationHandler(realAccount, "Admin")
        );

        adminAccount.deposit(500);  // 成功存款
        adminAccount.withdraw(300); // 成功取款
    }
}

输出结果

Access denied: You don't have permission to deposit
Access denied: You don't have permission to withdraw
Admin access granted.
Deposited 500.0, new balance is 1500.0
Admin access granted.
Withdrew 300.0, new balance is 1200.0

解释

  • 运行时生成代理类:通过 Proxy.newProxyInstance() 方法,动态生成代理类。
  • 权限控制:动态代理可以在运行时灵活地进行权限控制,且不需要手动创建代理类。

CGLIB 动态代理

通过生成目标类的子类,并重写其中的方法来实现代理。它是在运行时生成的字节码,所以可以代理普通类和接口。代理类实际上是目标类的子类,并且会调用父类的方法。

  • 依赖:需要导入 cglib 相关的库。
  • 限制:由于 CGLIB 是通过继承实现的,所以不能代理 final或**final 方法**,因为这些无法被继承和重写。

CGLIB 依赖导入

在项目中,你需要下载CGLIB相关的所有JAR包,或者使用 MavenGradle 导入 cglib 依赖

Jar包下载地址:相关JAR点击下载

Maven 依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

Gradle 依赖

implementation 'cglib:cglib:3.3.0'

案例场景:银行账户管理(CGLIB 动态代理)

我们将基于前面的银行账户管理系统,使用 CGLIB 实现动态代理,控制用户操作权限并记录日志。

场景:

  • 实际对象BankAccount 是一个普通类,没有实现任何接口。
  • 代理对象:使用 CGLIB 动态生成代理类,实现权限控制和日志功能

CGLIB 动态代理代码实现

Step 1: 创建 BankAccount

不再实现接口,这是一个普通类,CGLIB 可以代理这个类。

// RealSubject 实现类,普通类,没有实现接口
public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        balance += amount;
        System.out.println("Deposited " + amount + ", new balance is " + balance);
    }

    public void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Withdrew " + amount + ", new balance is " + balance);
        } else {
            System.out.println("Insufficient funds.");
        }
    }

    public double getBalance() {
        return balance;
    }
}

Step 2: 创建 MethodInterceptor 实现类

MethodInterceptor 是 CGLIB 代理的核心,通过重写 intercept() 方法来拦截目标类的方法调用

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

import java.lang.reflect.Method;

public class BankAccountMethodInterceptor implements MethodInterceptor {
    private String userRole;

    public BankAccountMethodInterceptor(String userRole) {
        this.userRole = userRole;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (userRole.equals("Admin")) {
            System.out.println("Admin access granted.");
            return proxy.invokeSuper(obj, args);  // 调用父类的原方法
        } else {
            System.out.println("Access denied: You don't have permission to " + method.getName());
            return null;
        }
    }
}

Step 3: 使用 Enhancer 动态生成代理类

CGLIB 使用 Enhancer 类来生成代理对象,Enhancer 会生成一个目标类的子类,并将方法调用委托给 MethodInterceptor

import net.sf.cglib.proxy.Enhancer;

public class Client {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(BankAccount.class);  // 设置代理目标类
        enhancer.setCallback(new BankAccountMethodInterceptor("User"));  // 设置拦截器

        // 创建代理对象
        BankAccount proxyAccount = (BankAccount) enhancer.create(new Class[]{double.class}, new Object[]{1000.0});

        // 测试代理访问
        proxyAccount.deposit(500);  // 访问受限
        proxyAccount.withdraw(300); // 访问受限

        // 以 Admin 身份访问
        enhancer.setCallback(new BankAccountMethodInterceptor("Admin"));
        BankAccount adminAccount = (BankAccount) enhancer.create(new Class[]{double.class}, new Object[]{1000.0});

        adminAccount.deposit(500);  // 成功存款
        adminAccount.withdraw(300); // 成功取款
    }
}

输出结果

Access denied: You don't have permission to deposit
Access denied: You don't have permission to withdraw
Admin access granted.
Deposited 500.0, new balance is 1500.0
Admin access granted.
Withdrew 300.0, new balance is 1200.0

解释

  • 动态生成代理类:通过 Enhancer 类,动态生成了 BankAccount 类的代理对象。
  • 权限控制MethodInterceptor 控制了对目标方法的调用,只有具有 Admin 权限的用户才能执行操作。
  • 日志功能:代理类在执行目标方法前,打印日志信息。

CGLIB 动态代理的优缺点

优点

  1. 支持无接口类的代理:CGLIB 能够代理普通类,不要求目标类必须实现接口,这比 JDK 动态代理更灵活。
  2. 性能高:相比 JDK 动态代理,CGLIB 生成的代理类性能更高,尤其是在大量调用代理方法的场景下。
  3. 透明性:客户端无需修改,代理类的生成是透明的。

缺点

  1. 不能代理 final 类和方法:由于 CGLIB 代理是通过生成子类实现的,因此无法代理 final 类和 final 方法。
  2. 依赖第三方库:CGLIB 是一个外部库,增加了项目的依赖复杂度。

总结:CGLIB 动态代理的特点

  • 代理普通类:CGLIB 允许代理没有实现接口的类,这比 JDK 动态代理更加灵活。
  • 通过继承实现代理:CGLIB 生成目标类的子类,并重写目标方法来实现代理。
  • 应用场景广泛:CGLIB 动态代理适用于需要代理普通类、且调用频繁的场景。

🎯 Spring AOP 代理机制

Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架中的核心特性之一,它通过代理对象来对目标对象的方法进行拦截,在方法执行前后加入额外的逻辑,如日志记录、权限验证、事务管理等。

Spring AOP 中使用了两种代理机制:

  1. JDK 动态代理(基于接口的代理)
  2. CGLIB 动态代理(基于子类的代理)

使用的代理类型

  • JDK 动态代理:当目标类实现了接口时,Spring AOP 默认使用 JDK 动态代理来生成代理对象。
  • CGLIB 动态代理:当目标类没有实现接口时,Spring AOP 会使用 CGLIB 来生成代理对象。

Spring AOP 如何选择代理机制

Spring 选择代理的规则
  1. 如果目标对象实现了接口,Spring AOP 默认使用 JDK 动态代理
  2. 如果目标对象没有实现接口,Spring AOP 使用 CGLIB 动态代理
  3. 可以强制使用 CGLIB:即使目标对象实现了接口,也可以通过配置来强制使用 CGLIB 代理。

Spring AOP 动态代理应用场景

Spring AOP 的代理机制广泛应用于以下场景:

  1. 事务管理:通过代理对象,在方法执行时自动管理事务的开启和提交。
  2. 日志记录:在方法执行前后自动添加日志记录。
  3. 权限控制:通过代理对象,控制用户是否有权限调用某些方法。
  4. 缓存机制:在方法执行前,先检查缓存,如果缓存中存在结果则直接返回,否则执行目标方法并将结果存入缓存

三种代理类型的对比

下表详细对比了 静态代理JDK 动态代理CGLIB 动态代理 的不同点。

对比维度静态代理JDK 动态代理CGLIB 动态代理
实现方式手动创建代理类通过 Proxy 类和 InvocationHandler 动态生成通过继承目标类,使用字节码生成子类
是否需要接口是,需要代理类和目标类实现相同接口是,必须代理实现了接口的类否,不需要接口,直接代理类本身
代理类生成时间编译时生成,代码已确定运行时动态生成运行时动态生成
实现复杂度需要手动编写代理类,代码重复较简单,自动生成代理类复杂度较高,需要字节码生成库
方法调用方式代理类直接调用目标类方法代理对象通过 InvocationHandler 反射调用目标方法通过字节码技术生成子类,直接调用父类方法
代理性能性能较好,方法直接调用性能较差,基于反射调用,反射开销大性能较高,生成的子类直接调用父类方法
代理对象结构代理对象和目标对象有相同的接口代理对象和目标对象实现相同接口代理对象是目标类的子类
应用场景适用于代理数量少、简单的场景适用于需要代理实现接口的场景适用于没有实现接口的类,或者需要大量代理的场景
是否可代理 final否,无法代理 final否,无法代理 final
优点实现简单,直观灵活,可以代理接口,易于扩展可代理普通类,性能较高,适用于没有接口的类
缺点需要为每个目标类手动编写代理类,代码冗余只能代理接口,基于反射调用性能较低无法代理 final 类,依赖外部库,配置较复杂

总结:三种代理的特点和适用场景

  1. 静态代理
    • 特点:代理类由开发者手动编写,固定且已确定,代码较多、可维护性较低。
    • 适用场景:代理类少、功能固定的场景,简单、容易实现。
  2. JDK 动态代理
    • 特点:只能代理实现了接口的类,通过反射调用目标方法,代理类在运行时生成,性能相对较低。
    • 适用场景:目标对象实现了接口,尤其是需要动态代理多个接口的场景,如日志记录、权限控制等。
  3. CGLIB 动态代理
    • 特点:不要求目标类必须实现接口,使用字节码生成技术生成目标类的子类,性能高于 JDK 动态代理,但无法代理 final 类和 final 方法。
    • 适用场景:目标类没有实现接口,且代理调用频繁时使用,尤其适合对普通类的代理

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

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

相关文章

JDBC的介绍和连接MySQL数据库

目录 1. 为什么学习JDBC 1.1 数据存储​编辑​编辑 1.2 数据操作​编辑 2. JDBC概述 2.1 JDBC概念 2.2 JDBC 核心组成 3. 实现 JDBC 3.1 JDBC 搭建步骤 3.2 详细演示 3.3 核心API 3.3.1 Driver​ 3.3.2 Connection​ 3.3.3 Statament​ 3.3.4 PreparedStatement …

嵌入式单片机中can总线调试方法

大家好,今天将向大家介绍如何使用STM32F4自带的CAN控制器实现两个开发板之间的CAN通信。 1.CAN CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO 11898),是国际上应用最广泛的…

大模型笔记03--快速体验dify

大模型笔记03--快速体验dify 介绍部署&测试部署 dify测试dify对接本地ollama大模型对接阿里云千问大模型在个人网站中嵌入dify智能客服 注意事项说明 介绍 Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;…

优化 OR 条件过多导致的查询超时

优化 OR 条件过多导致的查询超时 文章目录 优化 OR 条件过多导致的查询超时背景问题分析方案分析方案一&#xff1a;入参去重方案二&#xff1a;分页或者分批查询方案三&#xff1a;UNION 代替 OR方案四&#xff1a;IN 代替 OR1. 分别对列进行 IN 查询&#xff0c;在代码中进行…

同一Python脚本中训练多个模型时的 wandb 配置错误解决方案

文章目录 摘要背景介绍报错信息wandb 模型训练名 摘要 在机器学习项目中&#xff0c;使用Python脚本训练多个模型时&#xff0c;可能会遇到WandB&#xff08;Weights and Biases&#xff09;配置错误&#xff0c;尤其是在训练多个模型参数大小不一致的情况下。 本文将介绍如何…

Vue学习记录之三(ref全家桶)

ref、reactive是在 setup() 声明组件内部状态用的&#xff0c; 这些变量通常都要 return 出去&#xff0c;除了供 < template > 或渲染函数渲染视图&#xff0c;也可以作为 props 或 emit 参数 在组件间传递。它们的值变更可触发页面渲染。 ref &#xff1a;是一个函数&…

Get包中的根组件

文章目录 1. 知识回顾2. 使用方法2.1 源码分析2.2 常用属性 3. 示例代码4. 内容总结 我们在上一章回中介绍了"Get包简介"相关的内容&#xff0c;本章回中将介绍GetMaterialApp组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 知识回顾 我们在上一章回中已经…

Unity webgl跨域问题 unity使用nginx设置跨域 ,修改请求头

跨域 什么是跨域 跨域是指浏览器因安全策略限制&#xff0c;阻止一个域下的网页访问另一个域下的资源。 一些常见的跨域情况&#xff1a; 协议不同 从 http://example.com 请求 https://example.com。域名不同 从 http://example.com 请求 http://anotherdomain.com。端口不…

Village Exteriors Kit 中世纪乡村房屋场景模型

此模块化工具包就是你一直在寻找的适合建造所有中世纪幻想村庄和城市建筑所需要的工具包。 皇家园区 - 村庄外饰套件的模型和纹理插件资源包 酒馆和客栈、魔法商店、市政大厅、公会大厅、布莱克史密斯锻造厂、百货商店、珠宝商店、药店、草药师、银行、铠甲、弗莱切、马厩、桌…

list从0到1的突破

目录 前言 1.list的介绍 2.list的常见接口 2.1 构造函数&#xff08; (constructor)&#xff09; 接口说明 2.2 list iterator 的使用 2.3 list capacity 2.4 list element access 2.5 list modifiers 3.list的迭代器失效 附整套练习源码 结束语 前言 前面我们学习…

Defining Constraints with ObjectProperties

步骤4&#xff1a;使用对象定义约束 物业 您可以创建时间和放置约束&#xff0c;如本教程所示。你也可以 更改单元格的属性以控制Vivado实现如何处理它们。许多 物理约束被定义为单元对象的属性。 例如&#xff0c;如果您在设计中发现RAM存在时序问题&#xff0c;为了避免重新合…

C语言代码练习(第二十六天)

今日练习&#xff1a; 数据的交换输出输入 n 个数&#xff0c;找出其中最小的数&#xff0c;将它与最前面的数交换后输出这些数 输入一个英文句子&#xff0c;将每个单词的第一个字母改成大写字母 输入一个十进制数 N &#xff0c;将它转换成 R 进制数输出 数据的交换输出输入 …

阿里OSS对象存储服务,实现图片上传回显

阿里OSS对象存储服务 OSS服务1. 创建buckte2. 获取accesskey3. 参照官方SDK编写程序安装SDK 4. 程序编写5. 封装6. 在spring中调用 OSS服务 阿里云对象存储 OSS&#xff08;Object Storage Service&#xff09;是一款海量、安全、低成本、高可靠的云存储服务&#xff0c;提供最…

利用JS数组根据数据生成柱形图

要求 <html> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document…

精准识别,高效管理:工服识别AI检测算法在多场景中的应用优势

随着人工智能技术的快速发展&#xff0c;其在各个行业的应用也日益广泛。特别是在工业生产和安全监管领域&#xff0c;工服识别AI检测算法凭借其高效、精准的特点&#xff0c;成为提升生产效率、保障工作人员安全的重要手段。本文将详细介绍TSINGSEE青犀AI智能分析网关V4工服识…

Hibernate基础

Hibernate基础总结 有利的条件和主动的恢复产生于再坚持一下的努力之中&#xff01; 好久没更新了&#xff0c;今天入门了Hibernate&#xff0c;由于之前学习了MyBatis&#xff0c;初步感觉二者的底层实现思想有很多相似之处&#xff0c;下面让我们以一个入门Demo的形式感受一…

3.Java高级编程实用类介绍(一)

三、Java高级编程实用类介绍(一) 文章目录 三、Java高级编程实用类介绍(一)一、枚举类型二、包装类三、Math 一、枚举类型 使用enum进行定义 public enum 枚举名字{值1,值2.... }二、包装类 每个基本类型在java.lang包中都有一个相应的包装类 /** new包装类&#xff08;字符…

【C++笔记】类和对象的深入理解(三)

【C笔记】类和对象的深入理解(三) &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】类和对象的深入理解(三)前言一.日期类的实现1.1声明和定义分离1.2日期类整数1.3日期类整数1.4日期类-整数1.5日期类-日期1.6复用对…

并发安全与锁

总述 这篇文章&#xff0c;我想谈一谈自己对于并发变成的理解与学习。主要涉及以下三个部分&#xff1a;goroutine&#xff0c;channel以及lock 临界区 首先&#xff0c;要明确下面两组概念 并发和并行 并行&#xff1a;指几个程序每时每刻都同时进行 并发&#xff1a;指…

lnmp - 登录技术方案设计与实现

概述 登录功能是对于每个动态系统来说都是非常基础的功能&#xff0c;用以区别用户身份、和对应的权限和信息&#xff0c;设计出一套安全的登录方案尤为重要&#xff0c;接下来我介绍一下常见的认证机制的登录设计方案。 方案设计 HTTP 是一种无状态的协议&#xff0c;客户端…