Spring高手之路21——深入剖析Spring AOP代理对象的创建

news2024/9/21 16:28:48

文章目录

  • 创建代理对象核心动作的三个步骤
    • 1. 判断 Bean 是否需要增强(源码分析+时序图说明)
    • 2. 匹配增强器 Advisors(源码分析+时序图说明)
    • 3. 创建代理对象(源码分析+时序图说明)

创建代理对象核心动作的三个步骤

本文将详细介绍创建代理对象的三个核心步骤。关于AOP的基本调试,可以参考前文介绍的调试代码(任何涉及AOP的代码均可,如前置通知),这里不再详细说明。

1. 判断 Bean 是否需要增强(源码分析+时序图说明)

本节源码基于 spring-aop-5.3.16

  在Spring AOP中,这一步骤主要通过检查目标bean是否实现了特定接口或已是代理对象来完成。关键的方法是在AbstractAutoProxyCreator类的postProcessBeforeInstantiation中实现,该方法是 Spring AOP 的核心,属于 Spring Bean 生命周期的一部分,特别是在后置处理器(BeanPostProcessor)机制中起重要作用。

主要功能:

  • 拦截Bean的创建:在实际的 Bean 实例化之前拦截创建过程,这使得开发者可以在对象实际创建之前注入特定的行为或逻辑。
  • 条件判断:基于特定条件(例如Bean的类型或注解)来确定是否需要对该 Bean 应用代理或其他增强,特定条件比如(切点表达式)
  • 创建代理:如果条件满足,这个方法可以用来创建一个代理实例代替原来的 Bean,以便在运行时应用如安全、事务、日志等横切关注点。

作用流程:

1. 获取缓存键:首先通过 Bean 的类和名称构造一个缓存键,用于后续的快速查找和决策。
2. 初步检查:检查缓存是否已经有该 Bean 的信息,检查Bean是否为基础设施类或是否标记为不应代理。如果缓存中未找到对应键且Bean需要代理,将进入代理创建步骤。
3. 决定是否创建代理:如果 Bean 不在上述类别中,进一步检查是否存在自定义的 TargetSource(一个控制如何获取或创建被代理对象的组件)。如果存在,表示这个 Bean 需要被增强或代理。
4. 代理创建:如果需要,创建一个代理实例,这个代理会封装原始 Bean,加入额外的行为如拦截方法调用。
5. 结果返回:返回创建的代理实例或者在不需要代理的情况下返回 null

来看看对应的源码:
在这里插入图片描述

源码提出来分析:

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    // 获取beanClass和beanName组合的缓存键
    Object cacheKey = getCacheKey(beanClass, beanName);

    // 检查beanName是否有效,并且当前bean是否已经有一个自定义的TargetSource
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 如果当前bean已经被处理过,直接返回null,不再处理
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 判断当前类是否是基础设施类或者是否应该跳过代理
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            // 标记为不需要代理,并返回null
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // 获取自定义的TargetSource,如果存在,则创建代理
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        // 如果beanName有效,将其加入到管理自定义TargetSource的集合中
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        // 获取适用于当前bean的advice和advisor
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        // 创建代理对象
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        // 缓存代理对象的类型
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

源码分析说明:

  • 获取缓存键:通过 beanClassbeanName 组合生成缓存键。
  • 检查 beanName 和 TargetSource:确保 beanName 有效且 bean 未被处理过,若已处理则返回 null
  • 判断基础设施类或跳过代理:检查 bean 是否为基础设施类或应跳过代理,若是则返回 null
  • 获取自定义 TargetSource 并创建代理:如果存在自定义 TargetSource,则创建代理对象并缓存其类型。

