Spring Data Mongo DB去掉插入的_class字段分析

大致的框架是从网上找来的资源。但是遇到了两个问题:运行代码后,MongoDB数据库没有收到改变。想起了在yaml中配置的mongodb参数,那些参数,data的上一层是spring,如下:spring boot在加载这些数据时,会得到一个MongoProperties。按住Ctrl,然后点在那些值上

大致的框架是从网上找来的资源。但是遇到了两个问题:

运行代码后,MongoDB数据库没有收到改变。 想起了在yaml中配置的mongodb参数,那些参数,data的上一层是spring,如下:

在这里插入图片描述

spring boot在加载这些数据时,会得到一个MongoProperties。按住Ctrl,然后点在那些值上,然后点进去后,就出来了。

在这里插入图片描述

该类的属性如下:

在这里插入图片描述

所以Spring Boot的加载过程中,会产生这样一个bean,通过注入的方式,拿到该类,便可以拿到相应的mongo db配置。再通过一定的分析,将配置信息传给自定义的MongoTemplate。

其中使用@Deprecated方法。 这里换成了其中的没有@Deprecated的构造方法。

先给出可以忽略掉_class字段的配置如下:

@Configuration
public class SpringMongoConfig {

  @Autowired
  public MongoProperties mongoProperties;

  public @Bean MongoDbFactory mongoDbFactory() throws Exception {
    // MongoClientURI uri = new MongoClientURI("mongodb://"+mongoProperties.getUsername()+":"+new String(mongoProperties.getPassword())+"@"+mongoProperties.getHost()+"/"+mongoProperties.getDatabase());
    return new SimpleMongoDbFactory(mongoProperties.createMongoClient(null, null), mongoProperties.getDatabase());
  }

  public @Bean MongoTemplate mongoTemplate() throws Exception {
    //remove _class
    MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()), new MongoMappingContext());
    converter.setTypeMapper(new DefaultMongoTypeMapper(null));

    return new MongoTemplate(mongoDbFactory(), converter);
  }
}

从源代码去分析这个过程

从save()开始:

// 从MongoTemplate的save方法开始
public void save(Object objectToSave) {
	Assert.notNull(objectToSave, "Object to save must not be null!");
	save(objectToSave, determineEntityCollectionName(objectToSave));
}
// 获取到集合的名字后,再存储
public void save(Object objectToSave, String collectionName) {

	Assert.notNull(objectToSave, "Object to save must not be null!");
	Assert.hasText(collectionName, "Collection name must not be null or empty!");

	MongoPersistentEntity<?> mongoPersistentEntity = getPersistentEntity(objectToSave.getClass());

	// No optimistic locking -> simple save
	if (mongoPersistentEntity == null || !mongoPersistentEntity.hasVersionProperty()) {
		doSave(collectionName, objectToSave, this.mongoConverter);
		return;
	}

	doSaveVersioned(objectToSave, mongoPersistentEntity, collectionName);
}
// 存储的主要步骤
protected <T> void doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {

	maybeEmitEvent(new BeforeConvertEvent<T>(objectToSave, collectionName));
	assertUpdateableIdIfNotSet(objectToSave);
	// 加上_class的语句在这里
	DBObject dbDoc = toDbObject(objectToSave, writer);
	// 执行词语后,debug显示的数据如下图
	maybeEmitEvent(new BeforeSaveEvent<T>(objectToSave, dbDoc, collectionName));
	Object id = saveDBObject(collectionName, dbDoc, objectToSave.getClass());

	populateIdIfNecessary(objectToSave, id);
	maybeEmitEvent(new AfterSaveEvent<T>(objectToSave, dbDoc, collectionName));
}

执行toDbObject(objectToSave, writer);后,debug显示的数据如下:

在这里插入图片描述

所以继续toDbObject(objectToSave, writer);里面走:

在这里插入图片描述

所以进入MappingMongoConverterwrite(final Object obj, final DBObject dbo)方法:

public void write(final Object obj, final DBObject dbo) {

	if (null == obj) {
		return;
	}

	Class<?> entityType = obj.getClass();
	boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, DBObject.class) != null;
	TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType);
	// typeMapper在MappingMongoConverter的构造函数中初始化
	if (!handledByCustomConverter && !(dbo instanceof BasicDBList)) {
		typeMapper.writeType(type, dbo);
	}

	Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj;

	writeInternal(target, dbo, type);
}

与typeMapper初始化相关的代码如下:

在这里插入图片描述

因此,打开DefaultMongoTypeMapper,找到writeType方法,但是发现并没有,所以从它的父类DefaultTypeMapper<DBObject>中找。

在这里插入图片描述

再看accessor.writeTypeTo()。这里的accessor在该类初始化的构造器中就已经被初始化,所以我们可以从继承该类的子类DefaultMongoTypeMapper中找到该类的实现,这里的accessor就是DefaultMongoTypeMapper的一个静态内部类。

在这里插入图片描述

这个时候,再回想一下之前的修改,有一种豁然开朗的感觉,哈哈。

Read more

Volcano 与 Kubernetes GPU 调度学习笔记

本笔记系统整理 Volcano 调度器、Kubernetes 调度框架、GPU Device Plugin、HAMi 等云原生 AI 调度领域的核心知识,适合用于学习、复习和工程实践参考。 目录 * 第一部分:Volcano 入门 * 1. Volcano 是什么 * 2. 安装与快速使用 * 3. 核心特性一览 * 第二部分:Volcano 整体架构 * 4. Volcano 解决的核心问题 * 5. 整体架构与数据流 * 6. 三层抽象模型 * 第三部分:Volcano 核心实现原理 * 7. Session 机制 * 8. Gang Scheduling 实现 * 9. Queue 与 DRF 公平调度

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd