【结构型模式】代理模式

news2025/4/13 21:03:58

文章目录

    • 优秀借鉴
    • 1、简介
    • 2、结构
    • 3、实现方式
      • 3.1、案例引入
      • 3.2、静态代理
      • 3.3、JDK动态代理
      • 3.4、CGLIB动态代理
    • 4、区别对比
      • 4.1、静态代理和动态代理
      • 4.2、JDK动态代理和CGLIB动态代理
    • 5、代理模式优缺点
    • 6、应用场景

优秀借鉴

设计模式(四)——搞懂什么是代理模式

代理设计模式 (refactoringguru.cn)

黑马程序员Java设计模式详解-设计模式-结构型模式-代理对象概述

《深入设计模式》-亚历山大·什韦茨(Alexander Shvets)

1、简介

代理模式(Proxy)是一种常见的设计模式,它允许通过代理对象控制对某个对象的访问。在代理模式中,代理类扮演着客户端和真正的目标对象之间的中介角色,代理类可以为目标对象提供额外的功能,例如远程访问、延迟加载、权限控制等。

使用代理模式可以实现对象的封装,同时也能够降低系统耦合度,增强了系统的灵活性和可扩展性。如果在开发过程中需要对某个对象进行控制,并且希望保持系统的高内聚、低耦合特性,那么代理模式是一个不错的选择。

2、结构

代理模式通常包括三个角色:抽象主题(Subject)、真实主题(Real Subject)和代理主题(Proxy Subject)

  • 抽象主题定义了一个共同的接口,也可被称为真实主题的规范,被代理类和真实的目标类都要实现该接口;
  • 真实主题即真正执行业务逻辑的对象;
  • 代理主题是代理类,可以代替真实主题来完成一些操作,同时也可以在完成操作前或者之后添加一些额外的逻辑,以实现对真实主题的控制。

img

上面结构在真实世界中可以通过一个银行的场景来类比:

真实世界类比

代理模式三个角色在上面的实现分别如下:

  • 抽象主题:体现在上面的接口中,定下一个支付的规范,实现该接口的类需要重写支付方法;
  • 真实主题:体现在上面的现金中,最直接的支付方式便是通过现金支付;
  • 代理主题:体现在上面的信用卡中,用户可以不用带着一大捆的现金出门,直接通过信用卡进行刷卡支付,有信用卡的厂商代付。

信用卡是银行账户的代理银行账户则是一大捆现金的代理。它们都实现了同样的接口,均可用于进行支付。消费者会非常满意,因为不必随身携带大量现金;商店老板同样会十分高兴,因为交易收入能以电子化的方式进入商店的银行账户中,无需担心存款时出现现金丢失或被抢劫的情况。

3、实现方式

3.1、案例引入

在下面的案例中,统一使用租房的案例进行介绍,整个场景的UML图如下:

租房uml

代理模式三个角色在上面的实现分别如下:

  • 抽象主题:体现在上面的接口IRentHouse中,定下一个租房的规范,实现该接口的类需要重写租房方法;
  • 真实主题:体现在上面的房东Landlord中,最直接的租房方式便是直接找到房东租房;
  • 代理主题:体现在上面的中介Intermediary中,租客Customer不用为了房源大老远跑去找房东,而是通过手握房源的中介进行租房。

中介是房东的代理,中介和房东都实现了抽象主题接口,均可以找到他们去租房子。租客在租房过程中,可以直接找到距离自己近的中介租房而不用奔波到遥远的房东去租房

3.2、静态代理

在静态代理中,代理类和真实类都要实现相同的接口或者继承相同的抽象类。代理类负责将客户端请求转发给真实对象,并且可以在调用真实对象前后添加一些额外的逻辑。

值得注意的是,静态代理需要手动编写代理类,代码量较大,但是运行效率较高。

使用静态代理对案例进行实现如下:

/**
 * @author xbaozi
 * @version 1.0
 * @classname StaticProxy
 * @date 2023-04-09  12:42
 * @description 静态代理
 */
public class StaticProxy {
    public static void main(String[] args) {
        System.out.println("\n********** 直接找到房东租房 **********");
        Customer customerToLandlord = new Customer(new Landlord());
        customerToLandlord.findHouse();
        System.out.println("\n********** 找附近手握房源的中介租房 **********");
        Customer customerToIntermediary = new Customer(new Intermediary());
        customerToIntermediary.findHouse();
    }
}