这里为什么要获取自定义的 TargetSource?

  在Spring AOP中,创建代理对象时,TargetSource起着关键作用。它主要决定了如何获取或创建将被代理的目标对象。默认情况下,Spring 使用简单的目标源,即直接引用具体的 Bean 实例。但在某些情况下,开发者可能需要通过自定义TargetSource来改变目标对象的获取逻辑,以适应特定的增强需求。

  在AbstractAutoProxyCreator类的postProcessBeforeInstantiation方法中,我们可以决定是否使用自定义的TargetSource。如果存在,这意味着Bean需要特殊的处理或增强。自定义的TargetSource还可以实现很多复杂的逻辑,比如:

  • 池化目标对象:为了提高性能,可以使用对象池来管理目标对象的实例。
  • 延迟初始化:只有在真正需要时才创建目标对象,可以减少资源使用和启动时间。
  • 远程对象访问:目标对象可能在远程服务器上,需要通过网络调用。
  • 多租户支持:基于当前用户或会话信息返回不同的目标对象实例。

用时序图表示如下:
在这里插入图片描述
总体流程
  这个时序图描述了Spring AOP在创建代理对象时的核心过程。主要涉及的组件包括:调用者、postProcessBeforeInstantiation方法、缓存与条件检查、自定义TargetSource管理以及代理创建与返回。

步骤解析
1. 调用开始

  • 调用者调用postProcessBeforeInstantiation方法,开始代理对象的创建过程。
  • 这个方法主要负责在bean实例化之前判断并创建其代理。

2. 获取缓存键和初步条件检查:

  • 方法首先通过beanClassbeanName获取一个cacheKey
  • 接着检查beanName是否有效(非空且长度大于0)和当前bean是否已经有自定义的TargetSource
  • 如果bean已经存在于advisedBeans缓存中,或者属于基础设施类(如配置类等),或已指定为跳过代理,则不会进行进一步处理。

3. 尝试获取自定义TargetSource

  • 如果通过了初步的条件检查,将尝试获取一个针对当前bean的自定义TargetSource
  • 这一步是检查是否有特定于该bean的增强配置,如果有,则可以继续创建代理。

4. 代理对象的创建:

  • 如果存在自定义的TargetSource,则使用相关的advisors(增强器)和这个TargetSource来创建一个代理对象。
  • 这个代理对象将能够在运行时拦截对bean的调用,并应用定义的增强逻辑(如安全检查、事务管理等)。

5. 返回结果:

  • 如果成功创建了代理对象,则返回这个对象给调用者。
  • 如果没有自定义的TargetSource或者不需要创建代理,方法将返回null

条件判断

  • 缓存键不存在或bean需要代理:这个分支处理创建代理所需的条件检查和配置获取。
  • 自定义TargetSource存在:只有当自定义的TargetSource存在时,才会尝试创建代理对象。
  • 自定义TargetSource不存在或缓存键存在且bean不需要代理:这些情况将导致方法返回null,不进行代理的创建。

2. 匹配增强器 Advisors(源码分析+时序图说明)

  增强器(或称为 “advisors”)定义何时以及如何增强目标对象。
  源码中的AbstractAdvisorAutoProxyCreator类继承自AbstractAutoProxyCreator,添加处理Advisor的功能。特别是findEligibleAdvisors方法,它用于找出适用于特定bean的所有advisors

来看看源码

在这里插入图片描述

提取关键代码分析

// 重写getAdvicesAndAdvisorsForBean方法,用于获取适用于特定bean的advisors和advices
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

    // 调用findEligibleAdvisors方法获取所有适用的advisors
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 如果没有找到任何适用的advisor,则返回DO_NOT_PROXY(不进行代理)
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 将找到的advisors转换为数组并返回
    return advisors.toArray();
}

/**
 * 查找所有适合自动代理的advisors。
 * @param beanClass 需要查找advisors的bean的类
 * @param beanName 当前被代理的bean的名称
 * @return 如果没有找到适用的pointcuts或interceptors,返回空列表,而不是null
 * @see #findCandidateAdvisors 查找候选的advisors
 * @see #sortAdvisors 对advisors进行排序
 * @see #extendAdvisors 扩展advisors
 */
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 查找所有候选的advisors
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 从候选advisors中找出可以应用于当前bean的advisors
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 可选操作:扩展advisors列表
    extendAdvisors(eligibleAdvisors);
    // 如果找到了适用的advisors,则对它们进行排序
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    // 返回最终确定的适用于当前bean的advisors列表
    return eligibleAdvisors;
}

用时序图表示如下:

在这里插入图片描述

时序图详解

1. 客户端请求Bean实例:

  • 客户端向AbstractAutoProxyCreator发出请求以获取Bean实例。这通常发生在Spring的应用上下文中,当一个Bean被请求时,Spring会检查这个Bean是否需要代理。

2. 调用findEligibleAdvisors方法:

  • AbstractAutoProxyCreator调用findEligibleAdvisors方法,传入beanClassbeanName。这个方法负责找出所有可能适用于这个特定BeanAdvisors

3. 查找所有候选Advisors:

  • findEligibleAdvisors进一步调用findCandidateAdvisors,这个方法从Spring的应用上下文中检索所有配置的Advisors

4. 筛选适用于Bean的Advisors:

  • 返回的候选Advisors列表会传给findAdvisorsThatCanApply,这个方法根据当前Bean的类型和名称筛选出适用的Advisors

5. 对Advisors进行排序:

  • 适用的Advisors会传给sortAdvisors,以确保在代理中按正确的顺序应用它们。

6. 返回适用的Advisors列表:

  • findEligibleAdvisors将排序后的Advisors列表返回给AbstractAutoProxyCreator

7. 决策点 - 是否存在适用的Advisors:

  • 如果找到适用的Advisors,将继续创建Bean的代理;如果没有找到,直接返回原始的Bean实例。

8. 创建Bean的代理:

  • 代理工厂根据返回的Advisors创建Bean的代理实例,并将Advisors注入到代理中。

9. 客户端调用Bean的方法:

  • 客户端通过代理实例调用Bean的方法。如果Bean被代理,Advisors中定义的额外逻辑(例如,拦截、事务管理)会在调用实际Bean方法之前或之后执行。

10. 返回方法执行结果:

  • 方法执行完成后,结果通过代理返回给客户端。

3. 创建代理对象(源码分析+时序图说明)

  如果发现有合适的advisorsSpring将使用AOP代理工厂来创建代理对象。这部分的处理通常涉及到多种代理的创建策略,如JDK动态代理或CGLIB代理。

  查看AbstractAutoProxyCreator中的createProxy方法。这个方法负责根据给定的bean、其对应的bean名称、匹配到的advisors等信息来创建代理对象。

源代码如下:
在这里插入图片描述

提出代码分析:

/**
 * 创建给定bean的AOP代理。
 * @param beanClass bean的类
 * @param beanName bean的名称
 * @param specificInterceptors 针对这个bean的一组拦截器(可能为空,但不为null)
 * @param targetSource 为代理配置的目标源
 * @return bean的AOP代理
 * @see #buildAdvisors 构建advisor的方法
 */
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    // 检查bean工厂是否为ConfigurableListableBeanFactory类型,如果是,则暴露bean的目标类
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // 创建ProxyFactory对象,并从当前实例复制配置
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    // 判断是否应该使用代理目标类,而不是只使用接口代理
    if (proxyFactory.isProxyTargetClass()) {
        // 如果beanClass是一个JDK代理类,处理引入通知场景
        if (Proxy.isProxyClass(beanClass)) {
            // 允许引入,不能仅设置接口为代理的接口
            for (Class<?> ifc : beanClass.getInterfaces()) {
                proxyFactory.addInterface(ifc);
            }
        }
    } else {
        // 如果没有强制代理目标类标志,则进行默认检查
        if (shouldProxyTargetClass(beanClass, beanName)) {
            // 如果决定代理目标类,则设置相应标志
            proxyFactory.setProxyTargetClass(true);
        } else {
            // 否则,评估并设置需要代理的接口
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 构建适用于此bean的advisors数组
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 将advisors添加到代理工厂
    proxyFactory.addAdvisors(advisors);
    // 设置代理的目标源
    proxyFactory.setTargetSource(targetSource);
    // 根据需要自定义代理工厂
    customizeProxyFactory(proxyFactory);

    // 设置代理是否冻结,即之后是否允许更改代理的配置
    proxyFactory.setFrozen(this.freezeProxy);
    // 如果advisors已经预过滤,设置代理为预过滤状态
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 获取代理的类加载器
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
        // 如果类加载器为SmartClassLoader且与bean的类加载器不同,使用原始类加载器
        classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }
    // 获取并返回代理对象
    return proxyFactory.getProxy(classLoader);
}

