【动态代理详解】

文章目录

  • 1. 关于代理
    • 1.1 代理的概述
      • 1.1.1 什么是动态代理
      • 1.1.2 动态代理能做什么
    • 1.2 什么是代理
      • 1.2.1 生活中的代理
      • 1.2.2 为什么要找中介?
    • 1.3 开发中的代理模式(代理)
      • 1.3.1 使用代理模式的作用
    • 1.4 实现代理的方式
  • 2. 静态代理
    • 2.1 什么是静态代理
    • 2.2 静态代理的实现步骤
    • 2.3 静态代理的优缺点
  • 3. 动态代理(重点)
    • 3.1 什么是动态代理
    • 3.2 动态代理的优点
    • 3.3 动态代理的两种实现方式
      • 3.3.1 jdk动态代理的实现
        • 3.3.2 InvocationHandler 接口(调用处理器)
        • 3.3.3 如何理解InvocationHandler接口
        • 3.3.4 Method类
        • 3.3.5 Proxy类
    • 3.4 动态代理的实现步骤
  • 4.动态代理开发中实例

1. 关于代理

知道什么是动态代理以及动态代理能干什么就可以

1.1 代理的概述

1.1.1 什么是动态代理

使用jdk的反射机制,创建对象的能力, 创建的是代理类的对象。

  • 动态:在程序执行时,调用jdk提供的方法才能创建代理类的对象。
  • jdk动态代理,必须有接口,目标类必须实现接口, 没有接口时,需要使用cglib动态代理

1.1.2 动态代理能做什么

可以在不改变原来目标方法功能的前提下, 可以在代理中增强自己的功能代码。

​ 例如实际开发中,你所在的项目中,有一个功能是其他人(公司的其它部门,其它小组的人)写好的,你可以使用。你发现这个功能,现在还缺点,不能完全满足我项目的需要。 我需要在gn.print()执行后,需要自己在增加代码。用代理实现 gn.print()调用时, 增加自己代码, 而不用去改原来的 GoNong文件。

// GoNong.class 
GoNong gn = new GoNong();
gn.print();

​ 注意,不知道源代码,不能使用重写,而且你要用写好的功能想要去扩展功能,就只能先把写好的方法执行一边,然后再代理中扩展,重写的话,原来写好的方法也没了

1.2 什么是代理

1.2.1 生活中的代理

​ 代理就是中介、代购、商家等。

​ 例如留学中介就是一个代理,我想上美国的一所大学,但是我没办法去这个大学实地考察,也没办法要这个大学的联系方式,并且这个大学拒绝个人去访问它。但是这个大学在国内把招生业务委派给了一个留学中介。这样我就可以去和留学中介交谈,留学中介可以与美国大学联系。留学中介会从我这里收取额外的中介费。

​ 在以上的案例中,学校是目标,留学中介是代理,我是客户。它们具有以下特点:

  • 中介和代理他们要做的事情是一致的: 招生。

  • 中介是学校代理, 学校是目标。

  • 我—中介(学校介绍,办入学手续)----美国学校。

  • 中介是代理,不能白干活,需要收取费用。

  • 代理不让你访问到目标。

1.2.2 为什么要找中介?

  • 中介是专业的,方便

  • 我现在不能自己去找学校。 我没有能力访问学校。 或者美国学校不接收个人来访。

  • 买东西都是商家卖, 商家是某个商品的代理, 你个人买东西, 肯定不会让你接触到厂家的。

1.3 开发中的代理模式(代理)

​ 代理模式是指,当一个对象(目标)无法直接使用时,可以在该客户端和目标类之间创建一个中介,这个中介就是代理。

​ 在实际开发中有这样情况,有一个A类,有一个C类,但是C类不允许A类直接访问,这样我们可以创建一个A类和C类之间的B类,C类允许B类访问,这样我们可以在A类中访问B类,然后B类在访问C类,这样就相当于在A类中间接访问到了C类。其中A类是客户类,B类是代理类,C类是目标类。另外,我可以在B类中添加一些内容,意味着功能增强。

1.3.1 使用代理模式的作用

  • 功能增强: 在你原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。 (例如,留学中介要收取额外的费用)
  • 控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。(就跟房屋中介肯定不会给房东电话给你,否则它们怎么赚中介费)

1.4 实现代理的方式

实现代理的方式有:静态代理,动态代理

2. 静态代理

2.1 什么是静态代理

​ 静态代理:代理类是手工创建的,代理的目标类是固定的。

2.2 静态代理的实现步骤

模拟一个用户购买u盘的行为,其中用户是客户端类,商家是代理类,厂家是目标类,商家和厂家都是卖U盘的,应该把卖U盘这个动作抽象为一个接口。

  1. 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。

    // 表示功能的,厂家,商家都要完成的功能
    public interface UsbSell {
    
        //定义方法 参数 amount:表示一次购买的数量,暂时不用
        //返回值表示一个u盘的价格。
        float sell(int amount);
    
        //可以多个其它的方法
        //void print();
    }
    
  2. 创建厂家类,实现1步骤的接口

    //目标类:金士顿厂家, 不接受用户的单独购买。
    public class UsbKingFactory implements UsbSell {
    
        @Override
        public float sell(int amount) {
            System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
            //一个128G的u盘是 85元。
            //后期根据amount ,可以实现不同的价格,例如10000个,单击是80, 50000个75
            return 85.0f;
        }
    }                          
    
  3. 创建商家,就是代理类,也需要实现1步骤中的接口。

    // taobao是一个商家,代理金士顿u盘的销售。
    public class TaoBao implements UsbSell {
    
        // 声明 商家代理的厂家具体是谁
        // private修饰为了控制访问,不让客户知道厂家是谁
        private UsbKingFactory factory = new UsbKingFactory();
    
        @Override
        // 实现销售u盘功能
        public float sell(int amount) {
    
            // 向厂家发送订单,告诉厂家,我买了u盘,厂家发货
            float price = factory.sell(amount); //厂家的价格。
            // 商家 需要加价, 也就是代理要增加价格。
            price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
            // 在目标类的方法调用后,你做的其它功能,都是增强的意思。
            System.out.println("淘宝商家,给你返一个优惠券,或者红包");
    
            // 增加的价格
            return price;
        }
    }
    
    public class WeiShang implements UsbSell {
        //代理的是 金士顿,定义目标厂家类
        private UsbKingFactory factory = new UsbKingFactory();
        @Override
        public float sell(int amount) {
            //调用目标方法
            float price = factory.sell(amount);
            //只增加1元
            price = price + 1;
            return price;
        }
    }
    
  4. 创建客户端类,调用商家的方法买一个u盘。

    public class Customer {
        public static void main(String[] args) {
            // 通过淘宝卖
            // 下载了淘宝的app
            /*
            TaoBao taoBao = new TaoBao();
            // 通过代理类,实现购买u盘,增加了优惠券,红包等等
            float price = taoBao.sell(1);
            System.out.println("淘宝的价格:" + price);
            */
    
            // 取得微商的联系方式
            WeiShang weiShang = new WeiShang();
            // 通过微商代理,实现购买u盘,增加了优惠券,红包等等
            float price = weiShang.sell(1);
            System.out.println("通过微商购买的价格:"+ price);
        }
    }
    

分析问题:

  • 以上实例,若只有一个目标类,如金士顿厂家,商家只需要和这个厂家联系就行。但是如果又有一个闪迪厂家,那么得重写一套代理类,因为一个代理类中只能指定一个厂家,如以上的淘宝类,制定了厂家是金士顿,就不能在指定闪迪厂家,只能重新新建一个代理类,在这个代理类中指定厂家是闪迪,微商也是一样,要来一个专门代理闪迪的微商。如果有100个厂家,那么就要写200个代理类。
  • 如果UsbSell接口多了个退货功能,那么所有的目标类和代理类都要修改。

2.3 静态代理的优缺点

  • 优点
    • 实现简单
    • 容易理解
  • 缺点(当你的项目中,目标类和代理类很多时候,有以下的缺点:)
    • 当目标类增加了,代理类可能也需要成倍的增加。 代理类数量过多。
    • 当你的接口中功能增加了,或者修改了,会影响众多的实现类,厂家类,代理都需要修改。影响比较多。

简而言之,静态代理容易理解,代码好写,但是各个类之间耦合度太高,一旦扩展就有问题

3. 动态代理(重点)

3.1 什么是动态代理

​ 动态代理就是在程序执行过程(动态)中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。换句话说: 动态代理是一种创建java对象的能力,让你不用自己写代理类的java程序,然后new对象。而是直接用jdk创建代理类对象。这样我不关心代理类是谁,我只知道它能增强功能,并且通过它我可以找到目标类。(类似暗下交易)

3.2 动态代理的优点

  • 优点
    • 动态代理中目标类即使很多,但是代理类数量可以很少,当你修改了接口中的方法时,不会影响代理类(压根就不用写代理类了,这个代理类是jdk创建的,只是起到一个中介的功能)。符合OCP原则
    • 不用创建代理类文件,代理的目标类是灵活的,可以随意给不同目标创建代理

3.3 动态代理的两种实现方式

  • jdk动态代理(理解): 使用java反射包中的类和接口实现动态代理的功能。
    • 反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy.
  • cglib动态代理(了解): cglib是第三方的工具库, 创建代理对象。
    • cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改。
    • 因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。
    • cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用,比如 mybatis ,spring框架中都有使用。

注意:jdk动态代理必须有接口,对于无接口类,必须使用cglib来为它创建动态代理

3.3.1 jdk动态代理的实现

需要用到反射包 java.lang.reflect,里面有三个类 : InvocationHandler , Method, Proxy.

3.3.2 InvocationHandler 接口(调用处理器)
  • 该接口中只有一个方法:invoke()方法
  • invoke()方法最后是由代理类调用的,所以代理类完成的功能写在该方法里
    • 代理类完成的功能:调用目标方法、功能增强
public Object invoke(Object proxy, Method method, Object[] args)

invoke方法参数:

  • proxy:代理对象,由jdk创建
  • method:目标方法,因为代理类的功能是调用目标方法,所以这里要给出
  • args:目标方法的参数
3.3.3 如何理解InvocationHandler接口
  • InvocationHandler接口表示你的代理想要干什么,即代理完成的功能写在该接口实现类的invoke方法中

  • 使用方法:

    1. 创建类实现接口InvocationHandler
    2. 重写invoke()方法,把原来静态代理中代理类要完成的功能,写在这。
3.3.4 Method类
  • method表示目标类中的方法
  • method是要发给InvocationHandler实现类的invoke方法的参数,表示代理所调用的目标方法
3.3.5 Proxy类
  • 通过该类可以生成代理对象,代理对象的创建是由jdk提供的,我们只是负责调Proxy类中的方法来拿到这个代理对象
  • 调用Proxy类中的静态方法newProxyInstance()可以创建一个代理对象,具体创建步骤是由jdk完成的
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

newProxyInstance参数说明:

  • loader表示目标对象的类加载器,固定写法:目标对象.getClass().getClassLoader();
  • interfaces表示目标对象所实现的接口,接口多继承,因此是个数组。固定写法:目标对象.getClass().getInterface();
  • h表示代理类所要完成的功能

返回值就是代理类对象,并且在该对象创建时已经说明了,该代理类对象所代理的目标对象是谁,该目标对象完成了那些功能,该代理类所要完成的功能

