【手撕MyBatis源码】MyBatis映射体系

news2024/11/23 20:35:46

文章目录

  • 映射工具MetaObject
    • 基本功能
    • 底层结构
    • 获取属性值的流程
  • ResultMap结果集映射
    • 手动映射
    • 自动映射
  • 嵌套子查询
  • 循环依赖
  • 懒加载
    • 原理
    • 内部结构
    • Bean代理过程
  • 联合查询和嵌套映射
    • 映射说明
    • 联合查询
      • 1对1查询映射
      • 1对多查询
      • RowKey创建机制
      • 结果集解析流程

映射工具MetaObject

所谓映射是指结果集中的列填充至JAVA Bean属性。这就必须用到反射,而Bean的属性多种多样的有普通属性、对象、集合、Map都有可能。为了更加方便的操作Bean的属性,MyBatis提供了MetaObject 工具类,用于操作数据库中表的字段映射成 Java 对象的属性。它提供了一些方法来获取、设置、判断 Java 对象的属性

基本功能

其具体功能如下:

  • 查找属性:勿略大小写,支持驼峰、支持子属性 如:“blog.comment.user_name”
    在这里插入图片描述

  • 获取属性

    • 基于.获取子属性 “user.name”
    • 基于索引获取列表值 “users[1].id”
    • 基于key获取map值 “user[name]”
  • 设置属性:

    • 可设置子属性值
    • 支持自动创建子属性(必须带有空参构造方法,且不能是集合)
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

主要方法有:

  • getGetter(String name):获取属性的 getter 方法
  • getSetter(String name):获取属性的 setter 方法
  • getGetterType(String name):获取属性的类型
  • hasGetter(String name):判断是否有 getter 方法
  • hasSetter(String name):判断是否有 setter 方法
  • getValue(String name):获取属性的值
  • setValue(String name, Object value):设置属性的值
  • findProperty(String name, boolean useCamelCaseMapping):寻找属性,可以指定是否使用驼峰命名法(返回的是String类型)
public class User {
    private int id;
    private String name;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id; 
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

MetaObject metaObject = MetaObject.forObject(user);
// 获取 getter 方法  
Method idGetter = metaObject.getGetter("id");  
// 获取 setter 方法
Method nameSetter = metaObject.getSetter("name");
// 获取 name 属性的值
String name = (String) metaObject.getValue("name");
// 设置 id 属性的值 
metaObject.setValue("id", 1);

底层结构

为了实现上述功能,MetaObject 相继依赖了BeanWrapper、MetaClass、Reflector。这四个对象关系如下:

在这里插入图片描述

  • BeanWrapper: 功能与MeataObject类似,不同点是BeanWrapper只能针对单个当前对象属性进行操作,不能操作子属性。
    • 所以说Metaobject在查找一个属性的属性时,它会递进式的查找属性,再查找属性的属性,这个过程就是通过BeanWrapper查找的,Metaobject用来解析表达式
    • 而BeanWrapper其底层一定是基于反射的,这里就借助了MetaClass
  • MetaClass :类的反射功能支持,能获取整完整类的属性,包括属性的属性,其底层基于Reflector 。
  • Reflector :类的反射功能支持,仅支持当前类的属性。

获取属性值的流程

对象结构如下图:

在这里插入图片描述
获取博客的第一个评论者的名称,其获取表达式是:

"comments[0].user.name"

MetaObjbt 解析获取流程如下图:

在这里插入图片描述

我们可以从这个流程中看到,MetaObject会使用递归将表达式一层层的解析,直到不存在子属性的时候才会将真正的获取工作交给BeanWrapper,BeanWrapper其底层再基于反射拿到属性值。

如果涉及到集合索引,则先获取集合,在基于索引获取集合中的值。这一部分的工作交给了BaseWrapper。

流程中方法说明:

  • MetaObject.getValue():获取属性的值,首先根据属性名"comments[0].user.name" 解析成PropertyTokenizer,并基于属性中的“.” 来判断是否为子属性值,如果是就递归调用getValue()获取子属性对象。然后在递归调用getValue()获取子属性下的属性。直到最后的name属性。