时序图如下:
在这里插入图片描述

时序图详解:

1. 客户端请求创建代理

  • 动作:客户端向Spring容器请求创建一个代理对象。这通常是因为客户端需要一个被AOP增强的Bean,比如添加了事务管理、性能监控或安全控制等。
  • 背景:这通常发生在Spring应用启动时或者第一次请求Bean时,Spring容器会根据Bean的定义和AOP配置决定是否需要创建代理。

2. Spring容器调用createProxy方法

  • 动作:Spring容器调用createProxy方法开始代理创建过程。
  • 背景:createProxy是实现代理逻辑的核心方法,它负责集成所有相关配置并生成代理对象。

3. 检查BeanFactory类型并暴露目标类

  • 动作:createProxy方法首先检查Bean工厂的类型,如果是ConfigurableListableBeanFactory,则调用AutoProxyUtils.exposeTargetClass来暴露Bean的目标类。
  • 目的:这一步骤是为了在需要时能够获取Bean的实际类信息,尤其是当代理需要基于类而非接口创建时。

4. 创建ProxyFactory实例

  • 动作:createProxy方法创建一个ProxyFactory实例。
  • 目的:ProxyFactory是用于创建实际代理对象的工具,它支持对代理的各种配置,如代理的类型、拦截器链等。

5. 判断并处理代理策略

  • 动作:根据是否使用代理目标类来决定代理方式,包括是否为JDK动态代理或CGLIB代理。
  • 条件分支:
    • 如果目标类已是JDK代理类,将添加所有实现的接口到代理。
    • 如果不是JDK代理类,将根据shouldProxyTargetClass的结果决定是否代理目标类或仅代理特定接口。

6. 构建Advisors并配置ProxyFactory

  • 动作:调用buildAdvisors方法构建适用于此Beanadvisors数组,然后将这些advisors添加到ProxyFactory
  • 目的:Advisors包含了增强的定义,这些增强定义了如何拦截方法调用及在调用前后执行特定的操作。

7. 自定义ProxyFactory并创建代理对象

  • 动作:设置代理的目标源、自定义配置,冻结配置以确保在运行时不被修改,设置预过滤以优化匹配过程,最后通过ProxyFactory获取代理对象。
  • 目的:完成所有代理配置后,最终生成代理对象,该对象将在运行时代表原始Bean,增加了指定的AOP功能。

8. 返回代理对象

  • 动作:createProxy方法将代理对象返回给Spring容器,容器再返回给客户端。
  • 结果:客户端接收到的Bean是一个被代理增强过的对象,具备了额外的AOP功能,如事务控制、安全检查等。

欢迎一键三连~

有问题请留言,大家一起探讨学习

----------------------Talk is cheap, show me the code-----------------------

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

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

相关文章

C++模版基础知识与STL基本介绍

目录 一. 泛型编程 二. 函数模板 1. 概念 2. 函数模版格式 3. 函数模版的原理 4. 模版函数的实例化 (1). 隐式实例化 (2.) 显式实例化 5. 模版参数的匹配原则 三. 类模板 1. 类模板的定义格式 2. 类模板的实例化 四. STL的介绍 1. 什么是STL&#xff1f; 2. STL的版…

netty入门-7 ByteBuf

文章目录 前言ByteBuf结构池化与非池化创建&#xff08;直接内存/堆内存&#xff09;写入和读取释放零拷贝&#xff0c;slice&#xff0c;duplicate&#xff0c; copy&#xff0c;Composite 结语 前言 ByteBuf这部分视频讲的更为详细。 主要参考视频。 ByteBuf 结构 首先这…

C# Task.WaitAll 的用法

目录 简介 1.WaitAll(Task[], Int32, CancellationToken) 2.WaitAll(Task[]) 3.WaitAll(Task[], Int32) 4.WaitAll(Task[], CancellationToken) 5.WaitAll(Task[], TimeSpan) 结束 简介 Task.WaitAll 是 C# 中用于并行编程的一个的方法&#xff0c;它属于 System.Threa…

开始尝试从0写一个项目--后端(三)

器材管理 和员工管理基本一致&#xff0c;就不赘述&#xff0c;展示代码为主 新增器材 表设计&#xff1a; 字段名 数据类型 说明 备注 id bigint 主键 自增 name varchar(32) 器材名字 img varchar(255) 图片 number BIGINT 器材数量 comment VARC…

Elasticsearch 使用误区之三——分片设置不合理

Elasticsearch 是一个强大的搜索和分析引擎&#xff0c;它通过将数据分散到多个节点的分片中来进行分布式处理。 本文将探讨分片大小和策略的概念&#xff0c;以优化 Elasticsearch 的性能并防止过度分片或分片过大等问题。 先看个分片设置不合理的真实企业案例&#xff1a; 10…

陶晶驰串口屏使用记录与教程

首先把串口屏想象成和正点原子usmart调试程序一样的程序&#xff0c;串口屏主芯片有些是GD32 STM32都是主流单片机&#xff0c;里面下载了一些固件形成了现在的操作系统 其实我更喜欢把他们&#xff08;usmart&#xff0c;串口屏主程序&#xff0c;micropython&#xff0c;at指…

uniapp vue3 使用画布分享或者收藏功能

使用HBuilder X 开发小程序&#xff0c;大多数的画布插件很多都是vue2的写法&#xff0c;vue3的很少 我自己也试了很多个插件&#xff0c;但是有一些还是有问题&#xff0c;不好用 海报画板 - DCloud 插件市场 先将插件导入项目中 自己项目亲自用过&#xff0c;功能基本是完善…

GraphRAG:基于实体的本地搜索方法:知识图谱与非结构化数据的融合

GraphRAG&#xff1a;基于实体的本地搜索方法:知识图谱与非结构化数据的融合 在自然语言处理和信息检索领域,如何有效地结合结构化知识和非结构化文本数据一直是一个重要的研究方向。本文介绍一种基于实体的本地搜索方法,该方法巧妙地融合了知识图谱中的结构化数据和输入文档中…

优化冗余代码:提升前端项目开发效率的实用方法

目录 前言代码复用与组件化模块化开发与代码分割工具辅助与自动化结束语 前言 在前端开发中&#xff0c;我们常常会遇到代码冗余的问题&#xff0c;这不仅增加了代码量&#xff0c;还影响了项目的可维护性和开发效率。还有就是有时候会接到紧急业务需求&#xff0c;要求立马完…

打造一篇完美的【数学建模竞赛论文】:从准备到撰写的全面指南

目录 一、赛前准备 1.1 报名与纪律要求 1.2 MD5码上传 1.3 竞赛准备 1.4 时间分配 二、论文格式规范 2.1 摘要 2.2 参考文献 2.3 排版要求 三、建模过程与方法 3.1 问题分析与模型假设 3.2 模型构建与求解 3.3 结果分析与检验 四、论文撰写技巧 4.1 论文结构 4…

Redisson中分布式锁继承体系