/**
 * 抽象主题,对租房定下规范
 */
interface IRentHouse {
    void rantHouse();
}

/**
 * 真实主题,实现抽象主题
 */
class Landlord implements IRentHouse {

    @Override
    public void rantHouse() {
        System.out.println("[真实主题] 找到房东租房……");
    }
}

/**
 * 代理主题,实现抽象主题,同时对真实主题进行增强
 */
class Intermediary implements IRentHouse {

    private Landlord landlord = new Landlord();

    @Override
    public void rantHouse() {
        System.out.println("[代理主题] 找到手握房源的中介交中介费……");
        landlord.rantHouse();
        System.out.println("[代理主题] 和租户对接好后续工作");
    }
}

/**
 * 租户类
 */
class Customer {
    private IRentHouse rentHouse;

    public Customer(IRentHouse rentHouse) {
        this.rentHouse = rentHouse;
    }

    public void findHouse() {
        rentHouse.rantHouse();
    }
}

运行结果如下:

image-20230409133616645

3.3、JDK动态代理

JDK动态代理是一种在运行时生成代理对象的技术。它允许我们在不修改源代码的情况下,通过代理对象来调用目标对象的方法。

其通常用于实现 AOP(面向切面编程)RPC(远程过程调用协议) 等功能。在AOP中,代理对象可以在执行目标对象的方法前后进行一些额外的操作,如日志记录、事务管理等。而在远程方法调用中,代理对象可以隐藏底层的网络通信细节,使得远程调用看起来就像本地调用一样。

JDK动态代理的原理是基于反射机制和接口实现的。通过获取目标对象的接口信息和实现类,然后创建一个新的代理类并实现相同的接口,并在代理类中处理特定的逻辑操作。

/**
 * @author xbaozi
 * @version 1.0
 * @classname JavaDynamicProxy
 * @date 2023-04-09  13:48
 * @description Java动态代理
 */
public class JavaDynamicProxy {
    public static void main(String[] args) {
        System.out.println("\n********** 直接找到房东租房 **********");
        Customer customerToLandlord = new Customer(new Landlord());
        customerToLandlord.findHouse();
        System.out.println("\n********** 找附近手握房源的中介租房 **********");
        IRentHouse proxyIntermediary = ProxyFactory.getProxy();
        Customer customerToIntermediary = new Customer(proxyIntermediary);
        customerToIntermediary.findHouse();
    }
}

/**
 * 抽象主题类
 */
interface IRentHouse {
    void rantHouse();
}

/**
 * 真实主题,实现抽象主题
 */
class Landlord implements IRentHouse {

    @Override
    public void rantHouse() {
        System.out.println("[Java动态代理-真实主题] 找到房东租房……");
    }
}

/**
 * 代理工厂,用于生成代理主题类
 */
class ProxyFactory {
    private static Landlord landlord = new Landlord();