  • MetaObject.setValue():流程与getValue()类似,不同在于如果子属性不存在,则会尝试创建子属性。

我们结合一下源码:

我们可以看到MetaObject中表达式的解析其实依赖的是一个叫做PropertyTokenizer的属性分词器。

在这里插入图片描述
然后这个分词器有一个next方法,根据children生成分词器,以此来达到层层解析的效果。
在这里插入图片描述
在这里插入图片描述

集合这里使用的是BeanWrapper的get方法:

在这里插入图片描述

在这里插入图片描述

ResultMap结果集映射

映射是指返回的ResultSet列与Java Bean 属性之间的对应关系。通过ResultMapping进行映射描述,在用ResultMap封装成一个整体。

在这里插入图片描述

手动映射

映射设置
在这里插入图片描述
在这里插入图片描述

一个ResultMap 中包含多个ResultMapping 表示一个具体的JAVA属性到列的映射,其主要值如下:

在这里插入图片描述
ResultMapping 有多种表现形式如下:

  • constructor:构建参数字段
  • id:ID字段
  • result:普通结构集字段
  • association:1对1关联字段(关联一个对象的时候)
  • Collection:1对多集合关联字段

说一说这里的constructor:
resultMap中的constructor元素用于在结果集映射过程中调用类的构造方法。
当MyBatis构建结果对象时,默认会通过默认构造方法实例化目标类,然后通过setter方法为其赋值。
但是,有时我们的类没有默认构造方法,或者需要通过构造方法设置某些默认值,这时候constructor元素就很有用了。
我们可以在resultMap中配置constructor,指定目标类的构造方法,MyBatis在构建结果对象时就会调用这个构造方法。
constructor的配置语法如下:

<constructor> 
 <idArgumnet index="0" javaType="int"/>
 <arg index="1" javaType="String"/> 
</constructor>
  • idArgumnet:指定构造方法中用于id的那个参数。
  • arg:指定构造方法中的普通参数。
  • index:指定参数在构造方法参数列表中的位置。
  • javaType:指定参数的java类型。

那么MyBatis在构建结果对象时,会找一个匹配的构造方法,通过idArgumnet指定的id参数和arg指定的各个参数来调用构造方法并实例化对象。

举个例子,我们有一个用户类User:

public class User {
 private int id;
 private String name;
 
 public User(int id, String name) {
   this.id = id;
   this.name = name;
 }
}

我们的resultMap可以配置为:

<resultMap id="userMap" type="User">
 <constructor>
   <idArg index="0" javaType="int"/>
   <arg index="1" javaType="String"/>
 </constructor> 
</resultMap> 

那么MyBatis在处理结果集并映射为User对象时,会找到User(int, String)构造方法,并调用User(1, "Tom")来实例化User对象。
而如果没有配置constructor,MyBatis会默认调用无参构造User()来实例化,然后通过setter方法为id和name赋值,代码如下:

User user = new User(); 
user.setId(1);
user.setName("Tom");

可以看出,使用constructor具有一定的性能优势,并且可以避免无参构造方法的情况。
所以,constructor元素为resultMap的结果对象构建过程提供了更丰富的选择,我们可以根据自己的需要选择使用默认构造+setter方法或构造方法来实例化结果对象。这再一次体现了MyBatis灵活性的强大。

在这里插入图片描述

自动映射

当前列名和属性名相同的情况下,可使用自动映射

在这里插入图片描述
自动映射条件