直接上图 画了好久 关于非公平锁和公平锁中差异化函数如tryLockInnerAsyc 和unsubscribe还没有时间进行探索&#xff0c;这应该是公平锁和非公平锁之间的差异所在。 说一说Redisson中的类之间关系设计 参考抽象类实现接口_一个抽象之类 如果要实现某个接口怎么办-CSDN博客 众…

电脑文件误删除如何恢复?数据恢复第一步是什么?这五点要第一时间处理!

电脑文件误删除如何恢复&#xff1f;数据删除恢复的第一时间要做什么&#xff0c;你知道吗&#xff1f; 在使用电脑的过程中&#xff0c;误删除重要文件的情况时有发生。面对这种情况&#xff0c;不必过于慌张&#xff0c;因为有多种方法可以帮助你恢复误删除的文件。以下是恢复…

金字塔监督在人脸反欺骗中的应用

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2011.12032.pdf 近年来&#xff0c;人脸识别技术越来越普及。在智能手机解锁和进出机场时&#xff0c;理所当然地会用到它。人脸识别也有望被用于管理今年奥运会的相关人员。但与此同时&#xff0c;人们对人脸欺骗的关注度也…

醒醒,别睡了...讲《数据分析pandas库》了—/—<3>

直接上知识点 一、 1、新建数据框时建立索引 所有的数据框默认都已经使用从 0 开始的自然数索引&#xff0c;因此这里的"建立”索引指的是自定 df pd.DataFrame( {varl : 1.0, var2 :[1,2,3,4], var3 :[test,python,test,hello] , var4 : cons} , index [0,1,2,3]) …

【ESP32 IDF SPI硬件驱动W25Q64】

目录 SPISPI介绍idf配置初始化配置通信 驱动代码 SPI SPI介绍 详细SPI介绍内容参考我之前写的内容【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】 idf配置 初始化配置 spi_bus_initialize() 参数1 &#xff1a;spi几&#xff0c;例如spi2,spi3 参数2&#xff1a;…

MySQL体系结构与查询执行流程详解

MySQL 体系结构与查询执行过程详解 MySQL 是一个采用单进程多线程架构模式的关系型数据库管理系统。本文将详细介绍 MySQL 的体系结构及其查询语句的执行过程,并探讨性能优化的关键点。 MySQL 体系结构 MySQL 的架构为 Client-Server 架构。总体上,我们可以将 MySQL 的体系…

python—pandas基础(2)

文章目录 列操作修改变量列筛选变量列使用.loc[]&#xff08;基于标签)使用.iloc[]&#xff08;基于整数位置&#xff09;使用.filter()方法 删除变量列添加变量列 变量类型的转换Pandas 支持的数据类型在不同数据类型间转换 建立索引新建数据框时建立索引读入数据时建立索引指…

如何在宝塔面板给域名配置 SSL 证书

首先需要有证书 这里以阿里云为例 1. 首先进入到 SSL 证书管理控制台 选择个人测试证书&#xff0c;并点击购买 免费的可以使用三个月。 购买完成之后回到控制台。 点击创建证书&#xff0c;将标红的地方填写&#xff0c;其他默认就好。 然后提交审核就行。 这里需要对域名…

JS逆向高级爬虫

JS逆向高级爬虫 JS逆向的目的是通过运行本地JS的文件或者代码,以实现脱离他的网站和浏览器,并且还能拿到和浏览器加密一样的效果。 10.1、编码算法 【1】摘要算法&#xff1a;一切从MD5开始 MD5是一个非常常见的摘要(hash)逻辑. 其特点就是小巧. 速度快. 极难被破解. 所以,…

图像生成中图像质量评估指标—FID介绍

文章目录 1. 背景介绍2. 实际应用3. 总结和讨论 1. 背景介绍 Frchet Inception Distance&#xff08;\textbf{FID}&#xff09;是一种衡量生成模型性能的指标&#xff0c;它基于Inception网络提取的特征来计算模型生成的图像与真实图像集合之间的距离。 FID利用了Inception模…