3.4 动态代理的实现步骤

  1. 创建接口,定义目标类要完成的功能

    /**
     * 目标类要完成的功能
     */
    public interface UsbSell {
    
        float sell(int amount);
    }
    
  2. 创建目标类实现接口

    //目标类:金士顿厂家, 不接受用户的单独购买。
    public class UsbKingFactory implements UsbSell {
    
        @Override
        public float sell(int amount) {
            System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
            //一个128G的u盘是 85元。
            //后期根据amount ,可以实现不同的价格,例如10000个,单击是80, 50000个75
            return 85.0f;
        }
    }
    
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
    1.调用目标方法
    2.增强功能

    //必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2.功能增强)
    public class MySellHandler implements InvocationHandler {
    
        private Object target = null;
    
        //动态代理:目标对象是活动的,不是固定的,需要传入进来。
        //传入是谁,就给谁创建代理。
        public MySellHandler(Object target) {
            //给目标对象赋值
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object res  = null;
            //向厂家发送订单,告诉厂家,我买了u盘,厂家发货
            //float price = factory.sell(amount); //厂家的价格。
            res =  method.invoke(target,args); //执行目标方法
    
    
    
            //商家 需要加价, 也就是代理要增加价格。
            //price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
            if( res != null ){
                Float price = (Float)res;
                price = price + 25;
                res = price;
            }
    
            //在目标类的方法调用后,你做的其它功能,都是增强的意思。
            System.out.println("淘宝商家,给你返一个优惠券,或者红包");
            //记录数据库
    
            //增加的价格
            return res;
    
        }
    }
    
  4. 使用Proxy类的静态方法,创建代理对象。 并把返回值转为接口类型。

    public class Customer {
        public static void main(String[] args) {
            //创建代理对象,使用Proxy
            //1. 创建目标对象
            // UsbKingFacotry  factory = new UsbKingFactory();
            UsbSell factory = new UsbKingFactory();
            //2.创建InvocationHandler对象
            InvocationHandler handler = new MySellHandler(factory);
    
            //3.创建代理对象
            UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                    factory.getClass().getInterfaces(),
                    handler);
    
            //com.sun.proxy.$Proxy0 : 这是jdk动态代理创建的对象类型。
            System.out.println("proxy:"+proxy.getClass().getName());
            //4.通过代理执行方法
            float price = proxy.sell(1);
            System.out.println("通过动态代理对象,调用方法:"+price);
        }
    }
    

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

4.动态代理开发中实例

// 目标完成的功能
public interface HelloService {

    //打印报告, 报表
    int print(String name);
}


// 目标类
public class GoNeng implements HelloService {
    @Override
    public int print(String name) {
        System.out.println("其它人写好的个功能方法");
        return 2;
    }
}


// 代理完成的功能
public class MyInvocationHandler implements InvocationHandler {

    private Object target = null;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //我在项目中记录数据库,
        //调用目标方法, 执行print()得到 2
        Object res = method.invoke(target,args); //2
        
        // 代理进行“功能增强”
        //需要乘以 2的结果  
        if( res != null){
            Integer num = (Integer)res;
            res = num * 2;
        }

        return res;
    }
}


// 测试类
public class MyApp {