    public static IRentHouse getProxy() {
        /*
            newProxyInstance()方法参数说明:
                ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
                Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
                InvocationHandler h : 代理对象的调用处理程序
         */
        return (IRentHouse) Proxy.newProxyInstance(
                landlord.getClass().getClassLoader(),
                landlord.getClass().getInterfaces(),
                new InvocationHandler() {
                    /*
                        InvocationHandler中invoke方法参数说明:
                            proxy : 代理对象,newProxyInstance方法的返回对象
                            method : 对应于在代理对象上调用的接口方法的 Method 实例
                            args : 代理对象调用接口方法时传递的实际参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("[Java动态代理-代理主题] 去中介公司交中介费获取中介服务");
                        Object returnArg = method.invoke(landlord, args);
                        System.out.println("[Java动态代理-代理主题] 和租户对接好后续工作");
                        return returnArg;
                    }
                }
        );
    }
}

/**
 * 租户类
 */
class Customer {
    private IRentHouse rentHouse;

    public Customer(IRentHouse rentHouse) {
        this.rentHouse = rentHouse;
    }

    public void findHouse() {
        rentHouse.rantHouse();
    }
}

运行结果如下:

image-20230409192430876

这里不知道有没有小伙伴有疑问:ProxyFactory代理工厂是我们代理模式中的代理主题即代理类吗?

答案为并不是。ProxyFactory只是一个动态生成代理类的一个工厂,而代理类是程序在运行过程中动态的在内存中生成的类。这可以类比成ProxyFactory是一个中介公司,其并不是要真正为租客找房子的那个人,真正为租客代理租房的是中介公司派出(生成)的中介,即真正的代理类。

在动态代理中,底层通过反射获取到目标调用的方法,然后通过自定义的 InvocationHandler 中的 invoke 方法实现对目标方法的增强。这里可以通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看生成代理类的结构(精简版):

//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements IRentHouse {
    private static Method m3;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        m3 = Class.forName("com.xbaoziplus.proxy.dynamic.jdk.IRentHouse").getMethod("rantHouse", new Class[0]);
    }

    public final void rantHouse() {
        this.h.invoke(this, m3, null);
    }
}

Arthas 生成的完整的代码如下,感兴趣的小伙伴可以自行查看:

package com.sun.proxy;

import com.xbaoziplus.proxy.dynamic.jdk.IRentHouse;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IRentHouse {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.xbaoziplus.proxy.dynamic.jdk.IRentHouse").getMethod("rantHouse", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke(this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void rantHouse() {
        try {
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

3.4、CGLIB动态代理

CGLIB动态代理是一种Java动态代理技术,它可以在运行时动态地生成一个子类来作为被代理对象的代理。相比于JDK自带的动态代理,CGLIB动态代理使用更加灵活,它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。

CGLIB动态代理和JDK动态代理最大的区别就是前者使用的是第三方包,不需要有抽象主题的接口,后者是JDK自带的,必须要有抽象主题接口。

CGLIB动态代理的原理是通过继承被代理类,然后重写其中的方法实现代理功能。当调用被代理类的方法时,实际上是调用了代理类中重写的方法。这样就可以对被代理类的方法进行增强或拦截,从而实现**AOP(面向切面编程)**的功能。

CGLIB是第三方提供的资源包,所以在使用之前需要引入jar包依赖:

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

具体实现步骤如下:

  1. 创建 Enhancer 实例Enhancer 是 CGLIB 中的主要类,用于生成代理对象。需要使用 Enhancer 创建一个新的代理对象;
  2. 设置父类:CGLIB生成的代理对象是目标类的子类,因此需要设置父类。这可以通过调用Enhancer.setSuperclass()方法来完成;
  3. 设置回调:回调是代理对象将要执行的操作。可以使用 MethodInterceptorCallbackFilter 等类来设置回调;
  4. 创建代理对象:最后一步是使用 Enhancer.create() 方法创建代理对象。
/**
 * @author xbaozi
 * @version 1.0
 * @classname CGLIBDynamicProxy
 * @date 2023-04-10  15:22
 * @description CGLIB动态代理
 */
public class CGLIBDynamicProxy {
    public static void main(String[] args) {
        System.out.println("\n********** 直接找到房东租房 **********");
        Customer customerToLandlord = new Customer(new Landlord());
        customerToLandlord.findHouse();
        System.out.println("\n********** 找附近手握房源的中介租房 **********");
        Landlord proxyIntermediary = new ProxyFactory().getProxy();
        Customer customerToIntermediary = new Customer(proxyIntermediary);
        customerToIntermediary.findHouse();
    }
}

/**
 * 真实主题,实现抽象主题
 */
class Landlord {

    public void rantHouse() {
        System.out.println("[CGLIB动态代理-真实主题] 找到房东租房……");
    }
}

/**
 * 代理工厂,用于生成代理主题类
 */
class ProxyFactory implements MethodInterceptor {
    private Landlord landlordSuper = new Landlord();

    public Landlord getProxy() {
        // 1. 创建Enhancer实例
        Enhancer enhancer = new Enhancer();
        // 2. 设置父类
        enhancer.setSuperclass(landlordSuper.getClass());
        // 3. 设置回调
        enhancer.setCallback(this);
        // 4. 创建代理对象
        Landlord landlord = (Landlord) enhancer.create();
        return landlord;
    }

    @Override
    public Landlord intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("[CGLIB动态代理-代理主题] 去中介公司交中介费获取中介服务");
        Landlord landlord = (Landlord) methodProxy.invokeSuper(o, args);
        System.out.println("[CGLIB动态代理-代理主题] 和租户对接好后续工作");
        return landlord;
    }
}

/**
 * 租户类
 */
class Customer {
    private Landlord rentHouse;

    public Customer(Landlord rentHouse) {
        this.rentHouse = rentHouse;
    }

    public void findHouse() {
        rentHouse.rantHouse();
    }
}

运行结果如下:

image-20230410154919519

4、区别对比

4.1、静态代理和动态代理

动态代理和静态代理是代理模式中两种不同的实现方式,它们之间的区别主要体现在以下几个方面:

  1. 代理类生成时期不同。静态代理在编译期就已经确定了代理类与委托类的关系,即代理类和委托类是早已确定并且固定的。而动态代理则是在运行时通过反射机制动态地生成代理类,使其具有与委托类相同的接口和方法;

  2. 灵活性不同。由于静态代理在编译期就确定了代理类和委托类的关系,因此它的灵活性较差,无法在运行时改变代理类和委托类的关系。而动态代理则可以根据需要在运行时生成代理类,并动态地指定具体的委托类对象,从而具有更高的灵活性;

  3. 实现原理不同。静态代理在程序编写和编译时,需要开发人员手动编写代理类和委托类的代码,较为繁琐。而动态代理是通过Java反射机制生成代理类的字节码,并加载到JVM中,然后动态创建代理实例。这种方式大大简化了代理类的开发工作。

4.2、JDK动态代理和CGLIB动态代理

JDK动态代理和CGLIB动态代理都是Java中的动态代理技术,它们的主要区别在于实现方式和适用场景。

JDK动态代理是通过反射机制来实现的,在运行时动态地创建一个实现了指定接口的代理类,代理类中的方法调用会被转发到 InvocationHandler 进行处理。因此,JDK动态代理只能代理实现了接口的类,并且生成的代理类只能代理接口中声明的方法,对于其他方法则无法代理。

CGLIB动态代理则是通过继承目标类来实现的,它创建的代理类是目标类的子类,重写了目标类中的非final方法,并将它们分派到 Callback 中定义的拦截器中去处理。因此,CGLIB动态代理可以代理没有实现接口的类,并且可以代理目标类中所有非final方法

简而言之,JDK动态代理适用于代理有接口的类,而CGLIB动态代理则适用于代理没有接口或者需要代理目标类中所有非final方法的类。因此大部分情况下有接口用JDK,无接口用CGLIB

除了上述区别之外,JDK动态代理和CGLIB动态代理还有一些其他的差异:

  1. 性能:一般情况下,后者性能比前者要好。JDK动态代理使用反射机制动态创建代理类,生成代理对象的效率相对较低;而CGLIB动态代理则是直接生成目标类的子类,因此生成代理对象的效率较高。
  2. 内存占用:由于CGLIB动态代理创建的代理类是目标类的子类,所以代理类会继承目标类的所有非私有属性和方法,导致代理类的内存占用比较大;而JDK动态代理生成的代理类只包含需要代理的接口方法,因此内存占用相对较小。
  3. 依赖性:JDK动态代理是Java原生的API,不需要引入第三方库,而CGLIB动态代理需要引入cglib库进行支持。
  4. 版本兼容性:JDK动态代理是Java原生API,因此具有很好的版本兼容性;而CGLIB动态代理在不同版本的Java环境下可能存在兼容性问题,推荐使用的是 2.2.2及以下版本。

这里补充一个性能相关的小知识,在Java中,JDK在5、6、7、8等版本中都对动态代理进行了优化,使得在JDK8及之后,JDK动态代理的性能与CGLIB动态代理性能持平甚至反超。

  • JDK5中,Java引入了新的虚拟机指令——“invokedynamic”,该指令的出现为动态语言的实现提供了更广泛的支持。这项技术的引入也为Java的动态代理提供了更好的性能和灵活性。

  • JDK6和7中,Java对反射机制进行了一系列优化,使得动态代理的创建和调用效率得到了显著提升。

  • JDK8中,Java引入了默认方法和Lambda表达式等新特性,这些特性进一步提升了动态代理的性能和效率。同时,JDK8还引入了MethodHandle类,可以更高效地调用方法。

5、代理模式优缺点

优点缺点
可以在客户端毫无察觉的情况下控制服务对象代码可能会变得复杂, 因为需要新建许多类
如果客户端对服务对象的生命周期没有特殊要求, 可以对生命周期进行管理服务响应可能会延迟
即使服务对象还未准备好或不存在, 代理也可以正常工作
符合开闭原则。 可以在不对服务或客户端做出修改的情况下创建新代理

6、应用场景

代理模式是一种结构型设计模式,它通过增加一个代理对象来控制对原始对象的访问。代理对象可以在不改变原始对象的前提下,实现额外的功能或者控制访问级别。

下面是一些代理模式中常见的应用场景:

  1. AOP(面向切面编程):通过动态代理,在方法前后自动添加日志记录、权限控制、性能统计等通用功能,避免了代码冗余,提高了代码的复用性和可维护性;
  2. RPC(远程过程调用):在分布式系统中,动态代理可以将远程方法调用封装成本地方法调用,使得远程调用像本地调用一样简单,同时也支持负载均衡、容错等功能;
  3. 数据库连接池:数据库连接池可以通过动态代理来实现,每次获取连接时,动态代理会检查当前连接是否可用,如果已经关闭或者超时,则重新创建连接返回给用户;
  4. 缓存框架:缓存框架可以通过动态代理来实现,当一个对象需要缓存时,动态代理可以根据缓存配置来判断是否需要从缓存中获取数据,还是直接从数据库中获取数据并更新缓存;
  5. 日志框架:动态代理可以用于实现日志框架,例如Spring AOP中的日志切面,可以动态地在方法前后添加日志记录代码,以此来监控系统运行情况,方便问题排查;
  6. 虚拟代理:虚拟代理是一种延迟加载技术,它允许对象在真正需要时才被创建。例如,在需要显示大量图片的应用程序中,可以使用虚拟代理来延迟加载图片,只有当用户需要查看图片时才会加载。

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

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

相关文章

线段树的懒标记

上次看的那个视频讲线段树的时候压根没讲懒标记&#xff0c;然后我今天去写题目直接被薄纱&#xff01;都是70分&#xff0c;剩下3个节点tml&#xff01;&#xff01;&#xff01; 懒标记 我们在修改一些区间的时候&#xff0c;按照我昨天来学的来修改要改到最下面的叶节点去…

Visual Studio如何将UTF-8字符串输出到控制台

解决c语言使用libcurl库时控制台输出中文出现乱码的问题。 字符编码问题一直以来都是Windows系统的诟病&#xff0c;而Visual Studio也是饱受此诟病。由于历史原因&#xff0c;Windows系统对各个不同的国家地区可能采用不同的code page。由于早先Unicode并未发展成熟&#xff0…

Java设计模式之工厂模式

什么是工厂模式 工厂模式就是将创建对象的具体过程和使用过程分开&#xff0c;这样能够使代码更加灵活。 工厂模式主要分为三类&#xff1a; 简单工厂模式工厂方法模式抽象工厂模式 比如在没有工厂的时候&#xff0c;用户需要一台奥迪车&#xff0c;那么就需要用户去创建一台…

【java 多线程】并发设计模式-两阶段终止模式(对interrupt的理解)

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

惊艳!阿里出品“Java全栈进阶宝典”,广度与深度齐头并进

前言 据有关数据统计&#xff0c;目前来看&#xff0c;大大小小的招聘网站上面&#xff0c;Java岗的招聘量仍然是最多的&#xff0c;基本是其他语言的3倍以上&#xff0c;由于目前Java所处的统治级地位&#xff0c;单就数量来看&#xff0c;Java语言实现的系统是海量的&#x…

linux安装MongoDB

环境说明 系统CentOS&#xff1a;CentOS7 mongodb版本 4.2.24 下载 官网下载地址 Download MongoDB Community Server | MongoDB 某云盘 链接&#xff1a;https://pan.baidu.com/s/1G4AC3h5rvz9WM3fx4gJzbA 提取码&#xff1a;ojkl 上传解压 在根目录下创建opt文件夹…

【数据分析之道-基础知识(九)】推导式

文章目录专栏导读1、前言2、列表推导式3、集合推导式4、字典推导式5、元组推导式专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python领域新星创作者&#xff0c;专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》&#xff0c;本专栏针对大学生、初级数据分…

C语言学习1--------Visual Studio集成开发环境的搭建

C语言学习1--------Visual Studio集成开发环境的搭建适合初学者适用集成开发环境下载 Visual Studio 2019安装 Visual Studio 2019安装工作负载为C自定义安装位置激活 Visual Studio适合初学者适用集成开发环境 建议初学者适用最新的——Visual Studio 2019为集成开发环境。 部…

【数据库原理 • 五】数据库安全性与完整性

前言 数据库技术是计算机科学技术中发展最快&#xff0c;应用最广的技术之一&#xff0c;它是专门研究如何科学的组织和存储数据&#xff0c;如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进&#xff0c;最常用的技术。 当前…

【C++11那些事儿(一)】

文章目录一、C11简介二、列表初始化2.1 C98中{}的初始化问题2.2 C11中的列表初始化三、各种小语法3.1 auto3.2 decltype3.3 nullptr3.4 范围for四、STL中的一些变化五、左/右值引用和移动语义&#xff08;本篇重点&#xff09;5.1 做值引用和右值引用5.2 左值引用与右值引用比较…

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表

【快乐手撕LeetCode题解系列】—— 复制带随机指针的链表&#x1f60e;前言&#x1f64c;复制带随机指针的链表&#x1f64c;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵…

shell结构化命令中for命令

shell脚本编程系列 for var in list docommands done读取列表中的值 每次遍历值列表时&#xff0c;for命令会将列表中的下一个值赋值给变量 #!/bin/bash# basic for commandfor test in Alabama Alaska Arizona Arkansas California Coloradodoecho The next state is $testdo…

第07章_面向对象编程(进阶)

第07章_面向对象编程(进阶) 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 1. 关键字&#xff1a;this 1.1 this是什么&#xff1f; 在Java中&#xff0c;this关键字不算难理解…

关于图像分割的预处理 transform

目录 1. 介绍 2. 关于分割中的 resize 问题 3. 分割的 transform 3.1 随机缩放 RandomResize 3.2 随机水平翻转 RandomHorizontalFlip 3.3 随机竖直翻转 RandomVerticalFlip 3.4 中心裁剪 RandomCrop 3.5 ToTensor 3.6 normalization 3.7 Compose 4. 预处理结果可视…

WPF mvvm框架Stylet使用教程-基础用法

Stylet框架基础用法 安装Nuget包 在“管理Nuget程序包”中搜索Stylet&#xff0c;查看Stylet包支持的net版本&#xff0c;然后选择第二个Stylet.Start包进行安装&#xff0c;该包会自动安装stylet并且生成基本的配置 注意事项&#xff1a;安装时要把需要安装的程序设为启动项…

第06章_面向对象编程(基础)

第06章_面向对象编程&#xff08;基础&#xff09; 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 本章专题与脉络 学习面向对象内容的三条主线 Java类及类的成员&#xff1a;&#xff08;重点&…

《QT+CGAL网格处理——网格重建》

QT+CGAL网格处理——网格重建 一、重建效果二、代码分析显示代码格式转换彩色网格显示三、后续一、重建效果 二、代码分析 显示 1、依旧采取VTK显示,参照《QT+PCL》; 2、点数据、网格数据依旧采用pcl数据结构,cgal处理完成后转换格式即可 界面参照:

微服务学习-SpringCloud -Nacos (心跳机制及健康检查源码学习)

文章目录心跳机制与健康检查流程图心跳机制与健康检查总结详细源码说明当多个服务进行注册时&#xff0c;如何解决注册表并发冲突问题?心跳机制与健康检查流程图 心跳机制与健康检查总结 微服务在启动注册Nacos时&#xff0c;会创建一个定时任务&#xff0c;定时向服务端发生…

基于文心一言的底层视觉理解,百度网盘把「猫」换成了「黄色的猫」

随着移动互联网的一路狂飙&#xff0c;手机已经成为人们的新器官。出门不带钥匙可以&#xff0c;不带手机却是万万不可以的。而手机上&#xff0c;小小的摄像头也越来越成为各位「vlogger」的口袋魔方。每天有超过数亿的照片和视频被上传到百度网盘中&#xff0c;这些照片和视频…

Nginx 实战-负载均衡

一、负载均衡今天学习一下Nginx的负载均衡。由于传统软件建构的局限性&#xff0c;加上一台服务器处理能里的有限性&#xff0c;在如今高并发、业务复杂的场景下很难达到咱们的要求。但是若将很多台这样的服务器通过某种方式组成一个整体&#xff0c;并且将所有的请求平均的分配…