一文总结代理:代理模式、代理服务器

概述

代理在计算机编程领域,是一个很通用的概念,包括:代理设计模式,代理服务器等。

代理类持有具体实现类的实例,将在代理类上的操作转化为实例上方法的调用。为某个对象提供一个代理,以控制对这个对象的访问。代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。为其他对象提供一种代理以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或者增加额外的功能。

角色

代理模式一般涉及到的角色有:

  • 对象:Client,请求客户端
  • 抽象角色:Subject,声明真实对象和代理对象的共同接口,对应代理接口;
  • 真实角色:RealSubject,代理角色所代表的真实对象,是最终要引用的对象,对应委托类;
  • 代理角色:Proxy,代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装,对应代理类,可以有未公开的方法。

UML类图如下:
在这里插入图片描述
调用顺序示意图:
在这里插入图片描述

分类

代理从类型来分类:

  • 虚代理:虚拟代理,Virtual Proxy,根据需要来创建开销很大的对象,该对象只有在需要时才会被真正创建,即所谓的延迟加载;
  • 远程代理:Remote Proxy,用来在不同的地址空间上代表同一个对象,这个不同的地址空间可以是在本机,也可以在其它机器上,在Java里面最典型的就是RMI技术;
  • copy-on-write代理:在客户端操作时,只有对象确实改变后,才会真的拷贝一个日标对象,算是虚代理的一个分支;
  • 保护代理:Protect Proxy,控制对原始对象的访问,如果有需要,可以给不同的用户提供不同的访问权限,以控制他们对原始对象的访问;
  • Cache代理:为那些昂贵操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果;
  • 防火墙代理:保护对象不被恶意用户访问和操作;
  • 同步代理:使多个用户能够同时访问目标对象而没有冲突;
  • 智能引用代理:Smart Reference Proxy,在访问对象时执行一些附加操作。比如,对指向实际对象的引用计数、第一次引用一个持久对象时,将它装入内存等

另外,根据代理类的生成时间的不同,可分为静态代理和动态代理。

静态代理

代理和被代理对象(目标对象)在代理之前是确定的,都实现相同的接口或者继承相同的抽象类。目标对象作为代理对象的一个属性,具体接口实现中,代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态,在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定。

实现静态代理有继承、聚合方法。

优点:业务类只需要关注业务逻辑本身,保证业务类的重用性。

缺点:

  1. 需要大量硬编码
  2. 一个代理类只能代理一个业务类
  3. 如果业务类增加方法时,相应的代理类也要增加方法

动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。优势在于可以很方便的对代理类的函数进行统一的处理,添加方法调用次数、添加日志功能等,而不用修改每个代理类的函数。分为JDK动态代理和cglib动态代理。

实现

一般有JDK和cglib等实现方式。

JDK

基于反射,核心API包括:
java.lang.reflect.Proxy,Java动态代理机制生成的所有动态代理类的父类,提供一组静态方法来为一组接口动态地生成代理类及其对象,共4个static方法:

public class Proxy implements java.io.Serializable {// 用于获取指定代理对象所关联的调用处理器public static InvocationHandler getInvocationHandler(Object proxy)// 用于获取关联于指定类装载器和一组接口的动态代理类的类对象public static class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)// 用于判断指定类对象是否是一个动态代理类public static boolean isProxyClass(Class<?> cl)// 用于为指定类装载器、一组接口及调用处理器生成动态代理类实例public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
}

InvocationHandler接口主要用来处理执行逻辑,源码:

public interface InvocationHandler {// obj指代理类,method指被代理的方法,args为该参数的方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

使用JDK动态代理的步骤

  1. 创建被代理的类以及接口;
  2. 创建一个实现接口InvocationHandler,并重写invoke方法的类;
  3. 调用Proxy的静态方法,创建代理类newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
  4. 通过代理调用方法。

实例

代表Subject的接口:

public interface Movable {void move();
}

Car实现Movable接口,是要代理的实际对象,对应RealSubject:

@Slf4j
public class Car implements Movable {@Overridepublic void move() {// 实现开车try {Thread.sleep(new Random().nextInt(1000));System.out.println("汽车行驶中....");} catch (InterruptedException e) {log.error("thread fail", e);}}
}

对应于Proxy的TimeHandler类实现InvocationHandler接口,并在invoke()方法中添加额外的逻辑,用于在代理对象方法调用前后执行:

public class TimeHandler implements InvocationHandler {private final Object target;TimeHandler(Object target) {super();this.target = target;}/*** 参数:* proxy:被代理对象* method:被代理对象的方法* args:方法的参数* res:方法的返回值*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();System.out.println("汽车开始行驶....");Object res = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("汽车结束行驶....  汽车行驶时间:" + (endTime - startTime) + "毫秒!");return res;}
}

JDK动态代理测试类,也就是上图中的Client类:

public class Test {public static void main(String[] args) {Car car = new Car();InvocationHandler h = new TimeHandler(car);Class<?> cls = car.getClass();/** loader:类加载器* interfaces:实现接口* h:InvocationHandler*/Movable m = (Movable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);m.move();}
}

使用Proxy.newProxyInstance()方法创建代理对象,指定Movable接口作为代理对象类型,并将TimeHandler对象作为代理对象的InvocationHandler。

缺点:

  1. 仍有硬编码
  2. 需要在对象初始化时,使用特定的方式进行初始化

源码分析

基于JDK-22,以Proxy.newProxyInstance()方法为切入点:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {Objects.requireNonNull(h);@SuppressWarnings("removal")final Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();// 查找或生成指定的代理类及其构造函数Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);
}

基于源码的注释,进一步查看getProxyConstructor代码:

private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {// optimization for single interfaceif (interfaces.length == 1) {Class<?> intf = interfaces[0];if (caller != null) {checkProxyAccess(caller, loader, intf);}return proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build());} else {// interfaces clonedfinal Class<?>[] intfsArray = interfaces.clone();if (caller != null) {checkProxyAccess(caller, loader, intfsArray);}final List<Class<?>> intfs = Arrays.asList(intfsArray);return proxyCache.sub(intfs).computeIfAbsent(loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build());}
}

核心方法是构建ProxyBuilder实例,ProxyBuilder是java.lang.reflect.Proxy的静态内部类,利用构造器模式:

ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {// 在模块系统完全初始化之前不支持代理if (!VM.isModuleSystemInited()) {throw new InternalError("Proxy is not supported until module system is fully initialized");}// 接口数不得超过65535个if (interfaces.size() > 65535) {throw new IllegalArgumentException("interface limit exceeded: " interfaces.size());}Set<Class<?>> refTypes = referencedTypes(loader, interfaces);// IAE if violates any restrictions specified in newProxyInstancevalidateProxyInterfaces(loader, interfaces, refTypes);this.interfaces = interfaces;this.context = proxyClassContext(loader, interfaces, refTypes);assert getLoader(context.module()) == loader;
}

核心方法之一,referencedTypes检查是否为static方法:

private static Set<Class<?>> referencedTypes(ClassLoader loader, List<Class<?>> interfaces) {var types = new HashSet<Class<?>>();for (var intf : interfaces) {for (Method m : intf.getMethods()) {// 不能为static方法if (!Modifier.isStatic(m.getModifiers())) {addElementType(types, m.getReturnType());addElementTypes(types, m.getSharedParameterTypes());addElementTypes(types, m.getSharedExceptionTypes());}}}return types;
}

validateProxyInterfaces方法检查是否是接口,

private static void validateProxyInterfaces(ClassLoader loader, List<Class<?>> interfaces, Set<Class<?>> refTypes) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());for (Class<?> intf : interfaces) {// 检查是否为接口if (!intf.isInterface()) {throw new IllegalArgumentException(intf.getName() + " is not an interface");}// 检查是否为隐藏类:JDK15引入新特性if (intf.isHidden()) {throw new IllegalArgumentException(intf.getName() + " is a hidden interface");}// 检查是否是密封类:JDK17引入新特性if (intf.isSealed()) {throw new IllegalArgumentException(intf.getName() + " is a sealed interface");}// 验证类加载器是否将此接口的名称解析为同一个Class对象,下同ensureVisible(loader, intf);// 检查是否已经生成过此接口的代理类if (interfaceSet.put(intf, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + intf.getName());}}for (Class<?> type : refTypes) {ensureVisible(loader, type);}
}

为什么JDK动态代理只能代理接口

面试时常见的问题之一,参考上面的源码分析,实际上还可以补充回答:JDK动态代理对密封类,隐藏类不生效,即不能代理密封类,隐藏类。

cglib

Code Generation Library,一款高性能Code生成类库的开源组件,可在运行期扩展Java类与实现Java接口,很多其他开源组件都在使用cglib

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调类型,经常被基于代理的AOP用来实现拦截方法的调用,接口只定义一个方法:

public interface MethodInterceptor extends Callback {// object是代理对像,method是拦截方法,args是方法参数。原来的方法可通过使用Method对象的一般反射调用,或使用MethodProxy对象调用,后者更快Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}	

实例

创建代理类:

public class CglibProxy implements MethodInterceptor {private final Enhancer enhancer = new Enhancer();Object getProxy(Class<?> clazz) {// 设置创建子类的类enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}/*** 拦截所有目标类方法的调用*/@Overridepublic Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("日志开始...");// 代理类调用父类的方法Object res = proxy.invokeSuper(obj, args);System.out.println("日志结束...");return res;}
}

测试类:

public class Client {public static void main(String[] args) {CglibProxy proxy = new CglibProxy();Train t = (Train) proxy.getProxy(Train.class);t.move();}static class Train {void move() {System.out.println("火车行驶中...");}}
}

源码分析

基于cglib最新版3.3.0来分析,从enhancer.create()入手,调用createHelper方法:

private Object createHelper() {// 校验callbackTypes、filter是否为空,及相应处理策略preValidate();Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,ReflectUtils.getNames(interfaces),filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),callbackTypes,useFactory,interceptDuringConstruction,serialVersionUID);this.currentKey = key;Object result = super.create(key);return result;
}

核心方法是create:

protected Object create(Object key) {try {ClassLoader loader = getClassLoader();// 查询缓存Map<ClassLoader, ClassLoaderData> cache = CACHE;ClassLoaderData data = cache.get(loader);// DCLif (data == null) {synchronized (AbstractClassGenerator.class) {cache = CACHE;data = cache.get(loader);if (data == null) {// 构建缓存Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);data = new ClassLoaderData(loader);newCache.put(loader, data);CACHE = newCache;}}}this.key = key;Object obj = data.get(this, getUseCache());if (obj instanceof Class) {// 用于向后兼容return firstInstance((Class) obj);}// 真正创建代理对象return nextInstance(obj);} catch (RuntimeException e) {throw e;} catch (Error e) {throw e;} catch (Exception e) {throw new CodeGenerationException(e);}
}

不管是firstInstance还是nextInstance,最后都是调用ReflectUtils.newInstance方法:

public static Object newInstance(final Constructor cstruct, final Object[] args) {boolean flag = cstruct.isAccessible();try {if (!flag) {cstruct.setAccessible(true);}// 使用JDKreturn cstruct.newInstance(args);} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {throw new CodeGenerationException(e);} finally {if (!flag) {cstruct.setAccessible(flag);}}
}

最后使用JDK源码Constructor.newInstance(args);,因此cglib不能对声明为final的方法进行代理,因为cglib原理是动态生成被代理类的子类。

区别

主要区别:

  • JDK:利用拦截器(实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;
  • cglib:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

具体来说:
JDK动态代理只能针对实现接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。

cglib是针对类实现代理,主要是对指定的目标类生成一个子类(没有实例化一个类),覆盖其中的方法,通过方法拦截技术拦截所有父类方法的调用。

使用区别:

  • JDK不能用于非接口类、隐藏类、(未经允许扩展的)密封类(的子类)
  • cglib不能用于final方法、隐藏类、同上

Spring AOP

Spring AOP基于JDK Proxy和cglib来生成代理对象,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认策略是如果目标类是接口,则使用JDK动态代理,如果目标对象没有实现接口,则默认会采用cglib代理。

代理模式将继承模式和关联模式结合在一起使用,是两者的综合体,不过这个综合体的作用倒不是解决对象注入的问题,而是为具体操作对象找到一个保姆或者是秘书,对外代表具体的实例对象,实例对象的入口和出口都是通过这个二号首长,具体的实例对象是一号首长,一号首长是要干大事的,所以一些事务性,重复性的工作例如泡茶,安排车子,这样的工作是不用劳烦一号首长的大驾,而是二号首长帮忙解决的,这就是AOP的思想。AOP解决程序开发里事务性,和核心业务无关的问题,但这些问题对于业务场景的实现是很有必要的,在实际开发里AOP也是节省代码的一种方式。

AOP将应用系统分为两部分,核心业务逻辑(Core business concerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,大中型应用都会涉及到的持久化、事务、安全、日志和调试等。

实现AOP的技术,主要分为两大类:

  • 动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
  • 静态织入的方式,引入特定的语法创建Aspect,从而使得编译器可以在编译期间织入有关Aspect代码。

拓展

代理服务器

一般有正向代理、反向代理。

正向代理

一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。如通过Chrome的SwitchSharp访问外网。

反向代理

反向代理:以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。这个服务器没有保存任何网页的真实数据,所有的静态网页或者CGI程序,都保存在内部的Web服务器上。因此对反向代理服务器的攻击并不会使得网页信息遭到破坏,这样就增强Web服务器的安全性。

反向代理经常和CDN一起工作,其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置反向代理节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。

区别

正向代理解决的是客户端访问互联网的问题,客户端知道目标的;
反向代理解决的是互联网收到客户端请求,如何把请求转到内网服务器的问题,不知道目标,代理的是服务端。

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

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

相关文章

本地部署Graphhopper路径规划服务(graphhopper.sh启动版)

文章目录 文章参考源码获取一、配置Java环境变量二、配置Maven环境变量三、构建graphhopper步骤1. 下载数据2. 配置graphhopper配置文件config-example.yml3. 在项目中启动命令行执行./graphhopper.sh build3.1|、遇到的问题3.1.1、pom.xml中front-maven-plugin-无法下载npm6.1…

linux nginx 命令记录,和转发

nginx: 查看配置文件&#xff1a;sudo find / -name nginx.conf 配置文件&#xff1a;/etc/nginx/nginx.conf 检查nginx.conf文件正确性 nginx -t -c /path/to/nginx.conf 或者 有nginx命令执行 nginx -t 查找nginx 可执行文件&#xff1a;which nginx /usr/sbin/nginx 安装Ng…

C语言中内存四区的本质分析

数据类型本质分析 1 数据类型的概念 “类型”是对数据的抽象 类型相同的数据有相同的表示形式、存储格式以及相关的操作 程序中使用的所有数据都必定属于某一种数据类型 2 数据类型的本质 数据类型可理解为创建变量的模具&#xff1a;是固定内存大小的别名。 数据类型的作…

【cuda】在老服务器上配置CUDA+cmake开发环境

在老服务器上配置CUDA+cmake开发环境 服务器x86_64,系统是centos8,cmake版本是2.8.10 背景 不能更换服务器系统无法下载CUDA安装包解决思路 使用可以至此CUDA开发的较老的cmake直接移植CUDA环境配置环境中遇到的问题 服务器无法编译cmake移植CUDA编译器及部分库,代码无法…

vue3 使用Mock

官网: http://mockjs.com/ 安装 npm install mockjs -Dsteps1: main.js 文件引入 import /api/mock.jssteps2: src/api/mock.js import Mock from mockjs import homeApi from ./mockData/home /*** 1.拦截的路径:mock拦截了正常NetWork/网络请求,数据正常响应* 2.方法* …

PVE环境中调整虚拟机磁盘大小

我的希望将PVE中的虚拟机磁盘调整一下&#xff0c;增加20GB。在查询了一些资料后&#xff0c;做一下总结教程。 环境是 PVE8.2.2 版本&#xff0c;虚拟机系统是centos7.9.2009-minimal&#xff0c; 安装系统时划分磁盘分区方式是默认分区方式&#xff08;不同分区方式下&#…

Redis的相关基础了解

1. 什么是nosql nosql【not only sql】不仅仅是sql。所有非关系型数据库的统称&#xff0c;除去关系型数据库之外都是非关系数据库 2. NOSQL和RDBMS的区别 RDBMS——关系型数据库的通常 高度组织化 结构化 数据结构化查询语言(SQL) sql语句数据和关系都存储在单独的表中数据操纵…

八、桥接模式

文章目录 1 基本介绍2 案例2.1 OperatingSystem 抽象类2.2 LinuxOS 类2.3 WindowsOS 类2.4 FileOperation 类2.5 FileAppender 类2.6 FileReplicator 类2.7 Client 类2.8 Client 类运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 Implementor ( 实现者 )3.1.2 ConcreteImpl…

ARTMO Table ‘db1.test_mla_result‘ doesn‘t exist解决方案

com.mysql.jdbc.JDBC4PreparedStatement3f3c966c: describe test_mla_result; Java exception occurred: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table db1.test_mla_result doesnt exist解决方案&#xff1a; 打开MySQL的command Line, 输入SHOW TABLES…

基于微信小程序的高校排课系统 /基于微信小程序的排课管理系统/课程管理系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

Vue2和Vue3实战代码中的小差异(实时更新)

目录 前言1. 未使用自闭合标签2. 事件名连字符3. 换行符4. 弃用.sync 前言 以下文章实时更新&#xff0c;主打记录差异 1. 未使用自闭合标签 104:7 error Require self-closing on Vue.js custom components (<el-table-column>) vue/html-self-closing✖ 1 problem…

Canal监听Mysql回写Redis

目录 一、canal服务端 1.1 下载 1.2 解压 1.3 配置 1.4 启动 1.5 查看 二、canal客户端&#xff08;Java编写业务程序&#xff09; 2.1 SQL脚本 2.2 写POM 2.3 写Yaml 2.4 写业务类 2.4.1.项目结构 2.4.2 Utils.RedisUtil 2.4.3 biz.RedisCanalClientExample 一、…

麦田物语第十五天

系列文章目录 麦田物语第十五天 文章目录 系列文章目录一、构建游戏的时间系统二、时间系统 UI 制作总结 一、构建游戏的时间系统 在该游戏中我们要构建年月日天时分秒等时间的概念&#xff0c;从而实现季节的更替&#xff0c;昼夜的更替等&#xff08;不同的季节可以播种不同…

基于gaussian计算NICS值评估分子体系的芳香性和反芳香性

计算分子NICS值的基本流程 其中第1行的Bq即为NICS(0)虚原子对应的位置&#xff0c;7.5538为NICS(0)对应虚原子位置处的各向同性化学位移屏蔽值。 由于NICS值为各向同性化学屏蔽值的负值&#xff0c;因此苯的NICS(0)和NICS(1)分别为-7.5538和-10.5301&#xff0c;这也表明苯分…

通信类IEEE会议——第四届通信技术与信息科技国际学术会议(ICCTIT 2024)

[IEEE 独立出版&#xff0c;中山大学主办&#xff0c;往届均已见刊检索] 第四届通信技术与信息科技国际学术会议&#xff08;ICCTIT 2024&#xff09; 2024 4th International Conference on Communication Technology and Information Technology 重要信息 大会官网&#xf…

python生成系统测试数据

开发系统的时候,为了系统可以更好的进行测试,一般需要准备测试数据,以便可以顺利的对各种场景进行测试,使用两张表来说明怎么快速生成测试数据。 1.用户表 一般登录的时候,需要用到用户表 用户表字段如下: 用户名、密码、姓名、性别、邮箱、手机号、用户类型、地址 下…

Linux进程间通信(管道+共享内存)

进程间通信&#xff08;interprocess communication&#xff0c;简称 IPC&#xff09;指两个进程之间的通信。系统中的每一个进程都有各自的地址空间&#xff0c;并且相互独立、隔离&#xff0c;每个进程都处于自己的地址空间中。所以同一个进程的不同模块&#xff08;譬如不同…

【PyQt5】一文向您详细介绍 setPlaceholderText() 的作用

【PyQt5】一文向您详细介绍 setPlaceholderText() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通…

剑和沙盒 6 - 线程辱骂 – 使用线程名称进行攻击

强调&#xff1a; 进程注入是攻击者工具包中的重要技术之一。在下面的文章中 解释了如何滥用线程描述 API 来绕过端点保护产品。提出了一种新的注入技术&#xff1a;Thread Name-Calling&#xff0c;并给出了实施保护的相关建议。 介绍 进程注入是攻击者使用的重要技术之一 。…

《Programming from the Ground Up》阅读笔记:p75-p87

《Programming from the Ground Up》学习第4天&#xff0c;p75-p87总结&#xff0c;总计13页。 一、技术总结 1.persistent data p75, Data which is stored in files is called persistent data, because it persists in files that remain on disk even when the program …