    public static void main(String[] args) {
       // GoNeng gn = new GoNeng();
        //int num = gn.print("销售");
       // System.out.println("num="+num);
		
        // 创建目标类
        GoNeng goNeng = new GoNeng();
        // 代理完成的功能
        InvocationHandler handler = new MyInvocationHandler(goNeng);
		// 这里表示目标类必须实现一个接口,否则不能用jdk实现动态代理,只能用cglib实现动态代理
        System.out.println("goNeng.getClass().getInterfaces()="+goNeng.getClass().getInterfaces()[0].getName());
        HelloService proxy = (HelloService) Proxy.newProxyInstance( goNeng.getClass().getClassLoader(),
                goNeng.getClass().getInterfaces(),handler);

        // 代理proxy的print方法实际上是接口中的,因为再创建代理是,把目标类的构造器和实现的接口都给了代理,并且代理完成的功能也给了代理
        // 代理执行print方法就去handler的invoke方法,把print方法给了method,参数“市场”给了args
        int num = proxy.print("市场");
        System.out.println("我们期望的 num ==" + num);
		
        // 总之proxy,handler,目标类之间是相关联的。
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

百川智能发布角色大模型 ,零代码复刻角色轻松满足游戏领域定制需求

2024年1月9日&#xff0c;百川智能发布角色大模型Baichuan-NPC&#xff0c;深度优化了“角色知识”和“对话能力”&#xff0c;使模型能够更好的理解上下文对话语义&#xff0c;更加符合人物性格地进行对话和行动&#xff0c;让角色栩栩如生。此外&#xff0c;对于游戏领域AI角…

各版本 操作系统 对 .NET Framework 与 .NET Core 支持

有两种类型的受支持版本&#xff1a;长期支持 (LTS) 版本和标准期限支持 (STS) 版本。 所有版本的质量都是一样的。 唯一的区别是支持的时间长短。 LTS 版本可获得为期三年的免费支持和补丁。 STS 版本可获得 18 个月的免费支持和修补程序。 有关详细信息&#xff0c;请参阅 .N…

找不到文件Computer Management.lnk怎么办

win10系统右键“此电脑”&#xff0c;在右键菜单选择“管理”弹出错误提示&#xff1a; windows找不到文件Computer Management.lnk 打开电脑C盘路径C:\Windows\System32&#xff0c;找到compmgmt.msc文件 给compmgmt.msc文件创建一个快捷键选择文件compmgmt.msc&#xff0c;右…

联盛德-安全物联网芯片w800

联盛德-安全物联网芯片w800 前言一、w800简介&#xff1f;芯片外观 总结 前言 本文介绍w800的基本信息&#xff0c;详细规格&#xff0c;后续有时间会介绍如何适配openharmony的过程。 一、w800简介&#xff1f; W800 芯片是一款安全 IoT Wi-Fi/蓝牙 双模 SoC 芯片。支持 2.…

软件测试|Linux三剑客之sed命令详解

简介 sed&#xff08;Stream Editor&#xff09;是一款流式文本编辑器&#xff0c;在 Linux 和类 Unix 系统中广泛使用。它的设计目的是用于对文本进行处理和转换&#xff0c;可以用于替换、删除、插入、打印等操作。sed 命令通过逐行处理文本&#xff0c;允许您使用简单的命令…

物联网协议Coap中Californium CoapClient解析

目录 前言 一、CoapClient对象 1、类定义 2、Client方法调用 二、发送请求 1、构建请求 2、发起请求 3、接收响应 总结 前言 在之前的博客中物联网协议Coap之Californium CoapServer解析&#xff0c;文中简单介绍了CoapServer的实现。在物联网开发环境中&#xff0c;除了…

git安装教程 Windows 附安装包链接

Git是一款分布式源代码管理工具(版本控制工具) 。 git的作用 当你需要做一个大工程的时候&#xff0c;文件的管理无疑是非常庞大的工作&#xff0c;因为你需要不断的修改更新文件内容&#xff0c;同时可能还要保留旧版本保证可以复原&#xff0c;这样就需要备份多个版本的文件…

【镜像压缩】linux 上 SD/TF 卡镜像文件压缩到实际大小的简单方法(树莓派、nvidia jetson)

文章目录 1. 备份 SD/TF 卡为镜像文件2. 压缩镜像文件2.1. 多分区镜像文件的压缩&#xff08;树莓派、普通 linux 系统等&#xff09;2.2. 单分区镜像文件的压缩&#xff08;Nvidia Jetson Nano 等&#xff09; 3. 还原镜像文件到 SD/TF 卡4. 镜像还原后处理4.1. 镜像分区调整4…

【MySQL】:用户管理

用户管理 一.用户1.用户信息2.创建用户3.修改密码4.删除用户 二.数据库权限1.给用户授权2.回收权限 如果在以下操作中出现以下报错 可以使用刷新权限 一.用户 1.用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中。 字段解释&#xff1a; host&#xff…

基于SSM+Vue的学校社团管理系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

基于代理IP的多线程爬虫实现

目录 前言 1. 爬虫的基本原理 2. 多线程爬虫的优势 3. 代理IP的应用 4. 基于代理IP的多线程爬虫实现 步骤1&#xff1a;导入必要的模块 步骤2&#xff1a;定义爬虫函数 步骤3&#xff1a;创建线程并启动爬虫 总结 前言 本文将介绍如何使用Python编写一个基于代理IP的多…

UR机器人旋转矢量和俯仰角的转换

我们在使用机器人的时候&#xff0c;总是习惯使用俯仰角来描述机器人末端姿态的变换。这样更直观&#xff0c;但是机器人为了插值方便计算&#xff0c;总是采用旋转矢量来来描述机器人的姿态。该旋转矢量及不直观&#xff0c;单一轴角度旋转时还可以理解&#xff0c;当两个轴或…

少儿编程 2023年12月中国电子学会图形化编程等级考试Scratch编程四级真题解析(判断题)

2023年12月scratch编程等级考试四级真题 判断题 11、创建自制积木时,参数只能添加数字和文本 答案:错 考点分析:考查自制积木的使用,创建自制积木的时候参数可以是数字、文本以及布尔值类型;所以答案错误 12、分查找是我们经常用的查找方式,它比顺序查找效率更高,比如…

前端中什么是DOM对象

DOM&#xff08;文档对象模型&#xff09;是一种编程接口&#xff0c;用于HTML和XML文档。它提供了一种将文档结构表示为树结构的方式&#xff0c;这使得程序和脚本能够动态地访问和更新文档的内容、结构和样式。 在前端开发中&#xff0c;DOM是非常重要的概念。当浏览器加载网…

高级别智能驾驶业务系列:港口作业仿真系统

在港口无人水平运输场景中&#xff0c;经纬恒润提供了车、路、网、云、图全栈式自研解决方案&#xff0c;包含了自动驾驶车辆、路侧车路协同、全场高可靠定位、5G远程驾驶、运营调度管理、数字孪生、仿真系统、 高精地图等专业模块&#xff0c;组成了一套完整的智慧港口解决方案…

sqlmap性能优化

sqlmap性能优化 &#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f32d;&#x1f32d;&#…

查看springboot starter提供的jar包默认版本

1.找到parent的版本&#xff0c;ctrl点进去 2.在spring-boot-dependencies继续点版本号 3.最终文件为spring-boot-dependencies-版本.pom

Git将本地项目上传到Gitee仓库

1.右键点击文件&#xff0c;点击Git Bash Here,进入git窗口 2.初始化本地仓库 git init3.将本地仓库与远程仓库建立连接 git remote add origin 远程仓库地址远程仓库地址在gitee仓库复制即可 4.将远程仓库的文件拉到本地仓库中 git pull origin master5.将本地文件全部上传…

数据结构与算法之美学习笔记:45 | 位图:如何实现网页爬虫中的URL去重功能?

目录 前言算法解析总结引申 前言 本节课程思维导图&#xff1a; 网页爬虫是搜索引擎中的非常重要的系统&#xff0c;负责爬取几十亿、上百亿的网页。爬虫的工作原理是&#xff0c;通过解析已经爬取页面中的网页链接&#xff0c;然后再爬取这些链接对应的网页。而同一个网页链接…

Windows内存管理(一):Windows性能监视器(PerfMon)

一、什么是性能监视器 什么是性能监视器&#xff1f; (What is Performance Monitor? )很多时候&#xff0c;我们的计算机只是停止响应、意外关闭或行为异常。这种行为可能有多种原因&#xff0c;指出确切原因可能会有很大帮助。Windows有一个名为Performance Monitor的工具&…