  • 列名和属性名同时存在(勿略大小写、驼峰-下划线
  • 当前列未手动设置映射
  • 属性类别存在TypeHandler
  • 开启autoMapping (默认开启)

自动映射只有在左边三种情况下可以成立,一旦涉及复杂的映射就不行了:
在这里插入图片描述

嵌套子查询

我们先来说说嵌套子查询的由来:
关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。
关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联(当然Collection也可以使用):

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
    在这里插入图片描述

就是这么简单。我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性。
其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。
这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:

  • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
  • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。
好消息是,MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。

  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
    在这里插入图片描述

但很多时候对象结构, 是树级程现的。即对象中包含对象。可以通过子查询获取子对象属性。

在这里插入图片描述
当依次解析Blog中的属性时,会先解析填充普通属性,当解析到复合对象时,就会触发对子查询。

在这里插入图片描述

循环依赖

两个对象之间互相引用即循环引用,如下图就是一个例子:

在这里插入图片描述
对应ResultMap如下:
在这里插入图片描述
这种情况会导致解析死循环吗?答案是不会。DefaultResultSetHandler 在解析复合映射之前都会在上下文中填充当前解析对象(使用resultMapId做为Key)。如果子属性又映射引用了父映射ID,就可以直接获取不需要在去解析父对象。具体流程如下:
在这里插入图片描述
具体代码:
在这里插入图片描述

懒加载

懒加载是为改善,解析对象属性时大量的嵌套子查询的并发问题。设置懒加载后,只有在使用指定属性时才会加载,从而分散SQL请求

<resultMap id="blogMap" type="blog" autoMapping="true">
    <id column="id" property="id"></id>
    <association property="comments" column="id" select="selectCommentsByBlog" fetchType="lazy"/>
</resultMap>

在嵌套子查询中指定 fetchType=“lazy” 即可设置懒加载。在调用getComments时才会真正加载。此外调用:“equals”, “clone”, “hashCode”, “toString” 均会触发当前对象所有未执行的懒加载。通过设置全局参数aggressiveLazyLoading=true ,也可指定调用对象任意方法触发所有懒加载。
在这里插入图片描述

set覆盖 &序列化

当调用setXXX方法手动设置属性之后,对应的属性懒加载将会被移除,不会覆盖手动设置的值。

当对象经过序列化和反序列化之后,默认不再支持懒加载。但如果在全局参数中设置了configurationFactory类,而且采用JAVA原生序列化是可以正常执行懒加载的。其原理是将懒加载所需参数以及配置一起进行序列化,反序列化后在通过configurationFactory获取configuration构建执行环境。

configurationFactory 是一个包含 getConfiguration 静态方法的类

public static class ConfigurationFactory {
        public static Configuration getConfiguration() {
        return configuration;
    }
}

原理

MyBatis提供了association和collection两个标签来配置懒加载,它们的工作原理如下:

  1. association或collection标签配置了fetchType="lazy"时,MyBatis会在查询结果中返回该属性的代理对象。这个代理对象包含了目标属性的数据加载逻辑。
  2. 当我们第一次调用代理对象的getter方法时,代理对象会自己去执行查询加载真实的数据,并将自己替换为真实的数据对象。
  3. 之后再调用getter方法则会直接返回真实的数据对象。

这样就实现了属性的懒加载,只有在真正需要用到该属性时才会执行查询加载数据。

我们来看个例子:

用户和账户的配置:

<resultMap id="userAccountMap" type="User">
  <association property="account" select="selectAccountByUserId" column="id" fetchType="lazy"/> 
</resultMap>

<select id="selectUserById" resultMap="userAccountMap">
  select * from user where id = #{id}
</select>

<select id="selectAccountByUserId" resultType="Account">
  select * from account where user_id = #{id}
</select>

测试代码:

User user = mapper.selectUserById(1);
// 第一次调用account属性的getter方法
Account accout = user.getAccount();  
  • 当执行selectUserById(1)时,会查询出User对象,其account属性会被填充为一个代理对象。
  • 当第一次调用user.getAccount()时,代理对象会执行selectAccountByUserId查询真实的Account数据,并将自己替换为真实的Account对象。
  • 之后再调用getAccount()就会直接返回Account对象。

那么问题来了,这个代理对象是什么?它是如何知道要执行哪个select语句的?

MyBatis使用了JDK动态代理并增强了InvocationHandler来实现懒加载。当fetchType=lazy时,MyBatis会创建以下代理对象:

Proxy.newProxyInstance(
  mapperInterface.getClassLoader(), 
  new Class[] { mapperInterface },
  new LazyLoaderHandler(sqlSession, selectStatement)
)
  • LazyLoaderHandler实现了InvocationHandler接口,它知道实际要执行的select语句。
  • 当调用getter方法时,实际会调用InvocationHandler的invoke方法。
  • invoke方法会先判断属性是否已经被加载,如果未加载则会通过SqlSession执行select语句查询数据,并将结果设置到源对象中。
  • 之后再调用invoke方法就会直接返回已经加载的数据。

在这里插入图片描述

所以总结来说,MyBatis懒加载的原理是:

  1. 通过JDK动态代理生成包含懒加载逻辑的代理对象。
  2. 当第一次调用getter方法时,通过代理对象执行select语句查询数据。
  3. 将查询结果设置至源对象,并返回该结果。
  4. 之后再调用getter方法将直接返回结果,实现懒加载。

JDK动态代理是MyBatis懒加载功能的基础,MyBatis通过增强InvocationHandler来实现懒加载的代理效果。这是MyBatis这项功能的巧妙实现之处。

懒加载让我们可以将关联属性的加载延后到真正需要使用它的时候,这大大提高了数据库交互的效率,同时也减少内存的消耗。它是MyBatis优化数据库性能的重要手段之一。

内部结构

在这里插入图片描述

代理之后Bean会包含一个MethodHandler,内部在包含一个Map用于存放待执行懒加载,执行前懒加载前会移除。LoadPair用于针对反序列化的Bean准备执行环境。ResultLoader用于执行加载操作,执行前如果原执行器关闭会创建一个新的。

特定属性如果加载失败,不会在进行二次加载。

Bean代理过程

代理过程发生在结果集解析 交创建对象之后(DefaultResultSetHandler.createResultObject),如果对应的属性设置了懒加载,则会通过ProxyFactory 创建代理对象,该对象继承自原对象,然后将对象的值全部拷贝到代理对像。并设置相应MethodHandler(原对象直接抛弃)

在这里插入图片描述

联合查询和嵌套映射

映射说明

映射是指返回的ResultSet列与Java Bean 属性之间的对应关系。通过ResultMapping进行映射描述,在用ResultMap封装成一个整体。映射分为简单映射与复合嵌套映射。

简单映射:即返回的结果集列与对象属性是1对1的关系,这种情况下ResultHandler 会依次遍历结果集中的行,并给每一行创建一个对象,然后在遍历结果集列填充至对象的映射属性。

在这里插入图片描述
嵌套映射:但很多时候对象结构, 是树级程现的。即对象中包含对象。与之对应映射也是这种嵌套结构。

在这里插入图片描述
在配置方式上可以直接配置子映射,也以引入外部映射和自动映射。共有两类嵌套结构分别是:

  • 一对多
  • 多对多

在这里插入图片描述
关于映射的使用方式,官网有非常详细的文档。这里就不在赘述。接下来分析一下,嵌套映射结果集填充过程。

联合查询

有了映射之后如何获取结果?普通的单表查询是无法获取复合映射所需结果,这就必须用到联合查询。然后在将联合查询返回的数据列,拆分给不同的对象属性。1对1与1对多拆分和创建的方式是一样的。

1对1查询映射

select a.id,
       a.title,
       b.id as user_id,
       b.name as user_name
from blog a
         left join users b on a.author_id=b.id
where a.id = 1;

通过上述语句联合查询语句,可以得出下表中结果。结果中前两字段对应Blog,后两个字段对应User。然后在将User作为author属性填充至Blog对象。

在这里插入图片描述
在这里插入图片描述
上述两个例子中,每一行都会产生两个对象,一个Blog父对象,一个User子对象。

1对多查询

select a.id,a.title,
       c.id as comment_id,
       c.body as comment_body
from blog a
         left join comment c on a.id=c.blog_id
where a.id = 1;

上述语句可得出三条结果,前两个字段对应Blog,后两个字段对应Comment(评论)。与1对1不同的是,三行指向的是同一Blog。因为它ID都是一样的。

在这里插入图片描述
在这里插入图片描述
上述结果中,相同的三行Blog将会创建一个Blog,同时分别创建三个不同的Comment组成一个集合,并填充至comments对象。

RowKey创建机制

在1对多的查询过程中,是基于RowKey来断定两行数据是否相同的 。RowKey一般基于。但有时并不会指定 这时将会采用其它映射字段创建RowKey具体规则如下:

在这里插入图片描述

结果集解析流程

这里直接采用1对多的情况进行解析,因为1对1就是1对多的简化版。查询的结果如下表:

在这里插入图片描述
其整个解析流程如下图:

在这里插入图片描述

流程说明:

所有映射流程的解析都是在DefaultResultSetHandler当中完成。主要方法如下:

  • handleRowValuesForNestedResultMap():嵌套结果集解析入口,在这里会遍历结果集中所有行。并为每一行创建一个RowKey对象。然后调用getRowValue()获取解析结果对象。最后保存至ResultHandler中。

    • 注:调用getRowValue前会基于RowKey获取已解析的对象,然后作为partialObject参数发给getRowValue
  • getRowValue():该方法最终会基于当前行生成一个解析好对象。具体职责包括,1.创建对象、2.填充普通属性和3.填充嵌套属性。在解析嵌套属性时会以递归的方式在调用getRowValue获取子对象。最后一步4.基于RowKey 暂存当前解析对象。

    • 如果partialObject参数不为空 只会执行 第3步。因为1、2已经执行过了。
  • applyNestedResultMappings():解析并填充嵌套结果集映射,遍历所有嵌套映射,然后获取其嵌套ResultMap。接着创建RowKey 去获取暂存区的值。然后调用getRowValue 获取属性对象。最后填充至父对象。

    • 如果通过RowKey能获取到属性对象,它还是会去调用getRowsValue,因为有可能属下还存在未解析的属性。

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

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

相关文章

【4】Midjourney常用技巧

【常用技巧】 本篇主要讲述MJ的常用技巧&#xff0c;围绕着一些常用指令的使用方法展开。 【版本切换】 在使用MJ时&#xff0c;最常用的技巧之一是版本切换。你可以在输入提示后添加"--v"加上相应的数字来实现版本切换。通常我默认使用MJ 4&#xff0c;偶尔会使用…

《精通特征工程》学习笔记(2):文本数据:扁平化、过滤和分块

1.元素袋&#xff1a;将自然文本转换为扁平向量 1.1 词袋 词袋将一个文本文档转换为一个扁平向量。之所以说这个向量是“扁平”的&#xff0c;是因为它 文本数据&#xff1a;扁平化、过滤和分块&#xff5c;35不包含原始文本中的任何结构。原始文本是一个单词序列&#xff0c…

【小沐学Python】Python实现绘画(海龟turtle)

文章目录 1、简介2、接口说明2.1 海龟动作2.1.1 移动和绘制2.1.2 获取海龟的状态 2.2 画笔控制2.2.1 绘图状态2.2.2 颜色控制2.2.3 填充2.2.4 更多绘图控制 2.3 TurtleScreen/Screen 方法2.3.1 窗口控制2.3.2 使用屏幕事件2.3.3 Screen 专有方法 3、示例测试3.1 Turtle star3.2…

[starrocks BE] 启动报错问题记录

文章目录 一、一句话描述二、问题表象1. starrocks_be的启动现象&#xff1a;2. starrocks_fe的启动现象 一、一句话描述 starrocks部署在没有AVX2指令集的机器上导致部署失败&#xff0c;解决方式更换支持AVX2指令集的机器。 官方说明&#xff1a; 二、问题表象 starrocks所…

Rocket面试(五)Rocketmq发生流量控制的情况有哪些?

在使用rocketmq过程中总能看见一下异常 [TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: 206ms, size of queue: 5这是因为Rocketmq出发了流量控制。 触发流量控制就是为了防止Broker压力过大挂掉。主要分为Broker流控&#xff0c;Consu…

全志V3S嵌入式驱动开发(开发环境再升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们陆陆续续开发了差不多有10个驱动&#xff0c;涉及到网口、串口、音频和视频等几个方面。但是整个开发的效率还是比较低的。每次开发调试的…

【MySQL 数据库】8、视图

目录 一、什么是视图二、视图语法三、检查选项(1) cascaded&#xff08;级联&#xff09;(2) local 四、视图的作用五、视图案例 一、什么是视图 视图&#xff08;View&#xff09;是一种虚拟存在的表视图中的数据并不在数据库中真实存在行和列数据来自定义视图的查询中使用的…

一棵有点自律的树——搜索二叉树

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f337;搜索二叉树概念&#x1f337;二叉搜索树的构建&#x1f33a;查找操作&#x1f33a;插入操作&#x1f33a;删除操作&#x1f33a;遍历操作☘️测试 &#x1f3f5;️拓展——递归实现&#x1f343;递归查找&…

数据结构与算法·第9章【查找】

概念 关键字&#xff1a; 是数据元素&#xff08;或记录&#xff09;中某个数据项的值&#xff0c;用以标识&#xff08;识别&#xff09;一个数据元素&#xff08;或记录&#xff09;。若此关键字可以识别唯一的一个记录&#xff0c;则称之谓“主关键字”。若此关键字能识别…

2.3 利用MyBatis实现关联查询

一、创建数据库表 1、创建教师表 执行SQL语句&#xff0c;创建教师表t_teacher CREATE TABLE t_teacher ( t_id int(11) NOT NULL AUTO_INCREMENT, t_name varchar(20) DEFAULT NULL, PRIMARY KEY (t_id) ) ENGINEInnoDB AUTO_INCREMENT4 DEFAULT CHARSETutf8mb4;执行SQL语句…

使用OpenFlow和Ryu控制器实现网络交换机的软件定义网络(SDN)控制

使用OpenFlow和Ryu控制器实现网络交换机的软件定义网络&#xff08;SDN&#xff09;控制 &#xff08;1&#xff09;环境介绍 硬件环境&#xff1a;系统最低要求为2个CPU 、2 GB内存。 拓扑介绍&#xff1a;云平台具体安装拓扑如图5-4所示。 图5-4 云平台安装拓扑 搭建云平…

使用pipreqs生成requirements文件,并在服务器(矩池云)上通过requirements文件安装环境采坑记录

目录 问题描述问题1&#xff1a;问题2&#xff1a;发现问题问题解决 问题3&#xff1a;问题4&#xff1a;问题5&#xff1a;解决方案 关键&#xff01;&#xff01;&#xff01;正常安装成功的操作流程备注1.我为何不在vscode的终端中装pipreqs包&#xff1f;2.在vscode终端中输…

Spring Cloud构建微服务架构:服务注册与发现

Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具&#xff0c;它为基于JVM的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。 Spring Cloud包…

面了一个来华为要22K的人,啥都不会,还不如找个应届生来代替···

最近有个在华为的朋友跟我分享了他面试招人的过程&#xff0c;感觉华为还是挺难进的。面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试。 其实&#xff0c;第一轮的电话面试除…

ASEMI代理英飞凌TLE7244SL功率电子开关,TLE7244SL参数

编辑-Z TLE7244SL参数描述&#xff1a; 型号&#xff1a;TLE7244SL 数字电源电压VDD&#xff1a;3.0 V ~ 5.5 V 模拟电源电压VDDA&#xff1a;4.5 V ~ 5.5 V 每个通道在Tj150C时的最大导通状态电阻RDS(ON,max)&#xff1a;1.7 Ω 额定负载电流IL (nom)&#xff1a;290 mA…

Nginx【反向代理负载均衡动静分离】--中

Nginx【反向代理负载均衡动静分离】–中 负载均衡-配置实例 示意图 负载均衡配置-思路分析/图解 示意图 负载均衡配置规则 负载均衡就是将负载分摊到不同的服务单元&#xff0c;既保证服务的可用性&#xff0c;又保证响应足够快 linux 下有Nginx、LVS、Haproxy 等等服务可…

在Apifox中,使用后置脚本显示响应结果reponse中的base64图片

背景 在使用Apifox去请求有图片的接口时&#xff0c;我想要请求成功的同时&#xff0c;可以显示出来图片&#xff0c;这个时候就开始百度找官方文档。最终发现可以使用后置脚本显示reponse中的图片。 方案 如下图所示&#xff0c;接口请求成功后&#xff0c;返回的json结构为…

【Spring Boot 初识丨四】主应用类

上一篇讲了 Spring Boot 的启动器 本篇来讲一讲 主程序类 Main Application Class 及注解 Spring Boot 初识&#xff1a; 【Spring Boot 初识丨一】入门实战 【Spring Boot 初识丨二】maven 【Spring Boot 初识丨三】starter 主程序类 一、定义二、注解2.1 SpringBootApplicati…

秋招指南(菜狗版)-Java前/后端开发方向

期末考试结束&#xff0c;菜的人还在享受假期&#xff0c;即将进大厂的已经在学习了&#xff08;狗头&#xff09; 作为经受去年秋招摧残的老学姐&#xff0c;给大家带来一些秋招学习的小经验&#xff0c;希望可以帮助大家避免一些求职路上的坑&#xff0c;能快速顺利地找到心仪…

论文笔记与实战:对比学习方法MOCO

目录 1. 什么是MOCO2. MOCO是干吗用的3. MOCO的工作原理3.1 一些概念1. 无监督与有监督的区别2. 什么是对比学习3. 动量是什么 3.2 MOCO工作原理1. 字典查找2. 如何构建一个好的字典3. 工作流程 3.3 &#xff08;伪&#xff09;代码分析 4. 其他一些问题5. MOCO v2和MOCO v35.1…