diff --git a/Jenkinsfile b/Jenkinsfile
index 0e83b47e2f..59898e5d9b 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -9,7 +9,7 @@ pipeline {
 
 	triggers {
 		pollSCM 'H/10 * * * *'
-		upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
+		upstream(upstreamProjects: "spring-data-commons/4.0.x", threshold: hudson.model.Result.SUCCESS)
 	}
 
 	options {
diff --git a/pom.xml b/pom.xml
index ded4d85d02..5f78acf4a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
 	<groupId>org.springframework.data</groupId>
 	<artifactId>spring-data-mongodb-parent</artifactId>
-	<version>4.5.0-SNAPSHOT</version>
+	<version>5.0.x-GH-4874-SNAPSHOT</version>
 	<packaging>pom</packaging>
 
 	<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
 	<parent>
 		<groupId>org.springframework.data.build</groupId>
 		<artifactId>spring-data-parent</artifactId>
-		<version>3.5.0-SNAPSHOT</version>
+		<version>4.0.0-SNAPSHOT</version>
 	</parent>
 
 	<modules>
@@ -26,7 +26,7 @@
 	<properties>
 		<project.type>multi</project.type>
 		<dist.id>spring-data-mongodb</dist.id>
-		<springdata.commons>3.5.0-SNAPSHOT</springdata.commons>
+		<springdata.commons>4.0.0-SNAPSHOT</springdata.commons>
 		<mongo>5.3.1</mongo>
 		<mongodb-crypt>${mongo}</mongodb-crypt>
 		<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 58c63dfc97..5d61bbb01c 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -15,7 +15,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>4.5.0-SNAPSHOT</version>
+		<version>5.0.x-GH-4874-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 37e68c6f78..597905ce54 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -13,7 +13,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>4.5.0-SNAPSHOT</version>
+		<version>5.0.x-GH-4874-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
@@ -43,12 +43,6 @@
 		<dependency>
 			<groupId>org.springframework</groupId>
 			<artifactId>spring-core</artifactId>
-			<exclusions>
-				<exclusion>
-					<groupId>commons-logging</groupId>
-					<artifactId>commons-logging</artifactId>
-				</exclusion>
-			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
@@ -360,8 +354,76 @@
 
 	</dependencies>
 
-	<build>
+	<profiles>
+		<profile>
+			<id>nullaway</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.apache.maven.plugins</groupId>
+						<artifactId>maven-compiler-plugin</artifactId>
+						<configuration>
+							<annotationProcessorPaths>
+								<path>
+									<groupId>com.querydsl</groupId>
+									<artifactId>querydsl-apt</artifactId>
+									<version>${querydsl}</version>
+								</path>
+								<path>
+									<groupId>org.openjdk.jmh</groupId>
+									<artifactId>jmh-generator-annprocess</artifactId>
+									<version>${jmh}</version>
+								</path>
+								<path>
+									<groupId>com.google.errorprone</groupId>
+									<artifactId>error_prone_core</artifactId>
+									<version>${errorprone}</version>
+								</path>
+								<path>
+									<groupId>com.uber.nullaway</groupId>
+									<artifactId>nullaway</artifactId>
+									<version>${nullaway}</version>
+								</path>
+							</annotationProcessorPaths>
+						</configuration>
+						<executions>
+							<execution>
+								<id>default-compile</id>
+								<phase>none</phase>
+							</execution>
+							<execution>
+								<id>default-testCompile</id>
+								<phase>none</phase>
+							</execution>
+							<execution>
+								<id>java-compile</id>
+								<phase>compile</phase>
+								<goals>
+									<goal>compile</goal>
+								</goals>
+								<configuration>
+									<compilerArgs>
+										<arg>-XDcompilePolicy=simple</arg>
+										<arg>--should-stop=ifError=FLOW</arg>
+										<arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:TreatGeneratedAsUnannotated=true -XepOpt:NullAway:CustomContractAnnotations=org.springframework.lang.Contract</arg>
+									</compilerArgs>
+								</configuration>
+							</execution>
+							<execution>
+								<id>java-test-compile</id>
+								<phase>test-compile</phase>
+								<goals>
+									<goal>testCompile</goal>
+								</goals>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
 
+	<build>
 		<plugins>
 
 			<plugin>
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java
index 1f6875c080..3ae41aad35 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java
@@ -20,9 +20,10 @@
 import org.bson.Document;
 import org.bson.codecs.DocumentCodec;
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -31,8 +32,7 @@
  * A {@link MongoExpression} using the {@link ParameterBindingDocumentCodec} for parsing a raw ({@literal json})
  * expression. The expression will be wrapped within <code>{ ... }</code> if necessary. The actual parsing and parameter
  * binding of placeholders like {@code ?0} is delayed upon first call on the target {@link Document} via
- * {@link #toDocument()}.
- * <br />
+ * {@link #toDocument()}. <br />
  *
  * <pre class="code">
  * $toUpper : $name                -> { '$toUpper' : '$name' }
@@ -55,7 +55,7 @@ public class BindableMongoExpression implements MongoExpression {
 
 	private final @Nullable CodecRegistryProvider codecRegistryProvider;
 
-	private final @Nullable Object[] args;
+	private final Object @Nullable [] args;
 
 	private final Lazy<Document> target;
 
@@ -63,9 +63,9 @@ public class BindableMongoExpression implements MongoExpression {
 	 * Create a new instance of {@link BindableMongoExpression}.
 	 *
 	 * @param expression must not be {@literal null}.
-	 * @param args can be {@literal null}.
+	 * @param args must not be {@literal null} but may contain {@literal null} elements.
 	 */
-	public BindableMongoExpression(String expression, @Nullable Object[] args) {
+	public BindableMongoExpression(String expression, Object @Nullable [] args) {
 		this(expression, null, args);
 	}
 
@@ -74,10 +74,10 @@ public BindableMongoExpression(String expression, @Nullable Object[] args) {
 	 *
 	 * @param expression must not be {@literal null}.
 	 * @param codecRegistryProvider can be {@literal null}.
-	 * @param args can be {@literal null}.
+	 * @param args must not be {@literal null} but may contain {@literal null} elements.
 	 */
 	public BindableMongoExpression(String expression, @Nullable CodecRegistryProvider codecRegistryProvider,
-			@Nullable Object[] args) {
+			Object @Nullable [] args) {
 
 		Assert.notNull(expression, "Expression must not be null");
 
@@ -93,6 +93,7 @@ public BindableMongoExpression(String expression, @Nullable CodecRegistryProvide
 	 * @param codecRegistry must not be {@literal null}.
 	 * @return new instance of {@link BindableMongoExpression}.
 	 */
+	@Contract("_ -> new")
 	public BindableMongoExpression withCodecRegistry(CodecRegistry codecRegistry) {
 		return new BindableMongoExpression(expressionString, () -> codecRegistry, args);
 	}
@@ -103,6 +104,7 @@ public BindableMongoExpression withCodecRegistry(CodecRegistry codecRegistry) {
 	 * @param args must not be {@literal null}.
 	 * @return new instance of {@link BindableMongoExpression}.
 	 */
+	@Contract("_ -> new")
 	public BindableMongoExpression bind(Object... args) {
 		return new BindableMongoExpression(expressionString, codecRegistryProvider, args);
 	}
@@ -139,7 +141,7 @@ private Document parse() {
 
 	private static String wrapJsonIfNecessary(String json) {
 
-		if(!StringUtils.hasText(json)) {
+		if (!StringUtils.hasText(json)) {
 			return json;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
index b36382a58e..12d8c966af 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
@@ -17,6 +17,7 @@
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
 
 import com.mongodb.MongoBulkWriteException;
@@ -40,10 +41,10 @@ public class BulkOperationException extends DataAccessException {
 	/**
 	 * Creates a new {@link BulkOperationException} with the given message and source {@link MongoBulkWriteException}.
 	 *
-	 * @param message must not be {@literal null}.
+	 * @param message can be {@literal null}.
 	 * @param source must not be {@literal null}.
 	 */
-	public BulkOperationException(String message, MongoBulkWriteException source) {
+	public BulkOperationException(@Nullable String message, MongoBulkWriteException source) {
 
 		super(message, source);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
index 53acf65470..c59eecb43a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.NonTransientDataAccessException;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link NonTransientDataAccessException} specific to MongoDB {@link com.mongodb.session.ClientSession} related data
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
index c07e2dbe4a..87201ef9ee 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
@@ -18,7 +18,7 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Default implementation of {@link MongoTransactionOptions} using {@literal mongo:} as {@link #getLabelPrefix() label
@@ -42,9 +42,8 @@ public MongoTransactionOptions convert(Map<String, String> options) {
 		return SimpleMongoTransactionOptions.of(options);
 	}
 
-	@Nullable
 	@Override
-	public String getLabelPrefix() {
+	public @Nullable String getLabelPrefix() {
 		return PREFIX;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
index f73f9fb7ed..042a5ba1d3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.support.ResourceHolderSynchronization;
 import org.springframework.transaction.support.TransactionSynchronization;
 import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -29,8 +29,7 @@
 /**
  * Helper class for managing a {@link MongoDatabase} instances via {@link MongoDatabaseFactory}. Used for obtaining
  * {@link ClientSession session bound} resources, such as {@link MongoDatabase} and
- * {@link com.mongodb.client.MongoCollection} suitable for transactional usage.
- * <br />
+ * {@link com.mongodb.client.MongoCollection} suitable for transactional usage. <br />
  * <strong>Note:</strong> Intended for internal usage only.
  *
  * @author Christoph Strobl
@@ -42,8 +41,7 @@ public class MongoDatabaseUtils {
 
 	/**
 	 * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory} using
-	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
-	 * <br />
+	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
 	 * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
@@ -55,8 +53,7 @@ public static MongoDatabase getDatabase(MongoDatabaseFactory factory) {
 	}
 
 	/**
-	 * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}.
-	 * <br />
+	 * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
 	 * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
@@ -70,8 +67,7 @@ public static MongoDatabase getDatabase(MongoDatabaseFactory factory, SessionSyn
 
 	/**
 	 * Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory} using
-	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
-	 * <br />
+	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current
 	 * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
@@ -139,8 +135,7 @@ public static boolean isTransactionActive(MongoDatabaseFactory dbFactory) {
 		return resourceHolder != null && resourceHolder.hasActiveTransaction();
 	}
 
-	@Nullable
-	private static ClientSession doGetSession(MongoDatabaseFactory dbFactory,
+	private static @Nullable ClientSession doGetSession(MongoDatabaseFactory dbFactory,
 			SessionSynchronization sessionSynchronization) {
 
 		MongoResourceHolder resourceHolder = (MongoResourceHolder) TransactionSynchronizationManager.getResource(dbFactory);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java
index a1e8344a9f..81c25d0998 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.support.ResourceHolderSupport;
 
@@ -23,8 +23,7 @@
 
 /**
  * MongoDB specific {@link ResourceHolderSupport resource holder}, wrapping a {@link ClientSession}.
- * {@link MongoTransactionManager} binds instances of this class to the thread.
- * <br />
+ * {@link MongoTransactionManager} binds instances of this class to the thread. <br />
  * <strong>Note:</strong> Intended for internal usage only.
  *
  * @author Christoph Strobl
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java
index 4215479f62..3d7bec6780 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * A specific {@link ClientSessionException} related to issues with a transaction such as aborted or non existing
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java
index eda657f5f1..1f97bb69e9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionException;
 import org.springframework.transaction.TransactionSystemException;
@@ -36,19 +36,15 @@
 
 /**
  * A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages
- * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
- * <br />
- * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
- * <br />
+ * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. <br />
+ * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. <br />
  * {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal
  * consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction()
- * commit} or {@link ClientSession#abortTransaction() abort} a transaction.
- * <br />
+ * commit} or {@link ClientSession#abortTransaction() abort} a transaction. <br />
  * Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via
  * {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard
  * {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as
- * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
- * <br />
+ * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. <br />
  * By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override
  * {@link #doCommit(MongoTransactionObject)} to implement the
  * <a href="https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation">Retry Commit Operation</a>
@@ -80,7 +76,9 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
 	 * @see #setTransactionSynchronization(int)
 	 */
 	public MongoTransactionManager() {
+
 		this.transactionOptionsResolver = MongoTransactionOptionsResolver.defaultResolver();
+		this.options = MongoTransactionOptions.NONE;
 	}
 
 	/**
@@ -151,7 +149,8 @@ protected void doBegin(Object transaction, TransactionDefinition definition) thr
 		}
 
 		try {
-			MongoTransactionOptions mongoTransactionOptions = transactionOptionsResolver.resolve(definition).mergeWith(options);
+			MongoTransactionOptions mongoTransactionOptions = transactionOptionsResolver.resolve(definition)
+					.mergeWith(options);
 			mongoTransactionObject.startTransaction(mongoTransactionOptions.toDriverOptions());
 		} catch (MongoException ex) {
 			throw new TransactionSystemException(String.format("Could not start Mongo transaction for session %s.",
@@ -206,6 +205,7 @@ protected final void doCommit(DefaultTransactionStatus status) throws Transactio
 	 * By default those labels are ignored, nevertheless one might check for
 	 * {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the
 	 * commit. <br />
+	 * 
 	 * <pre>
 	 * <code>
 	 * int retries = 3;
@@ -302,8 +302,7 @@ public void setOptions(@Nullable TransactionOptions options) {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public MongoDatabaseFactory getDatabaseFactory() {
+	public @Nullable MongoDatabaseFactory getDatabaseFactory() {
 		return databaseFactory;
 	}
 
@@ -461,8 +460,7 @@ void closeSession() {
 			}
 		}
 
-		@Nullable
-		public ClientSession getSession() {
+		public @Nullable ClientSession getSession() {
 			return resourceHolder != null ? resourceHolder.getSession() : null;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
index e411bd5d2d..04bcd36e35 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
@@ -19,15 +19,16 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ReadConcernAware;
 import org.springframework.data.mongodb.core.ReadPreferenceAware;
 import org.springframework.data.mongodb.core.WriteConcernAware;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.ReadConcern;
 import com.mongodb.ReadPreference;
 import com.mongodb.TransactionOptions;
 import com.mongodb.WriteConcern;
+import org.springframework.lang.Contract;
 
 /**
  * Options to be applied within a specific transaction scope.
@@ -43,27 +44,23 @@ public interface MongoTransactionOptions
 	 */
 	MongoTransactionOptions NONE = new MongoTransactionOptions() {
 
-		@Nullable
 		@Override
-		public Duration getMaxCommitTime() {
+		public @Nullable Duration getMaxCommitTime() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public ReadConcern getReadConcern() {
+		public @Nullable ReadConcern getReadConcern() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public ReadPreference getReadPreference() {
+		public @Nullable ReadPreference getReadPreference() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public WriteConcern getWriteConcern() {
+		public @Nullable WriteConcern getWriteConcern() {
 			return null;
 		}
 	};
@@ -76,6 +73,7 @@ public WriteConcern getWriteConcern() {
 	 * @return new instance of {@link MongoTransactionOptions} or this if {@literal fallbackOptions} is {@literal null} or
 	 *         {@link #NONE}.
 	 */
+	@Contract("null -> this")
 	default MongoTransactionOptions mergeWith(@Nullable MongoTransactionOptions fallbackOptions) {
 
 		if (fallbackOptions == null || MongoTransactionOptions.NONE.equals(fallbackOptions)) {
@@ -84,30 +82,26 @@ default MongoTransactionOptions mergeWith(@Nullable MongoTransactionOptions fall
 
 		return new MongoTransactionOptions() {
 
-			@Nullable
 			@Override
-			public Duration getMaxCommitTime() {
+			public @Nullable Duration getMaxCommitTime() {
 				return MongoTransactionOptions.this.hasMaxCommitTime() ? MongoTransactionOptions.this.getMaxCommitTime()
 						: fallbackOptions.getMaxCommitTime();
 			}
 
-			@Nullable
 			@Override
-			public ReadConcern getReadConcern() {
+			public @Nullable ReadConcern getReadConcern() {
 				return MongoTransactionOptions.this.hasReadConcern() ? MongoTransactionOptions.this.getReadConcern()
 						: fallbackOptions.getReadConcern();
 			}
 
-			@Nullable
 			@Override
-			public ReadPreference getReadPreference() {
+			public @Nullable ReadPreference getReadPreference() {
 				return MongoTransactionOptions.this.hasReadPreference() ? MongoTransactionOptions.this.getReadPreference()
 						: fallbackOptions.getReadPreference();
 			}
 
-			@Nullable
 			@Override
-			public WriteConcern getWriteConcern() {
+			public @Nullable WriteConcern getWriteConcern() {
 				return MongoTransactionOptions.this.hasWriteConcern() ? MongoTransactionOptions.this.getWriteConcern()
 						: fallbackOptions.getWriteConcern();
 			}
@@ -128,8 +122,8 @@ default <T> T map(Function<MongoTransactionOptions, T> mappingFunction) {
 	 * @return MongoDB driver native {@link TransactionOptions}.
 	 * @see MongoTransactionOptions#map(Function)
 	 */
-	@Nullable
-	default TransactionOptions toDriverOptions() {
+	@SuppressWarnings("NullAway")
+	default @Nullable TransactionOptions toDriverOptions() {
 
 		return map(it -> {
 
@@ -157,7 +151,7 @@ default TransactionOptions toDriverOptions() {
 	/**
 	 * Factory method to wrap given MongoDB driver native {@link TransactionOptions} into {@link MongoTransactionOptions}.
 	 *
-	 * @param options
+	 * @param options can be {@literal null}.
 	 * @return {@link MongoTransactionOptions#NONE} if given object is {@literal null}.
 	 */
 	static MongoTransactionOptions of(@Nullable TransactionOptions options) {
@@ -168,35 +162,30 @@ static MongoTransactionOptions of(@Nullable TransactionOptions options) {
 
 		return new MongoTransactionOptions() {
 
-			@Nullable
 			@Override
-			public Duration getMaxCommitTime() {
+			public @Nullable Duration getMaxCommitTime() {
 
 				Long millis = options.getMaxCommitTime(TimeUnit.MILLISECONDS);
 				return millis != null ? Duration.ofMillis(millis) : null;
 			}
 
-			@Nullable
 			@Override
-			public ReadConcern getReadConcern() {
+			public @Nullable ReadConcern getReadConcern() {
 				return options.getReadConcern();
 			}
 
-			@Nullable
 			@Override
-			public ReadPreference getReadPreference() {
+			public @Nullable ReadPreference getReadPreference() {
 				return options.getReadPreference();
 			}
 
-			@Nullable
 			@Override
-			public WriteConcern getWriteConcern() {
+			public @Nullable WriteConcern getWriteConcern() {
 				return options.getWriteConcern();
 			}
 
-			@Nullable
 			@Override
-			public TransactionOptions toDriverOptions() {
+			public @Nullable TransactionOptions toDriverOptions() {
 				return options;
 			}
 		};
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
index b73b079a99..c4bdbcca53 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
@@ -18,7 +18,7 @@
 import java.util.Map;
 import java.util.stream.Collectors;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.interceptor.TransactionAttribute;
 import org.springframework.util.Assert;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
index f397818a4c..3d1c2ee89c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
@@ -18,7 +18,7 @@
 import reactor.core.publisher.Mono;
 import reactor.util.context.Context;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.NoTransactionException;
 import org.springframework.transaction.reactive.ReactiveResourceSynchronization;
 import org.springframework.transaction.reactive.TransactionSynchronization;
@@ -35,8 +35,7 @@
 /**
  * Helper class for managing reactive {@link MongoDatabase} instances via {@link ReactiveMongoDatabaseFactory}. Used for
  * obtaining {@link ClientSession session bound} resources, such as {@link MongoDatabase} and {@link MongoCollection}
- * suitable for transactional usage.
- * <br />
+ * suitable for transactional usage. <br />
  * <strong>Note:</strong> Intended for internal usage only.
  *
  * @author Mark Paluch
@@ -74,8 +73,7 @@ public static Mono<Boolean> isTransactionActive(ReactiveMongoDatabaseFactory dat
 
 	/**
 	 * Obtain the default {@link MongoDatabase database} form the given {@link ReactiveMongoDatabaseFactory factory} using
-	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
-	 * <br />
+	 * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
 	 * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
@@ -103,32 +101,32 @@ public static Mono<MongoDatabase> getDatabase(ReactiveMongoDatabaseFactory facto
 
 	/**
 	 * Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory
-	 * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
-	 * <br />
+	 * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
 	 * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
-	 * @param dbName the name of the {@link MongoDatabase} to get.
+	 * @param dbName the name of the {@link MongoDatabase} to get. If {@literal null} the default database of the
+	 *          {@link ReactiveMongoDatabaseFactory}.
 	 * @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from.
 	 * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
 	 */
-	public static Mono<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory) {
+	public static Mono<MongoDatabase> getDatabase(@Nullable String dbName, ReactiveMongoDatabaseFactory factory) {
 		return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION);
 	}
 
 	/**
 	 * Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory
-	 * factory}.
-	 * <br />
+	 * factory}. <br />
 	 * Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber
 	 * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}.
 	 *
-	 * @param dbName the name of the {@link MongoDatabase} to get.
+	 * @param dbName the name of the {@link MongoDatabase} to get. If {@literal null} the default database of the *
+	 *          {@link ReactiveMongoDatabaseFactory}.
 	 * @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from.
 	 * @param sessionSynchronization the synchronization to use. Must not be {@literal null}.
 	 * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}.
 	 */
-	public static Mono<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory,
+	public static Mono<MongoDatabase> getDatabase(@Nullable String dbName, ReactiveMongoDatabaseFactory factory,
 			SessionSynchronization sessionSynchronization) {
 		return doGetMongoDatabase(dbName, factory, sessionSynchronization);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java
index 33caa5e7fe..d01364b202 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java
@@ -15,16 +15,15 @@
  */
 package org.springframework.data.mongodb;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
-import org.springframework.lang.Nullable;
 import org.springframework.transaction.support.ResourceHolderSupport;
 
 import com.mongodb.reactivestreams.client.ClientSession;
 
 /**
  * MongoDB specific resource holder, wrapping a {@link ClientSession}. {@link ReactiveMongoTransactionManager} binds
- * instances of this class to the subscriber context.
- * <br />
+ * instances of this class to the subscriber context. <br />
  * <strong>Note:</strong> Intended for internal usage only.
  *
  * @author Mark Paluch
@@ -103,8 +102,7 @@ boolean hasSession() {
 	 * @param session
 	 * @return
 	 */
-	@Nullable
-	public ClientSession setSessionIfAbsent(@Nullable ClientSession session) {
+	public @Nullable ClientSession setSessionIfAbsent(@Nullable ClientSession session) {
 
 		if (!hasSession()) {
 			setSession(session);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
index 2c65c26b79..4f293c8ed6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java
@@ -17,8 +17,8 @@
 
 import reactor.core.publisher.Mono;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.InitializingBean;
-import org.springframework.lang.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionException;
 import org.springframework.transaction.TransactionSystemException;
@@ -64,7 +64,7 @@
 public class ReactiveMongoTransactionManager extends AbstractReactiveTransactionManager implements InitializingBean {
 
 	private @Nullable ReactiveMongoDatabaseFactory databaseFactory;
-	private @Nullable MongoTransactionOptions options;
+	private MongoTransactionOptions options;
 	private final MongoTransactionOptionsResolver transactionOptionsResolver;
 
 	/**
@@ -79,7 +79,9 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction
 	 * @see #setDatabaseFactory(ReactiveMongoDatabaseFactory)
 	 */
 	public ReactiveMongoTransactionManager() {
+
 		this.transactionOptionsResolver = MongoTransactionOptionsResolver.defaultResolver();
+		this.options = MongoTransactionOptions.NONE;
 	}
 
 	/**
@@ -98,7 +100,7 @@ public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact
 	 * starting a new transaction.
 	 *
 	 * @param databaseFactory must not be {@literal null}.
-	 * @param options can be {@literal null}.
+	 * @param options can be {@literal null}. Will default {@link MongoTransactionOptions#NONE} if {@literal null}.
 	 */
 	public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory,
 			@Nullable TransactionOptions options) {
@@ -112,7 +114,8 @@ public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact
 	 *
 	 * @param databaseFactory must not be {@literal null}.
 	 * @param transactionOptionsResolver must not be {@literal null}.
-	 * @param defaultTransactionOptions can be {@literal null}.
+	 * @param defaultTransactionOptions can be {@literal null}. Will default {@link MongoTransactionOptions#NONE} if
+	 *          {@literal null}.
 	 * @since 4.3
 	 */
 	public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory,
@@ -124,7 +127,7 @@ public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact
 
 		this.databaseFactory = databaseFactory;
 		this.transactionOptionsResolver = transactionOptionsResolver;
-		this.options = defaultTransactionOptions;
+		this.options = defaultTransactionOptions != null ? defaultTransactionOptions : MongoTransactionOptions.NONE;
 	}
 
 	@Override
@@ -318,8 +321,7 @@ public void setOptions(@Nullable TransactionOptions options) {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public ReactiveMongoDatabaseFactory getDatabaseFactory() {
+	public @Nullable ReactiveMongoDatabaseFactory getDatabaseFactory() {
 		return databaseFactory;
 	}
 
@@ -470,8 +472,7 @@ void closeSession() {
 			}
 		}
 
-		@Nullable
-		public ClientSession getSession() {
+		public @Nullable ClientSession getSession() {
 			return resourceHolder != null ? resourceHolder.getSession() : null;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java
index 93dbf5db69..ec30478a54 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java
@@ -22,8 +22,8 @@
 
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.MethodClassKey;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ConcurrentReferenceHashMap;
@@ -34,8 +34,7 @@
 
 /**
  * {@link MethodInterceptor} implementation looking up and invoking an alternative target method having
- * {@link ClientSession} as its first argument. This allows seamless integration with the existing code base.
- * <br />
+ * {@link ClientSession} as its first argument. This allows seamless integration with the existing code base. <br />
  * The {@link MethodInterceptor} is aware of methods on {@code MongoCollection} that my return new instances of itself
  * like (eg. {@link com.mongodb.reactivestreams.client.MongoCollection#withWriteConcern(WriteConcern)} and decorate them
  * if not already proxied.
@@ -95,13 +94,13 @@ public <T> SessionAwareMethodInterceptor(ClientSession session, T target, Class<
 		this.sessionType = sessionType;
 	}
 
-	@Nullable
 	@Override
-	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+	public @Nullable Object invoke(MethodInvocation methodInvocation) throws Throwable {
 
 		if (requiresDecoration(methodInvocation.getMethod())) {
 
 			Object target = methodInvocation.proceed();
+			Assert.notNull(target, "invocation target was null");
 			if (target instanceof Proxy) {
 				return target;
 			}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java
index b52fc0bd71..5c50ba686a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java
@@ -21,7 +21,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.Function;
@@ -41,10 +41,10 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions {
 	static final Set<String> KNOWN_KEYS = Arrays.stream(OptionKey.values()).map(OptionKey::getKey)
 			.collect(Collectors.toSet());
 
-	private final Duration maxCommitTime;
-	private final ReadConcern readConcern;
-	private final ReadPreference readPreference;
-	private final WriteConcern writeConcern;
+	private final @Nullable Duration maxCommitTime;
+	private final @Nullable ReadConcern readConcern;
+	private final @Nullable ReadPreference readPreference;
+	private final @Nullable WriteConcern writeConcern;
 
 	static SimpleMongoTransactionOptions of(Map<String, String> options) {
 		return new SimpleMongoTransactionOptions(options);
@@ -58,27 +58,23 @@ private SimpleMongoTransactionOptions(Map<String, String> options) {
 		this.writeConcern = doGetWriteConcern(options);
 	}
 
-	@Nullable
 	@Override
-	public Duration getMaxCommitTime() {
+	public @Nullable Duration getMaxCommitTime() {
 		return maxCommitTime;
 	}
 
-	@Nullable
 	@Override
-	public ReadConcern getReadConcern() {
+	public @Nullable ReadConcern getReadConcern() {
 		return readConcern;
 	}
 
-	@Nullable
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 		return readPreference;
 	}
 
-	@Nullable
 	@Override
-	public WriteConcern getWriteConcern() {
+	public @Nullable WriteConcern getWriteConcern() {
 		return writeConcern;
 	}
 
@@ -89,8 +85,7 @@ public String toString() {
 				+ ", readPreference=" + readPreference + ", writeConcern=" + writeConcern + '}';
 	}
 
-	@Nullable
-	private static Duration doGetMaxCommitTime(Map<String, String> options) {
+	private static @Nullable Duration doGetMaxCommitTime(Map<String, String> options) {
 
 		return getValue(options, OptionKey.MAX_COMMIT_TIME, value -> {
 
@@ -100,18 +95,15 @@ private static Duration doGetMaxCommitTime(Map<String, String> options) {
 		});
 	}
 
-	@Nullable
-	private static ReadConcern doGetReadConcern(Map<String, String> options) {
+	private static @Nullable ReadConcern doGetReadConcern(Map<String, String> options) {
 		return getValue(options, OptionKey.READ_CONCERN, value -> new ReadConcern(ReadConcernLevel.fromString(value)));
 	}
 
-	@Nullable
-	private static ReadPreference doGetReadPreference(Map<String, String> options) {
+	private static @Nullable ReadPreference doGetReadPreference(Map<String, String> options) {
 		return getValue(options, OptionKey.READ_PREFERENCE, ReadPreference::valueOf);
 	}
 
-	@Nullable
-	private static WriteConcern doGetWriteConcern(Map<String, String> options) {
+	private static @Nullable WriteConcern doGetWriteConcern(Map<String, String> options) {
 
 		return getValue(options, OptionKey.WRITE_CONCERN, value -> {
 
@@ -123,8 +115,8 @@ private static WriteConcern doGetWriteConcern(Map<String, String> options) {
 		});
 	}
 
-	@Nullable
-	private static <T> T getValue(Map<String, String> options, OptionKey key, Function<String, T> convertFunction) {
+	private static <T> @Nullable T getValue(Map<String, String> options, OptionKey key,
+			Function<String, T> convertFunction) {
 
 		String value = options.get(key.getKey());
 		return value != null ? convertFunction.apply(value) : null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java
index cd5f58d5b1..57ecec0342 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java
@@ -17,7 +17,7 @@
 
 import java.time.Duration;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * MongoDB-specific transaction metadata.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java
index 37c7e3686b..e42c26d95a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java
index bec05d0d68..69ec086e5a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.UncategorizedDataAccessException;
-import org.springframework.lang.Nullable;
 
 public class UncategorizedMongoDbException extends UncategorizedDataAccessException {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java
index 2fe27a2c9e..86a70600a8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java
@@ -17,11 +17,11 @@
 
 import java.util.function.Predicate;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
 import org.springframework.data.util.ReactiveWrappers;
 import org.springframework.data.util.ReactiveWrappers.ReactiveLibrary;
 import org.springframework.data.util.TypeUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java
index a33f20ffb6..4b7aa10c3f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.mongodb.aot;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.aot.generate.GenerationContext;
 import org.springframework.core.ResolvableType;
 import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor;
 import org.springframework.data.mongodb.MongoManagedTypes;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java
index 538fe4e812..f2442960ed 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java
@@ -15,10 +15,11 @@
  */
 package org.springframework.data.mongodb.aot;
 
-import static org.springframework.data.mongodb.aot.MongoAotPredicates.*;
+import static org.springframework.data.mongodb.aot.MongoAotPredicates.isReactorPresent;
 
 import java.util.Arrays;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.aot.hint.MemberCategory;
 import org.springframework.aot.hint.RuntimeHints;
 import org.springframework.aot.hint.RuntimeHintsRegistrar;
@@ -31,7 +32,6 @@
 import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback;
 import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback;
 import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.MongoClientSettings;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java
index b070a0190f..0f6ba01704 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java
@@ -17,7 +17,7 @@
 
 import java.beans.PropertyEditorSupport;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.ConnectionString;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java
index 164b4defb6..f3a7dc0437 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java
@@ -21,6 +21,8 @@
 import java.util.List;
 import java.util.Set;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinitionHolder;
@@ -56,7 +58,6 @@
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -76,6 +77,7 @@
  * @author Zied Yaich
  * @author Tomasz Forys
  */
+@NullUnmarked
 public class MappingMongoConverterParser implements BeanDefinitionParser {
 
 	private static final String BASE_PACKAGE = "base-package";
@@ -157,8 +159,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
 		return null;
 	}
 
-	@Nullable
-	private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {
+	private @Nullable BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) {
 
 		String disableValidation = element.getAttribute("disable-validation");
 		boolean validationDisabled = StringUtils.hasText(disableValidation) && Boolean.parseBoolean(disableValidation);
@@ -291,8 +292,7 @@ private static void parseFieldNamingStrategy(Element element, ReaderContext cont
 		}
 	}
 
-	@Nullable
-	private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) {
+	private @Nullable BeanDefinition getCustomConversions(Element element, ParserContext parserContext) {
 
 		List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters");
 
@@ -354,8 +354,7 @@ private static Set<String> getInitialEntityClasses(Element element) {
 		return classes;
 	}
 
-	@Nullable
-	public BeanMetadataElement parseConverter(Element element, ParserContext parserContext) {
+	public @Nullable BeanMetadataElement parseConverter(Element element, ParserContext parserContext) {
 
 		String converterRef = element.getAttribute("ref");
 		if (StringUtils.hasText(converterRef)) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java
index 4e05fe6c39..a304199776 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java
@@ -18,6 +18,8 @@
 import static org.springframework.data.config.ParsingUtils.*;
 import static org.springframework.data.mongodb.config.BeanNames.*;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -29,7 +31,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback;
 import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
 
@@ -42,6 +43,7 @@
  * @author Oliver Gierke
  * @author Mark Paluch
  */
+@NullUnmarked
 public class MongoAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
 
 	private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono",
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java
index 0594f6176c..b01827d8c6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java
@@ -35,6 +35,7 @@
 import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
+import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
 
@@ -52,7 +53,7 @@ public abstract class MongoConfigurationSupport {
 	/**
 	 * Return the name of the database to connect to.
 	 *
-	 * @return must not be {@literal null}.
+	 * @return never {@literal null}.
 	 */
 	protected abstract String getDatabaseName();
 
@@ -76,7 +77,7 @@ protected Collection<String> getMappingBasePackages() {
 	 * Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package.
 	 *
 	 * @see #getMappingBasePackages()
-	 * @return
+	 * @return never {@literal null}.
 	 */
 	@Bean
 	public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions,
@@ -172,8 +173,10 @@ protected Set<Class<?>> scanForEntities(String basePackage) throws ClassNotFound
 
 			for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
 
-				initialEntitySet
-						.add(ClassUtils.forName(candidate.getBeanClassName(), MongoConfigurationSupport.class.getClassLoader()));
+				String beanClassName = candidate.getBeanClassName();
+				Assert.notNull(beanClassName, "BeanClassName cannot be null");
+
+				initialEntitySet.add(ClassUtils.forName(beanClassName, MongoConfigurationSupport.class.getClassLoader()));
 			}
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java
index b8f23a35af..93d778c861 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java
@@ -26,7 +26,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.ReflectionUtils;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java
index 2e733cc79f..2d3649c53a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java
@@ -20,6 +20,8 @@
 
 import java.util.Set;
 
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -31,7 +33,6 @@
 import org.springframework.data.config.BeanComponentDefinitionBuilder;
 import org.springframework.data.mongodb.core.MongoClientFactoryBean;
 import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
-import org.springframework.lang.Nullable;
 import org.springframework.util.StringUtils;
 import org.w3c.dom.Element;
 
@@ -47,6 +48,7 @@
  * @author Viktor Khoroshko
  * @author Mark Paluch
  */
+@NullUnmarked
 public class MongoDbFactoryParser extends AbstractBeanDefinitionParser {
 
 	private static final Set<String> MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES = Set.of("id", "write-concern");
@@ -125,8 +127,7 @@ private BeanDefinition registerMongoBeanDefinition(Element element, ParserContex
 	 * @param parserContext
 	 * @return {@literal null} in case no client-/uri defined.
 	 */
-	@Nullable
-	private BeanDefinition getConnectionString(Element element, ParserContext parserContext) {
+	private @Nullable BeanDefinition getConnectionString(Element element, ParserContext parserContext) {
 
 		String type = null;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoJmxParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoJmxParser.java
deleted file mode 100644
index 07e9aace0c..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoJmxParser.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2011-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.config;
-
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.parsing.BeanComponentDefinition;
-import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.xml.BeanDefinitionParser;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.data.mongodb.core.MongoAdmin;
-import org.springframework.data.mongodb.monitor.*;
-import org.springframework.util.StringUtils;
-import org.w3c.dom.Element;
-
-/**
- * @author Mark Pollack
- * @author Thomas Risberg
- * @author John Brisbin
- * @author Oliver Gierke
- * @author Christoph Strobl
- */
-public class MongoJmxParser implements BeanDefinitionParser {
-
-	public BeanDefinition parse(Element element, ParserContext parserContext) {
-		String name = element.getAttribute("mongo-ref");
-		if (!StringUtils.hasText(name)) {
-			name = BeanNames.MONGO_BEAN_NAME;
-		}
-		registerJmxComponents(name, element, parserContext);
-		return null;
-	}
-
-	protected void registerJmxComponents(String mongoRefName, Element element, ParserContext parserContext) {
-		Object eleSource = parserContext.extractSource(element);
-
-		CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
-
-		createBeanDefEntry(AssertMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(BackgroundFlushingMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(BtreeIndexCounters.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(ConnectionMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(GlobalLockMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(MemoryMetrics.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(OperationCounters.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(ServerInfo.class, compositeDef, mongoRefName, eleSource, parserContext);
-		createBeanDefEntry(MongoAdmin.class, compositeDef, mongoRefName, eleSource, parserContext);
-
-		parserContext.registerComponent(compositeDef);
-
-	}
-
-	protected void createBeanDefEntry(Class<?> clazz, CompositeComponentDefinition compositeDef, String mongoRefName,
-			Object eleSource, ParserContext parserContext) {
-		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
-		builder.getRawBeanDefinition().setSource(eleSource);
-		builder.addConstructorArgReference(mongoRefName);
-		BeanDefinition assertDef = builder.getBeanDefinition();
-		String assertName = parserContext.getReaderContext().registerWithGeneratedName(assertDef);
-		compositeDef.addNestedComponent(new BeanComponentDefinition(assertDef, assertName));
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java
index 47519ca615..62a4a1082d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java
@@ -31,7 +31,6 @@ public void init() {
 		registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser());
 		registerBeanDefinitionParser("mongo-client", new MongoClientParser());
 		registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser());
-		registerBeanDefinitionParser("jmx", new MongoJmxParser());
 		registerBeanDefinitionParser("auditing", new MongoAuditingBeanDefinitionParser());
 		registerBeanDefinitionParser("template", new MongoTemplateParser());
 		registerBeanDefinitionParser("gridFsTemplate", new GridFsTemplateParser());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java
index 95b56b58f3..00e993fdc8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java
@@ -19,6 +19,7 @@
 
 import java.util.Map;
 
+import org.jspecify.annotations.NullUnmarked;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.CustomEditorConfigurer;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -40,6 +41,7 @@
  * @author Christoph Strobl
  * @author Mark Paluch
  */
+@NullUnmarked
 abstract class MongoParsingUtils {
 
 	private MongoParsingUtils() {}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java
index 1e1b11356f..5053e540fe 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java
@@ -18,6 +18,7 @@
 import static org.springframework.data.config.ParsingUtils.*;
 import static org.springframework.data.mongodb.config.MongoParsingUtils.*;
 
+import org.jspecify.annotations.NullUnmarked;
 import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -37,6 +38,7 @@
  * @author Martin Baumgartner
  * @author Oliver Gierke
  */
+@NullUnmarked
 class MongoTemplateParser extends AbstractBeanDefinitionParser {
 
 	@Override
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java
index 60bf126ae7..3f5cb0ca62 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java
@@ -17,7 +17,7 @@
 
 import java.beans.PropertyEditorSupport;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.ReadConcern;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java
index 5ed9b66619..f24c435348 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java
@@ -17,10 +17,10 @@
 
 import java.beans.PropertyEditorSupport;
 
-import org.springframework.lang.Nullable;
-
 import com.mongodb.ReadPreference;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * Parse a {@link String} to a {@link ReadPreference}.
  *
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
index 9c51900902..9ff59e5b22 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java
@@ -23,7 +23,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -80,11 +80,10 @@ public void setAsText(@Nullable String replicaSetString) {
 	 * @param source
 	 * @return the
 	 */
-	@Nullable
-	private ServerAddress parseServerAddress(String source) {
+	private @Nullable ServerAddress parseServerAddress(String source) {
 
 		if (!StringUtils.hasText(source)) {
-			if(LOG.isWarnEnabled()) {
+			if (LOG.isWarnEnabled()) {
 				LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source));
 			}
 			return null;
@@ -93,7 +92,7 @@ private ServerAddress parseServerAddress(String source) {
 		String[] hostAndPort = extractHostAddressAndPort(source.trim());
 
 		if (hostAndPort.length > 2) {
-			if(LOG.isWarnEnabled()) {
+			if (LOG.isWarnEnabled()) {
 				LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source));
 			}
 			return null;
@@ -105,11 +104,11 @@ private ServerAddress parseServerAddress(String source) {
 
 			return port == null ? new ServerAddress(hostAddress) : new ServerAddress(hostAddress, port);
 		} catch (UnknownHostException e) {
-			if(LOG.isWarnEnabled()) {
+			if (LOG.isWarnEnabled()) {
 				LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "host", hostAndPort[0]));
 			}
 		} catch (NumberFormatException e) {
-			if(LOG.isWarnEnabled()) {
+			if (LOG.isWarnEnabled()) {
 				LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "port", hostAndPort[1]));
 			}
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java
index b777969967..23c15102ac 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java
@@ -18,7 +18,7 @@
 import java.beans.PropertyEditorSupport;
 
 import org.bson.UuidRepresentation;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.StringUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java
index ee0d09e555..32c19e24c3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java
@@ -17,7 +17,7 @@
 
 import java.beans.PropertyEditorSupport;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.WriteConcern;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java
index 5a1e5b725e..555cc9f66e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Spring XML namespace configuration for MongoDB specific repositories.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.config;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java
index a00d95a9ad..ec7c368eaf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java
@@ -18,7 +18,7 @@
 import java.util.List;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
@@ -30,7 +30,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 
 /**
  * Utility methods to map {@link org.springframework.data.mongodb.core.aggregation.Aggregation} pipeline definitions and
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java
index 17b8835b7e..8a74ace28b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java
@@ -21,9 +21,9 @@
 import org.bson.BsonTimestamp;
 import org.bson.BsonValue;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.messaging.Message;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
 
@@ -78,8 +78,7 @@ public ChangeStreamEvent(@Nullable ChangeStreamDocument<Document> raw, Class<T>
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public ChangeStreamDocument<Document> getRaw() {
+	public @Nullable ChangeStreamDocument<Document> getRaw() {
 		return raw;
 	}
 
@@ -88,10 +87,10 @@ public ChangeStreamDocument<Document> getRaw() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public Instant getTimestamp() {
+	public @Nullable Instant getTimestamp() {
 
-		return getBsonTimestamp() != null ? converter.getConversionService().convert(raw.getClusterTime(), Instant.class)
+		return getBsonTimestamp() != null && raw != null
+				? converter.getConversionService().convert(raw.getClusterTime(), Instant.class)
 				: null;
 	}
 
@@ -111,8 +110,7 @@ public BsonTimestamp getBsonTimestamp() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public BsonValue getResumeToken() {
+	public @Nullable BsonValue getResumeToken() {
 		return raw != null ? raw.getResumeToken() : null;
 	}
 
@@ -121,8 +119,7 @@ public BsonValue getResumeToken() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public OperationType getOperationType() {
+	public @Nullable OperationType getOperationType() {
 		return raw != null ? raw.getOperationType() : null;
 	}
 
@@ -131,8 +128,7 @@ public OperationType getOperationType() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public String getDatabaseName() {
+	public @Nullable String getDatabaseName() {
 		return raw != null ? raw.getNamespace().getDatabaseName() : null;
 	}
 
@@ -141,8 +137,7 @@ public String getDatabaseName() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public String getCollectionName() {
+	public @Nullable String getCollectionName() {
 		return raw != null ? raw.getNamespace().getCollectionName() : null;
 	}
 
@@ -152,8 +147,7 @@ public String getCollectionName() {
 	 * @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocument()} is
 	 *         {@literal null}.
 	 */
-	@Nullable
-	public T getBody() {
+	public @Nullable T getBody() {
 
 		if (raw == null || raw.getFullDocument() == null) {
 			return null;
@@ -163,14 +157,14 @@ public T getBody() {
 	}
 
 	/**
-	 * Get the potentially converted {@link ChangeStreamDocument#getFullDocumentBeforeChange() document} before being changed.
+	 * Get the potentially converted {@link ChangeStreamDocument#getFullDocumentBeforeChange() document} before being
+	 * changed.
 	 *
 	 * @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocumentBeforeChange()} is
 	 *         {@literal null}.
 	 * @since 4.0
 	 */
-	@Nullable
-	public T getBodyBeforeChange() {
+	public @Nullable T getBodyBeforeChange() {
 
 		if (raw == null || raw.getFullDocumentBeforeChange() == null) {
 			return null;
@@ -189,6 +183,7 @@ private T getConvertedFullDocument(Document fullDocument) {
 		return (T) doGetConverted(fullDocument, CONVERTED_FULL_DOCUMENT_UPDATER);
 	}
 
+	@SuppressWarnings("NullAway")
 	private Object doGetConverted(Document fullDocument, AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> updater) {
 
 		Object result = updater.get(this);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java
index aaee3b76af..9c99b0e01f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java
@@ -23,9 +23,10 @@
 import org.bson.BsonTimestamp;
 import org.bson.BsonValue;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -248,6 +249,7 @@ private ChangeStreamOptionsBuilder() {}
 		 * @param collation must not be {@literal null} nor {@literal empty}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder collation(Collation collation) {
 
 			Assert.notNull(collation, "Collation must not be null nor empty");
@@ -257,14 +259,12 @@ public ChangeStreamOptionsBuilder collation(Collation collation) {
 		}
 
 		/**
-		 * Set the filter to apply.
-		 * <br />
+		 * Set the filter to apply. <br />
 		 * Fields on aggregation expression root level are prefixed to map to fields contained in
 		 * {@link ChangeStreamDocument#getFullDocument() fullDocument}. However {@literal operationType}, {@literal ns},
 		 * {@literal documentKey} and {@literal fullDocument} are reserved words that will be omitted, and therefore taken
 		 * as given, during the mapping procedure. You may want to have a look at the
-		 * <a href="https://docs.mongodb.com/manual/reference/change-events/">structure of Change Events</a>.
-		 * <br />
+		 * <a href="https://docs.mongodb.com/manual/reference/change-events/">structure of Change Events</a>. <br />
 		 * Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to ensure filter expressions are
 		 * mapped to domain type fields.
 		 *
@@ -272,6 +272,7 @@ public ChangeStreamOptionsBuilder collation(Collation collation) {
 		 *          {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder filter(Aggregation filter) {
 
 			Assert.notNull(filter, "Filter must not be null");
@@ -286,6 +287,7 @@ public ChangeStreamOptionsBuilder filter(Aggregation filter) {
 		 * @param filter must not be {@literal null} nor contain {@literal null} values.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder filter(Document... filter) {
 
 			Assert.noNullElements(filter, "Filter must not contain null values");
@@ -301,6 +303,7 @@ public ChangeStreamOptionsBuilder filter(Document... filter) {
 		 * @param resumeToken must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder resumeToken(BsonValue resumeToken) {
 
 			Assert.notNull(resumeToken, "ResumeToken must not be null");
@@ -330,6 +333,7 @@ public ChangeStreamOptionsBuilder returnFullDocumentOnUpdate() {
 		 * @param lookup must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder fullDocumentLookup(FullDocument lookup) {
 
 			Assert.notNull(lookup, "Lookup must not be null");
@@ -345,6 +349,7 @@ public ChangeStreamOptionsBuilder fullDocumentLookup(FullDocument lookup) {
 		 * @return this.
 		 * @since 4.0
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) {
 
 			Assert.notNull(lookup, "Lookup must not be null");
@@ -358,7 +363,7 @@ public ChangeStreamOptionsBuilder fullDocumentBeforeChangeLookup(FullDocumentBef
 		 *
 		 * @return this.
 		 * @since 4.0
-		 * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) 
+		 * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange)
 		 */
 		public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() {
 			return fullDocumentBeforeChangeLookup(FullDocumentBeforeChange.WHEN_AVAILABLE);
@@ -370,6 +375,7 @@ public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() {
 		 * @param resumeTimestamp must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder resumeAt(Instant resumeTimestamp) {
 
 			Assert.notNull(resumeTimestamp, "ResumeTimestamp must not be null");
@@ -385,6 +391,7 @@ public ChangeStreamOptionsBuilder resumeAt(Instant resumeTimestamp) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder resumeAt(BsonTimestamp resumeTimestamp) {
 
 			Assert.notNull(resumeTimestamp, "ResumeTimestamp must not be null");
@@ -400,6 +407,7 @@ public ChangeStreamOptionsBuilder resumeAt(BsonTimestamp resumeTimestamp) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) {
 
 			resumeToken(resumeToken);
@@ -415,6 +423,7 @@ public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) {
 
 			resumeToken(resumeToken);
@@ -426,6 +435,7 @@ public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) {
 		/**
 		 * @return the built {@link ChangeStreamOptions}
 		 */
+		@Contract("-> new")
 		public ChangeStreamOptions build() {
 
 			ChangeStreamOptions options = new ChangeStreamOptions();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java
index c142aca173..bf8be5ba69 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java
@@ -16,8 +16,8 @@
 package org.springframework.data.mongodb.core;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.MongoException;
 import com.mongodb.client.MongoCollection;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
index d627ba2468..ff2c708aa9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
@@ -19,6 +19,7 @@
 import java.util.Optional;
 import java.util.function.Function;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
@@ -26,7 +27,7 @@
 import org.springframework.data.mongodb.core.timeseries.GranularityDefinition;
 import org.springframework.data.mongodb.core.validation.Validator;
 import org.springframework.data.util.Optionals;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -184,7 +185,7 @@ public CollectionOptions collation(@Nullable Collation collation) {
 	 * @since 2.1
 	 */
 	public CollectionOptions schema(@Nullable MongoJsonSchema schema) {
-		return validator(Validator.schema(schema));
+		return validator(schema != null ? Validator.schema(schema) : null);
 	}
 
 	/**
@@ -461,7 +462,8 @@ public static class ValidationOptions {
 		private final @Nullable ValidationLevel validationLevel;
 		private final @Nullable ValidationAction validationAction;
 
-		public ValidationOptions(Validator validator, ValidationLevel validationLevel, ValidationAction validationAction) {
+		public ValidationOptions(@Nullable Validator validator, @Nullable ValidationLevel validationLevel,
+				@Nullable ValidationAction validationAction) {
 
 			this.validator = validator;
 			this.validationLevel = validationLevel;
@@ -483,6 +485,7 @@ public static ValidationOptions none() {
 		 * @param validator can be {@literal null}.
 		 * @return new instance of {@link ValidationOptions}.
 		 */
+		@Contract("_ -> new")
 		public ValidationOptions validator(@Nullable Validator validator) {
 			return new ValidationOptions(validator, validationLevel, validationAction);
 		}
@@ -493,6 +496,7 @@ public ValidationOptions validator(@Nullable Validator validator) {
 		 * @param validationLevel can be {@literal null}.
 		 * @return new instance of {@link ValidationOptions}.
 		 */
+		@Contract("_ -> new")
 		public ValidationOptions validationLevel(ValidationLevel validationLevel) {
 			return new ValidationOptions(validator, validationLevel, validationAction);
 		}
@@ -503,6 +507,7 @@ public ValidationOptions validationLevel(ValidationLevel validationLevel) {
 		 * @param validationAction can be {@literal null}.
 		 * @return new instance of {@link ValidationOptions}.
 		 */
+		@Contract("_ -> new")
 		public ValidationOptions validationAction(ValidationAction validationAction) {
 			return new ValidationOptions(validator, validationLevel, validationAction);
 		}
@@ -677,6 +682,7 @@ public static TimeSeriesOptions timeSeries(String timeField) {
 		 * @param metaField must not be {@literal null}.
 		 * @return new instance of {@link TimeSeriesOptions}.
 		 */
+		@Contract("_ -> new")
 		public TimeSeriesOptions metaField(String metaField) {
 			return new TimeSeriesOptions(timeField, metaField, granularity, expireAfter);
 		}
@@ -688,6 +694,7 @@ public TimeSeriesOptions metaField(String metaField) {
 		 * @return new instance of {@link TimeSeriesOptions}.
 		 * @see Granularity
 		 */
+		@Contract("_ -> new")
 		public TimeSeriesOptions granularity(GranularityDefinition granularity) {
 			return new TimeSeriesOptions(timeField, metaField, granularity, expireAfter);
 		}
@@ -700,6 +707,7 @@ public TimeSeriesOptions granularity(GranularityDefinition granularity) {
 		 * @see com.mongodb.client.model.CreateCollectionOptions#expireAfter(long, java.util.concurrent.TimeUnit)
 		 * @since 4.4
 		 */
+		@Contract("_ -> new")
 		public TimeSeriesOptions expireAfter(Duration ttl) {
 			return new TimeSeriesOptions(timeField, metaField, granularity, ttl);
 		}
@@ -715,8 +723,7 @@ public String getTimeField() {
 		 * @return can be {@literal null}. Might be an {@literal empty} {@link String} as well, so maybe check via
 		 *         {@link org.springframework.util.StringUtils#hasText(String)}.
 		 */
-		@Nullable
-		public String getMetaField() {
+		public @Nullable String getMetaField() {
 			return metaField;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java
index 644a3a54d1..bdf0b90ee3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java
@@ -21,6 +21,7 @@
 import java.util.function.Function;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.ReadConcern;
 import com.mongodb.ReadPreference;
@@ -84,7 +85,7 @@ public boolean hasReadConcern() {
 	}
 
 	@Override
-	public ReadConcern getReadConcern() {
+	public @Nullable ReadConcern getReadConcern() {
 
 		for (Object aware : sources) {
 			if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
@@ -108,7 +109,7 @@ public boolean hasReadPreference() {
 	}
 
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 
 		for (Object aware : sources) {
 			if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java
index 4fa6b3e97d..11d9f09afd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java
@@ -23,9 +23,9 @@
 import java.util.Map;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
 import org.springframework.data.mongodb.core.query.MetricConversion;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -154,7 +154,7 @@ private Collection<Object> rewriteCollection(Collection<?> source) {
 	 * @param $and potentially existing {@code $and} condition.
 	 * @return the rewritten query {@link Document}.
 	 */
-	@SuppressWarnings("unchecked")
+	@SuppressWarnings({ "unchecked", "NullAway" })
 	private static Document createGeoWithin(String key, Document source, @Nullable Object $and) {
 
 		boolean spheric = source.containsKey("$nearSphere");
@@ -233,6 +233,7 @@ private static boolean containsNearWithMinDistance(Document source) {
 		return source.containsKey("$minDistance");
 	}
 
+	@SuppressWarnings("NullAway")
 	private static Object toCenterCoordinates(Object value) {
 
 		if (ObjectUtils.isArray(value)) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java
index 9b7408b0cf..3b53cef8d0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java
@@ -18,7 +18,7 @@
 import java.util.function.Function;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadPreference;
@@ -76,8 +76,7 @@ default FindIterable<Document> initiateFind(MongoCollection<Document> collection
 	 * @since 2.2
 	 */
 	@Override
-	@Nullable
-	default ReadPreference getReadPreference() {
+	default @Nullable ReadPreference getReadPreference() {
 		return null;
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java
index 9d588ad16d..f450bddb30 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.MongoException;
 import com.mongodb.client.MongoDatabase;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java
index 52343522a7..8bc5349e61 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java
@@ -21,6 +21,7 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.ApplicationEvent;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.dao.DataIntegrityViolationException;
@@ -40,7 +41,7 @@
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.util.Pair;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.MongoBulkWriteException;
@@ -115,6 +116,7 @@ void setDefaultWriteConcern(@Nullable WriteConcern defaultWriteConcern) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations insert(Object document) {
 
 		Assert.notNull(document, "Document must not be null");
@@ -127,6 +129,7 @@ public BulkOperations insert(Object document) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations insert(List<? extends Object> documents) {
 
 		Assert.notNull(documents, "Documents must not be null");
@@ -137,6 +140,7 @@ public BulkOperations insert(List<? extends Object> documents) {
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public BulkOperations updateOne(Query query, UpdateDefinition update) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -146,6 +150,7 @@ public BulkOperations updateOne(Query query, UpdateDefinition update) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) {
 
 		Assert.notNull(updates, "Updates must not be null");
@@ -158,6 +163,7 @@ public BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) {
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public BulkOperations updateMulti(Query query, UpdateDefinition update) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -169,6 +175,7 @@ public BulkOperations updateMulti(Query query, UpdateDefinition update) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) {
 
 		Assert.notNull(updates, "Updates must not be null");
@@ -181,11 +188,13 @@ public BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) {
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public BulkOperations upsert(Query query, UpdateDefinition update) {
 		return update(query, update, true, true);
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations upsert(List<Pair<Query, Update>> updates) {
 
 		for (Pair<Query, Update> update : updates) {
@@ -196,6 +205,7 @@ public BulkOperations upsert(List<Pair<Query, Update>> updates) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations remove(Query query) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -209,6 +219,7 @@ public BulkOperations remove(Query query) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public BulkOperations remove(List<Query> removes) {
 
 		Assert.notNull(removes, "Removals must not be null");
@@ -221,6 +232,7 @@ public BulkOperations remove(List<Query> removes) {
 	}
 
 	@Override
+	@Contract("_, _, _ -> this")
 	public BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -412,7 +424,7 @@ public boolean skipEventPublishing() {
 			return eventPublisher == null;
 		}
 
-		@SuppressWarnings("rawtypes")
+		@SuppressWarnings({ "rawtypes", "NullAway" })
 		public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) {
 
 			if (skipEntityCallbacks()) {
@@ -422,7 +434,7 @@ public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, St
 			return entityCallbacks.callback(callbackType, entity, collectionName);
 		}
 
-		@SuppressWarnings("rawtypes")
+		@SuppressWarnings({ "rawtypes", "NullAway" })
 		public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Document document,
 				String collectionName) {
 
@@ -433,6 +445,7 @@ public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Do
 			return entityCallbacks.callback(callbackType, entity, document, collectionName);
 		}
 
+		@SuppressWarnings("NullAway")
 		public void publishEvent(ApplicationEvent event) {
 
 			if (skipEventPublishing()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java
index 2057e2f046..24d22bd80a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java
@@ -20,6 +20,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.UncategorizedMongoDbException;
@@ -28,7 +29,6 @@
 import org.springframework.data.mongodb.core.index.IndexInfo;
 import org.springframework.data.mongodb.core.index.IndexOperations;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 
@@ -115,6 +115,7 @@ public DefaultIndexOperations(MongoOperations mongoOperations, String collection
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public String ensureIndex(IndexDefinition indexDefinition) {
 
 		return execute(collection -> {
@@ -131,8 +132,7 @@ public String ensureIndex(IndexDefinition indexDefinition) {
 		});
 	}
 
-	@Nullable
-	private MongoPersistentEntity<?> lookupPersistentEntity(@Nullable Class<?> entityType, String collection) {
+	private @Nullable MongoPersistentEntity<?> lookupPersistentEntity(@Nullable Class<?> entityType, String collection) {
 
 		if (entityType != null) {
 			return mapper.getMappingContext().getRequiredPersistentEntity(entityType);
@@ -160,6 +160,7 @@ public void dropIndex(String name) {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public void alterIndex(String name, org.springframework.data.mongodb.core.index.IndexOptions options) {
 
 		Document indexOptions = new Document("name", name);
@@ -180,6 +181,7 @@ public void dropAllIndexes() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public List<IndexInfo> getIndexInfo() {
 
 		return execute(new CollectionCallback<List<IndexInfo>>() {
@@ -208,8 +210,7 @@ private List<IndexInfo> getIndexData(MongoCursor<Document> cursor) {
 		});
 	}
 
-	@Nullable
-	public <T> T execute(CollectionCallback<T> callback) {
+	public <T> @Nullable T execute(CollectionCallback<T> callback) {
 
 		Assert.notNull(callback, "CollectionCallback must not be null");
 
@@ -228,6 +229,7 @@ private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document source
 				mapper.getMappedSort((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity));
 	}
 
+	@SuppressWarnings("NullAway")
 	private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops,
 			@Nullable MongoPersistentEntity<?> entity) {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java
index e2471dbb14..a34c1fb945 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.index.IndexOperations;
@@ -43,7 +44,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider {
 	}
 
 	@Override
-	public IndexOperations indexOps(String collectionName, Class<?> type) {
+	public IndexOperations indexOps(String collectionName, @Nullable Class<?> type) {
 		return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper, type);
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java
index 59b7ccd63e..92c6a957dc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.springframework.lang.Contract;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -24,6 +25,7 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.ApplicationEvent;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.data.mapping.callback.EntityCallback;
@@ -40,7 +42,6 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.WriteConcern;
@@ -107,6 +108,7 @@ void setDefaultWriteConcern(@Nullable WriteConcern defaultWriteConcern) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public ReactiveBulkOperations insert(Object document) {
 
 		Assert.notNull(document, "Document must not be null");
@@ -120,6 +122,7 @@ public ReactiveBulkOperations insert(Object document) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public ReactiveBulkOperations insert(List<? extends Object> documents) {
 
 		Assert.notNull(documents, "Documents must not be null");
@@ -130,6 +133,7 @@ public ReactiveBulkOperations insert(List<? extends Object> documents) {
 	}
 
 	@Override
+	@Contract("_, _, _ -> this")
 	public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -140,6 +144,7 @@ public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) {
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -150,11 +155,13 @@ public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update)
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public ReactiveBulkOperations upsert(Query query, UpdateDefinition update) {
 		return update(query, update, true, true);
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public ReactiveBulkOperations remove(Query query) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -169,6 +176,7 @@ public ReactiveBulkOperations remove(Query query) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public ReactiveBulkOperations remove(List<Query> removes) {
 
 		Assert.notNull(removes, "Removals must not be null");
@@ -181,6 +189,7 @@ public ReactiveBulkOperations remove(List<Query> removes) {
 	}
 
 	@Override
+	@Contract("_, _, _ -> this")
 	public ReactiveBulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -359,7 +368,7 @@ public boolean skipEventPublishing() {
 			return eventPublisher == null;
 		}
 
-		@SuppressWarnings("rawtypes")
+		@SuppressWarnings({ "rawtypes", "NullAway" })
 		public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) {
 
 			if (skipEntityCallbacks()) {
@@ -369,7 +378,7 @@ public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T enti
 			return entityCallbacks.callback(callbackType, entity, collectionName);
 		}
 
-		@SuppressWarnings("rawtypes")
+		@SuppressWarnings({ "rawtypes", "NullAway" })
 		public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, Document document,
 				String collectionName) {
 
@@ -380,6 +389,7 @@ public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T enti
 			return entityCallbacks.callback(callbackType, entity, document, collectionName);
 		}
 
+		@SuppressWarnings("NullAway")
 		public void publishEvent(ApplicationEvent event) {
 
 			if (skipEventPublishing()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java
index 8e78f421f4..69ade2e163 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java
@@ -19,16 +19,15 @@
 import reactor.core.publisher.Mono;
 
 import java.util.Collection;
-import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.UncategorizedMongoDbException;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.index.IndexDefinition;
 import org.springframework.data.mongodb.core.index.IndexInfo;
 import org.springframework.data.mongodb.core.index.ReactiveIndexOperations;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 
@@ -48,7 +47,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
 	private final ReactiveMongoOperations mongoOperations;
 	private final String collectionName;
 	private final QueryMapper queryMapper;
-	private final Optional<Class<?>> type;
+	private final @Nullable Class<?> type;
 
 	/**
 	 * Creates a new {@link DefaultReactiveIndexOperations}.
@@ -59,7 +58,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
 	 */
 	public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
 			QueryMapper queryMapper) {
-		this(mongoOperations, collectionName, queryMapper, Optional.empty());
+		this(mongoOperations, collectionName, queryMapper, null);
 	}
 
 	/**
@@ -71,12 +70,7 @@ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, S
 	 * @param type used for mapping potential partial index filter expression, must not be {@literal null}.
 	 */
 	public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
-			QueryMapper queryMapper, Class<?> type) {
-		this(mongoOperations, collectionName, queryMapper, Optional.of(type));
-	}
-
-	private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName,
-			QueryMapper queryMapper, Optional<Class<?>> type) {
+			QueryMapper queryMapper, @Nullable Class<?> type) {
 
 		Assert.notNull(mongoOperations, "ReactiveMongoOperations must not be null");
 		Assert.notNull(collectionName, "Collection must not be null");
@@ -89,13 +83,12 @@ private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations,
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public Mono<String> ensureIndex(IndexDefinition indexDefinition) {
 
 		return mongoOperations.execute(collectionName, collection -> {
 
-			MongoPersistentEntity<?> entity = type
-					.map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val))
-					.orElseGet(() -> lookupPersistentEntity(collectionName));
+			MongoPersistentEntity<?> entity = getConfiguredEntity();
 
 			IndexOptions indexOptions = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition);
 
@@ -124,8 +117,7 @@ public Mono<Void> alterIndex(String name, org.springframework.data.mongodb.core.
 		}).then();
 	}
 
-	@Nullable
-	private MongoPersistentEntity<?> lookupPersistentEntity(String collection) {
+	private @Nullable MongoPersistentEntity<?> lookupPersistentEntity(String collection) {
 
 		Collection<? extends MongoPersistentEntity<?>> entities = queryMapper.getMappingContext().getPersistentEntities();
 
@@ -152,6 +144,14 @@ public Flux<IndexInfo> getIndexInfo() {
 				.map(IndexConverters.documentToIndexInfoConverter()::convert);
 	}
 
+	private @Nullable MongoPersistentEntity<?> getConfiguredEntity() {
+
+		if (type != null) {
+			return queryMapper.getMappingContext().getRequiredPersistentEntity(type);
+		}
+		return lookupPersistentEntity(collectionName);
+	}
+
 	private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document sourceOptions,
 			@Nullable MongoPersistentEntity<?> entity) {
 
@@ -164,6 +164,7 @@ private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document source
 				queryMapper.getMappedObject((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity));
 	}
 
+	@SuppressWarnings("NullAway")
 	private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops,
 			@Nullable MongoPersistentEntity<?> entity) {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java
index b236b4df28..6dde79e0e8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java
@@ -15,9 +15,9 @@
  */
 package org.springframework.data.mongodb.core;
 
-import static java.util.UUID.*;
-import static org.springframework.data.mongodb.core.query.Criteria.*;
-import static org.springframework.data.mongodb.core.query.Query.*;
+import static java.util.UUID.randomUUID;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,7 +28,8 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
-import org.springframework.dao.DataAccessException;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
 import org.springframework.data.mongodb.core.script.NamedMongoScript;
@@ -38,8 +39,6 @@
 import org.springframework.util.StringUtils;
 
 import com.mongodb.BasicDBList;
-import com.mongodb.MongoException;
-import com.mongodb.client.MongoDatabase;
 
 /**
  * Default implementation of {@link ScriptOperations} capable of saving and executing {@link ExecutableMongoScript}.
@@ -51,6 +50,7 @@
  * @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0.
  */
 @Deprecated
+@NullUnmarked
 class DefaultScriptOperations implements ScriptOperations {
 
 	private static final String SCRIPT_COLLECTION_NAME = "system.js";
@@ -85,38 +85,28 @@ public NamedMongoScript register(NamedMongoScript script) {
 	}
 
 	@Override
-	public Object execute(ExecutableMongoScript script, Object... args) {
+	public @Nullable Object execute(ExecutableMongoScript script, Object... args) {
 
 		Assert.notNull(script, "Script must not be null");
 
-		return mongoOperations.execute(new DbCallback<Object>() {
+		return mongoOperations.execute(db -> {
 
-			@Override
-			public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
-
-				Document command = new Document("$eval", script.getCode());
-				BasicDBList commandArgs = new BasicDBList();
-				commandArgs.addAll(Arrays.asList(convertScriptArgs(false, args)));
-				command.append("args", commandArgs);
-				return db.runCommand(command).get("retval");
-			}
+			Document command = new Document("$eval", script.getCode());
+			BasicDBList commandArgs = new BasicDBList();
+			commandArgs.addAll(Arrays.asList(convertScriptArgs(false, args)));
+			command.append("args", commandArgs);
+			return db.runCommand(command).get("retval");
 		});
 	}
 
 	@Override
-	public Object call(String scriptName, Object... args) {
+	public @Nullable Object call(String scriptName, Object... args) {
 
 		Assert.hasText(scriptName, "ScriptName must not be null or empty");
 
-		return mongoOperations.execute(new DbCallback<Object>() {
-
-			@Override
-			public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
-
-				return db.runCommand(new Document("eval", String.format("%s(%s)", scriptName, convertAndJoinScriptArgs(args))))
-						.get("retval");
-			}
-		});
+		return mongoOperations.execute(
+				db -> db.runCommand(new Document("eval", String.format("%s(%s)", scriptName, convertAndJoinScriptArgs(args))))
+						.get("retval"));
 	}
 
 	@Override
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java
index 8b4de14e05..c445e06f8a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java
@@ -15,6 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
+
 import com.mongodb.WriteConcern;
 
 /**
@@ -26,7 +28,7 @@ enum DefaultWriteConcernResolver implements WriteConcernResolver {
 
 	INSTANCE;
 
-	public WriteConcern resolve(MongoAction action) {
+	public @Nullable WriteConcern resolve(MongoAction action) {
 		return action.getDefaultWriteConcern();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java
index 94352ad65c..ad3c2b8564 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.lang.Nullable;
 
 /**
  * Delegate class to encapsulate lifecycle event configuration and publishing.
@@ -47,6 +47,7 @@ public void setEventsEnabled(boolean eventsEnabled) {
 	 *
 	 * @param event the application event.
 	 */
+	@SuppressWarnings("NullAway")
 	public void publishEvent(Object event) {
 
 		if (canPublishEvent()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
index 65a5131dd1..87a937a09e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
@@ -25,9 +25,11 @@
 
 import org.bson.BsonNull;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.core.env.Environment;
 import org.springframework.core.env.EnvironmentCapable;
+import org.springframework.core.env.StandardEnvironment;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.convert.CustomConversions;
 import org.springframework.data.expression.ValueEvaluationContext;
@@ -63,7 +65,6 @@
 import org.springframework.data.projection.TargetAware;
 import org.springframework.data.util.Optionals;
 import org.springframework.expression.spel.support.SimpleEvaluationContext;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.LinkedMultiValueMap;
@@ -413,6 +414,7 @@ interface Entity<T> {
 		 *
 		 * @return
 		 */
+		@Nullable
 		Object getId();
 
 		/**
@@ -518,10 +520,9 @@ interface AdaptibleEntity<T> extends Entity<T> {
 		 * Populates the identifier of the backing entity if it has an identifier property and there's no identifier
 		 * currently present.
 		 *
-		 * @param id must not be {@literal null}.
+		 * @param id can be {@literal null}.
 		 * @return
 		 */
-		@Nullable
 		T populateIdIfNecessary(@Nullable Object id);
 
 		/**
@@ -564,12 +565,12 @@ public String getIdFieldName() {
 		}
 
 		@Override
-		public Object getId() {
+		public @Nullable Object getId() {
 			return getPropertyValue(ID_FIELD);
 		}
 
 		@Override
-		public Object getPropertyValue(String key) {
+		public @Nullable Object getPropertyValue(String key) {
 			return map.get(key);
 		}
 
@@ -578,7 +579,6 @@ public Query getByIdQuery() {
 			return Query.query(Criteria.where(ID_FIELD).is(map.get(ID_FIELD)));
 		}
 
-		@Nullable
 		@Override
 		public T populateIdIfNecessary(@Nullable Object id) {
 
@@ -605,8 +605,7 @@ public T initializeVersionProperty() {
 		}
 
 		@Override
-		@Nullable
-		public Number getVersion() {
+		public @Nullable Number getVersion() {
 			return null;
 		}
 
@@ -723,7 +722,7 @@ public Object getId() {
 		}
 
 		@Override
-		public Object getPropertyValue(String key) {
+		public @Nullable Object getPropertyValue(String key) {
 			return propertyAccessor.getProperty(entity.getRequiredPersistentProperty(key));
 		}
 
@@ -790,8 +789,7 @@ public boolean isVersionedEntity() {
 		}
 
 		@Override
-		@Nullable
-		public Object getVersion() {
+		public @Nullable Object getVersion() {
 			return propertyAccessor.getProperty(entity.getRequiredVersionProperty());
 		}
 
@@ -839,7 +837,6 @@ public Map<String, Object> extractKeys(Document sortObject, Class<?> sourceType)
 			return keyset;
 		}
 
-		@Nullable
 		private Object getNestedPropertyValue(String key) {
 
 			String[] segments = key.split("\\.");
@@ -852,6 +849,10 @@ private Object getNestedPropertyValue(String key) {
 				currentValue = currentEntity.getPropertyValue(segment);
 
 				if (i < segments.length - 1) {
+					if (currentValue == null) {
+						return BsonNull.VALUE;
+					}
+
 					currentEntity = entityOperations.forEntity(currentValue);
 				}
 			}
@@ -888,7 +889,6 @@ private static <T> AdaptibleEntity<T> of(T bean,
 					new ConvertingPropertyAccessor<>(propertyAccessor, conversionService), entityOperations);
 		}
 
-		@Nullable
 		@Override
 		public T populateIdIfNecessary(@Nullable Object id) {
 
@@ -910,8 +910,7 @@ public T populateIdIfNecessary(@Nullable Object id) {
 		}
 
 		@Override
-		@Nullable
-		public Number getVersion() {
+		public @Nullable Number getVersion() {
 
 			MongoPersistentProperty versionProperty = entity.getRequiredVersionProperty();
 
@@ -1127,7 +1126,7 @@ public TimeSeriesOptions mapTimeSeriesOptions(TimeSeriesOptions source) {
 
 		@Override
 		public String getIdKeyName() {
-			return entity.getIdProperty().getName();
+			return entity.getIdProperty() != null ? entity.getIdProperty().getName() : ID_FIELD;
 		}
 
 		private String mappedNameOrDefault(String name) {
@@ -1147,7 +1146,8 @@ private ValueEvaluationContext getEvaluationContextForEntity(@Nullable Persisten
 				return mongoEntity.getValueEvaluationContext(null);
 			}
 
-			return ValueEvaluationContext.of(this.environment, SimpleEvaluationContext.forReadOnlyDataBinding().build());
+			return ValueEvaluationContext.of(this.environment != null ? this.environment : new StandardEnvironment(),
+					SimpleEvaluationContext.forReadOnlyDataBinding().build());
 		}
 
 		/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java
index ca5aa7a513..d28afada0a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java
@@ -17,6 +17,7 @@
 
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.aggregation.AggregationResults;
 import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
@@ -55,11 +56,11 @@ static class ExecutableAggregationSupport<T>
 
 		private final MongoTemplate template;
 		private final Class<T> domainType;
-		private final Aggregation aggregation;
-		private final String collection;
+		private final @Nullable Aggregation aggregation;
+		private final @Nullable String collection;
 
-		public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, Aggregation aggregation,
-				String collection) {
+		public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, @Nullable Aggregation aggregation,
+				@Nullable String collection) {
 			this.template = template;
 			this.domainType = domainType;
 			this.aggregation = aggregation;
@@ -84,21 +85,25 @@ public TerminatingAggregation<T> by(Aggregation aggregation) {
 
 		@Override
 		public AggregationResults<T> all() {
+
+			Assert.notNull(aggregation, "Aggregation must be set first");
 			return template.aggregate(aggregation, getCollectionName(aggregation), domainType);
 		}
 
 		@Override
 		public Stream<T> stream() {
+
+			Assert.notNull(aggregation, "Aggregation must be set first");
 			return template.aggregateStream(aggregation, getCollectionName(aggregation), domainType);
 		}
 
-		private String getCollectionName(Aggregation aggregation) {
+		private String getCollectionName(@Nullable Aggregation aggregation) {
 
 			if (StringUtils.hasText(collection)) {
 				return collection;
 			}
 
-			if (aggregation instanceof TypedAggregation typedAggregation) {
+			if (aggregation instanceof TypedAggregation<?> typedAggregation) {
 
 				if (typedAggregation.getInputType() != null) {
 					return template.getCollectionName(typedAggregation.getInputType());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java
index 3358ff2b17..21cb37ae86 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java
@@ -19,6 +19,7 @@
 import java.util.Optional;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.domain.KeysetScrollPosition;
 import org.springframework.data.domain.ScrollPosition;
@@ -27,7 +28,6 @@
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
 import org.springframework.data.mongodb.core.query.NearQuery;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.client.MongoCollection;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java
index 4e6c3547c5..af8c80903a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java
@@ -16,18 +16,17 @@
 package org.springframework.data.mongodb.core;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Stream;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.data.domain.ScrollPosition;
 import org.springframework.data.domain.Window;
 import org.springframework.data.mongodb.core.query.NearQuery;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -53,6 +52,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public <T> ExecutableFind<T> query(Class<T> domainType) {
 
 		Assert.notNull(domainType, "DomainType must not be null");
@@ -84,6 +84,7 @@ static class ExecutableFindSupport<T>
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public FindWithProjection<T> inCollection(String collection) {
 
 			Assert.hasText(collection, "Collection name must not be null nor empty");
@@ -92,6 +93,7 @@ public FindWithProjection<T> inCollection(String collection) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public <T1> FindWithQuery<T1> as(Class<T1> returnType) {
 
 			Assert.notNull(returnType, "ReturnType must not be null");
@@ -100,6 +102,7 @@ public <T1> FindWithQuery<T1> as(Class<T1> returnType) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingFind<T> matching(Query query) {
 
 			Assert.notNull(query, "Query must not be null");
@@ -108,7 +111,7 @@ public TerminatingFind<T> matching(Query query) {
 		}
 
 		@Override
-		public T oneValue() {
+		public @Nullable T oneValue() {
 
 			List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(2));
 
@@ -124,7 +127,7 @@ public T oneValue() {
 		}
 
 		@Override
-		public T firstValue() {
+		public @Nullable T firstValue() {
 
 			List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(1));
 
@@ -206,10 +209,10 @@ private String asString() {
 	 * @author Christoph Strobl
 	 * @since 2.0
 	 */
-	static class DelegatingQueryCursorPreparer implements SortingQueryCursorPreparer {
+	static class DelegatingQueryCursorPreparer implements CursorPreparer {
 
 		private final @Nullable CursorPreparer delegate;
-		private Optional<Integer> limit = Optional.empty();
+		private int limit = -1;
 
 		DelegatingQueryCursorPreparer(@Nullable CursorPreparer delegate) {
 			this.delegate = delegate;
@@ -219,25 +222,22 @@ static class DelegatingQueryCursorPreparer implements SortingQueryCursorPreparer
 		public FindIterable<Document> prepare(FindIterable<Document> iterable) {
 
 			FindIterable<Document> target = delegate != null ? delegate.prepare(iterable) : iterable;
-			return limit.map(target::limit).orElse(target);
+			if (limit >= 0) {
+				target.limit(limit);
+			}
+			return target;
 		}
 
+		@Contract("_ -> this")
 		CursorPreparer limit(int limit) {
 
-			this.limit = Optional.of(limit);
+			this.limit = limit;
 			return this;
 		}
 
 		@Override
-		@Nullable
-		public ReadPreference getReadPreference() {
-			return delegate.getReadPreference();
-		}
-
-		@Override
-		@Nullable
-		public Document getSortObject() {
-			return delegate instanceof SortingQueryCursorPreparer sqcp ? sqcp.getSortObject() : null;
+		public @Nullable ReadPreference getReadPreference() {
+			return delegate != null ? delegate.getReadPreference() : null;
 		}
 	}
 
@@ -258,6 +258,7 @@ public DistinctOperationSupport(ExecutableFindSupport<T> delegate, String field)
 
 		@Override
 		@SuppressWarnings("unchecked")
+		@Contract("_ -> new")
 		public <R> TerminatingDistinct<R> as(Class<R> resultType) {
 
 			Assert.notNull(resultType, "ResultType must not be null");
@@ -266,6 +267,7 @@ public <R> TerminatingDistinct<R> as(Class<R> resultType) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingDistinct<T> matching(Query query) {
 
 			Assert.notNull(query, "Query must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java
index 47b7127deb..599a910035 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java
@@ -18,8 +18,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -41,6 +42,7 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public <T> ExecutableInsert<T> insert(Class<T> domainType) {
 
 		Assert.notNull(domainType, "DomainType must not be null");
@@ -56,10 +58,11 @@ static class ExecutableInsertSupport<T> implements ExecutableInsert<T> {
 
 		private final MongoTemplate template;
 		private final Class<T> domainType;
-		@Nullable private final String collection;
-		@Nullable private final BulkMode bulkMode;
+		private final @Nullable String collection;
+		private final @Nullable BulkMode bulkMode;
 
-		ExecutableInsertSupport(MongoTemplate template, Class<T> domainType, String collection, BulkMode bulkMode) {
+		ExecutableInsertSupport(MongoTemplate template, Class<T> domainType, @Nullable String collection,
+				@Nullable BulkMode bulkMode) {
 
 			this.template = template;
 			this.domainType = domainType;
@@ -93,6 +96,7 @@ public BulkWriteResult bulk(Collection<? extends T> objects) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public InsertWithBulkMode<T> inCollection(String collection) {
 
 			Assert.hasText(collection, "Collection must not be null nor empty");
@@ -101,6 +105,7 @@ public InsertWithBulkMode<T> inCollection(String collection) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingBulkInsert<T> withBulkMode(BulkMode bulkMode) {
 
 			Assert.notNull(bulkMode, "BulkMode must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java
index 9f78693540..55864cbd8e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java
@@ -17,9 +17,10 @@
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -46,6 +47,7 @@ class ExecutableMapReduceOperationSupport implements ExecutableMapReduceOperatio
 	 * @see in org.springframework.data.mongodb.core.ExecutableMapReduceOperation#mapReduce(java.lang.Class)
 	 */
 	@Override
+	@Contract("_ -> new")
 	public <T> ExecutableMapReduceSupport<T> mapReduce(Class<T> domainType) {
 
 		Assert.notNull(domainType, "DomainType must not be null");
@@ -89,6 +91,7 @@ static class ExecutableMapReduceSupport<T>
 		 * @see in org.springframework.data.mongodb.core.ExecutableMapReduceOperation.TerminatingMapReduce#all()
 		 */
 		@Override
+		@SuppressWarnings("NullAway")
 		public List<T> all() {
 			return template.mapReduce(query, domainType, getCollectionName(), mapFunction, reduceFunction, options,
 					returnType);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java
index 8e84aa7dd6..e53e80b10f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java
@@ -17,8 +17,9 @@
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -42,6 +43,7 @@ public ExecutableRemoveOperationSupport(MongoTemplate tempate) {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public <T> ExecutableRemove<T> remove(Class<T> domainType) {
 
 		Assert.notNull(domainType, "DomainType must not be null");
@@ -60,7 +62,8 @@ static class ExecutableRemoveSupport<T> implements ExecutableRemove<T>, RemoveWi
 		private final Query query;
 		@Nullable private final String collection;
 
-		public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query, String collection) {
+		public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query,
+				@Nullable String collection) {
 			this.template = template;
 			this.domainType = domainType;
 			this.query = query;
@@ -68,6 +71,7 @@ public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Quer
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public RemoveWithQuery<T> inCollection(String collection) {
 
 			Assert.hasText(collection, "Collection must not be null nor empty");
@@ -76,6 +80,7 @@ public RemoveWithQuery<T> inCollection(String collection) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingRemove<T> matching(Query query) {
 
 			Assert.notNull(query, "Query must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java
index a5c63e9b67..69365459ba 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java
@@ -17,12 +17,12 @@
 
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AggregationUpdate;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.client.result.UpdateResult;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java
index 593d863d39..75756c6f1e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java
@@ -15,9 +15,10 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -41,6 +42,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public <T> ExecutableUpdate<T> update(Class<T> domainType) {
 
 		Assert.notNull(domainType, "DomainType must not be null");
@@ -52,6 +54,7 @@ public <T> ExecutableUpdate<T> update(Class<T> domainType) {
 	 * @author Christoph Strobl
 	 * @since 2.0
 	 */
+	@SuppressWarnings("rawtypes")
 	static class ExecutableUpdateSupport<T>
 			implements ExecutableUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>,
 			FindAndReplaceWithOptions<T>, TerminatingFindAndReplace<T>, FindAndReplaceWithProjection<T> {
@@ -66,9 +69,9 @@ static class ExecutableUpdateSupport<T>
 		@Nullable private final Object replacement;
 		private final Class<T> targetType;
 
-		ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, UpdateDefinition update,
-				String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
-				Object replacement, Class<T> targetType) {
+		ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, @Nullable UpdateDefinition update,
+				@Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions,
+				@Nullable FindAndReplaceOptions findAndReplaceOptions, @Nullable Object replacement, Class<T> targetType) {
 
 			this.template = template;
 			this.domainType = domainType;
@@ -82,6 +85,7 @@ static class ExecutableUpdateSupport<T>
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingUpdate<T> apply(UpdateDefinition update) {
 
 			Assert.notNull(update, "Update must not be null");
@@ -91,6 +95,7 @@ public TerminatingUpdate<T> apply(UpdateDefinition update) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public UpdateWithQuery<T> inCollection(String collection) {
 
 			Assert.hasText(collection, "Collection must not be null nor empty");
@@ -100,6 +105,7 @@ public UpdateWithQuery<T> inCollection(String collection) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -109,6 +115,7 @@ public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public FindAndReplaceWithProjection<T> replaceWith(T replacement) {
 
 			Assert.notNull(replacement, "Replacement must not be null");
@@ -118,6 +125,7 @@ public FindAndReplaceWithProjection<T> replaceWith(T replacement) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public FindAndReplaceWithProjection<T> withOptions(FindAndReplaceOptions options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -127,6 +135,7 @@ public FindAndReplaceWithProjection<T> withOptions(FindAndReplaceOptions options
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TerminatingReplace withOptions(ReplaceOptions options) {
 
 			FindAndReplaceOptions target = new FindAndReplaceOptions();
@@ -138,6 +147,7 @@ public TerminatingReplace withOptions(ReplaceOptions options) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public UpdateWithUpdate<T> matching(Query query) {
 
 			Assert.notNull(query, "Query must not be null");
@@ -147,6 +157,7 @@ public UpdateWithUpdate<T> matching(Query query) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public <R> FindAndReplaceWithOptions<R> as(Class<R> resultType) {
 
 			Assert.notNull(resultType, "ResultType must not be null");
@@ -171,6 +182,7 @@ public UpdateResult upsert() {
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public @Nullable T findAndModifyValue() {
 
 			return template.findAndModify(query, update,
@@ -179,6 +191,7 @@ public UpdateResult upsert() {
 		}
 
 		@Override
+		@SuppressWarnings({ "unchecked", "NullAway" })
 		public @Nullable T findAndReplaceValue() {
 
 			return (T) template.findAndReplace(query, replacement,
@@ -187,6 +200,7 @@ public UpdateResult upsert() {
 		}
 
 		@Override
+		@SuppressWarnings({ "unchecked", "NullAway" })
 		public UpdateResult replaceFirst() {
 
 			if (replacement != null) {
@@ -198,6 +212,7 @@ public UpdateResult replaceFirst() {
 					findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName());
 		}
 
+		@SuppressWarnings("NullAway")
 		private UpdateResult doUpdate(boolean multi, boolean upsert) {
 			return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
index 51a2c5b86a..6e9b775324 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
@@ -17,8 +17,9 @@
 
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * @author Mark Pollak
@@ -99,16 +100,19 @@ public static FindAndModifyOptions of(@Nullable FindAndModifyOptions source) {
 		return options;
 	}
 
+	@Contract("_ -> this")
 	public FindAndModifyOptions returnNew(boolean returnNew) {
 		this.returnNew = returnNew;
 		return this;
 	}
 
+	@Contract("_ -> this")
 	public FindAndModifyOptions upsert(boolean upsert) {
 		this.upsert = upsert;
 		return this;
 	}
 
+	@Contract("_ -> this")
 	public FindAndModifyOptions remove(boolean remove) {
 		this.remove = remove;
 		return this;
@@ -121,6 +125,7 @@ public FindAndModifyOptions remove(boolean remove) {
 	 * @return this.
 	 * @since 2.0
 	 */
+	@Contract("_ -> this")
 	public FindAndModifyOptions collation(@Nullable Collation collation) {
 
 		this.collation = collation;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java
index 266a0742c2..2005ba3c6c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java
@@ -15,6 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.springframework.lang.Contract;
+
 /**
  * Options for
  * <a href="https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndReplace/">findOneAndReplace</a>.
@@ -95,6 +97,7 @@ public static FindAndReplaceOptions empty() {
 	 *
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public FindAndReplaceOptions returnNew() {
 
 		this.returnNew = true;
@@ -106,6 +109,7 @@ public FindAndReplaceOptions returnNew() {
 	 *
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public FindAndReplaceOptions upsert() {
 
 		super.upsert();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java
index 625a85950e..f04417325c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java
@@ -18,7 +18,7 @@
 import java.util.function.Function;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadPreference;
@@ -76,8 +76,7 @@ default FindPublisher<Document> initiateFind(MongoCollection<Document> collectio
 	 * @since 2.2
 	 */
 	@Override
-	@Nullable
-	default ReadPreference getReadPreference() {
+	default @Nullable ReadPreference getReadPreference() {
 		return null;
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java
index 57abe9a529..043613122a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java
@@ -18,9 +18,9 @@
 import java.util.function.Function;
 
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.CodecRegistryProvider;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.StringUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
index f5856100d0..c206593fdc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
@@ -18,12 +18,11 @@
 import java.util.concurrent.TimeUnit;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.mongodb.core.index.IndexDefinition;
 import org.springframework.data.mongodb.core.index.IndexInfo;
 import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 import com.mongodb.client.model.Collation;
@@ -129,8 +128,7 @@ private static Converter<IndexDefinition, IndexOptions> getIndexDefinitionIndexO
 		};
 	}
 
-	@Nullable
-	public static Collation fromDocument(@Nullable Document source) {
+	public static @Nullable Collation fromDocument(@Nullable Document source) {
 
 		if (source == null) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
index da4766343a..cd9ba90453 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
@@ -20,6 +20,7 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
@@ -70,7 +71,7 @@ public boolean hasNonNullId() {
 		return hasId() && document.get(ID_FIELD) != null;
 	}
 
-	public Object getId() {
+	public @Nullable Object getId() {
 		return document.get(ID_FIELD);
 	}
 
@@ -86,7 +87,7 @@ public Bson getIdFilter() {
 		return new Document(ID_FIELD, document.get(ID_FIELD));
 	}
 
-	public Object get(String key) {
+	public @Nullable Object get(String key) {
 		return document.get(key);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
index 839f49c7da..789b9a013e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
@@ -24,6 +24,7 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -41,6 +42,7 @@
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject;
 import org.springframework.data.util.TypeInformation;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.CollectionUtils;
@@ -89,6 +91,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public MongoJsonSchemaCreator filter(Predicate<JsonSchemaPropertyContext> filter) {
 		return new MappingMongoJsonSchemaCreator(converter, mappingContext, filter, mergeProperties);
 	}
@@ -106,6 +109,7 @@ public PropertySpecifier property(String path) {
 	 * @return new instance of {@link MongoJsonSchemaCreator}.
 	 * @since 3.4
 	 */
+	@Contract("_, _ -> new")
 	public MongoJsonSchemaCreator withTypesFor(String path, Class<?>... types) {
 
 		LinkedMultiValueMap<String, Class<?>> clone = mergeProperties.clone();
@@ -176,6 +180,7 @@ private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistent
 		return schemaProperties;
 	}
 
+	@SuppressWarnings("NullAway")
 	private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty> path) {
 
 		String stringPath = path.stream().map(MongoPersistentProperty::getName).collect(Collectors.joining("."));
@@ -337,7 +342,9 @@ private TypedJsonSchemaObject createSchemaObject(Object type, Collection<?> poss
 		return schemaObject;
 	}
 
-	private String computePropertyFieldName(PersistentProperty<?> property) {
+	private String computePropertyFieldName(@Nullable PersistentProperty<?> property) {
+		
+		Assert.notNull(property, "Property must not be null");
 
 		return property instanceof MongoPersistentProperty mongoPersistentProperty ? mongoPersistentProperty.getFieldName()
 				: property.getName();
@@ -409,7 +416,8 @@ public MongoPersistentProperty getProperty() {
 		}
 
 		@Override
-		public <T> MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property) {
+		@SuppressWarnings("unchecked")
+		public <T> @Nullable MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property) {
 			return (MongoPersistentEntity<T>) mappingContext.getPersistentEntity(property);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java
index fdfeaa81ad..c827c5b8a9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.WriteConcern;
@@ -72,28 +72,23 @@ public String getCollectionName() {
 		return collectionName;
 	}
 
-	@Nullable
-	public WriteConcern getDefaultWriteConcern() {
+	public @Nullable WriteConcern getDefaultWriteConcern() {
 		return defaultWriteConcern;
 	}
 
-	@Nullable
-	public Class<?> getEntityType() {
+	public @Nullable Class<?> getEntityType() {
 		return entityType;
 	}
 
-	@Nullable
-	public MongoActionOperation getMongoActionOperation() {
+	public @Nullable MongoActionOperation getMongoActionOperation() {
 		return mongoActionOperation;
 	}
 
-	@Nullable
-	public Document getQuery() {
+	public @Nullable Document getQuery() {
 		return query;
 	}
 
-	@Nullable
-	public Document getDocument() {
+	public @Nullable Document getDocument() {
 		return document;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java
index c5fee9cf54..9210dd85ec 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java
@@ -24,11 +24,11 @@
 import java.util.stream.Collectors;
 
 import org.bson.UuidRepresentation;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.config.AbstractFactoryBean;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.support.PersistenceExceptionTranslator;
 import org.springframework.data.mongodb.SpringDataMongoDB;
-import org.springframework.lang.Nullable;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -78,7 +78,7 @@ public void setMongoClientSettings(@Nullable MongoClientSettings mongoClientOpti
 	 *
 	 * @param credential can be {@literal null}.
 	 */
-	public void setCredential(@Nullable MongoCredential[] credential) {
+	public void setCredential(MongoCredential @Nullable[] credential) {
 		this.credential = Arrays.asList(credential);
 	}
 
@@ -119,8 +119,7 @@ public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exce
 	}
 
 	@Override
-	@Nullable
-	public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+	public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
 		return exceptionTranslator.translateExceptionIfPossible(ex);
 	}
 
@@ -316,13 +315,13 @@ private <T> void applySettings(Consumer<T> settingsBuilder, @Nullable T value) {
 		settingsBuilder.accept(value);
 	}
 
-	private <S, T> T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder,
+	private <S, T> @Nullable T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder,
 			@Nullable T connectionStringValue) {
 		return computeSettingsValue(function.apply(defaultValueHolder), function.apply(settingsValueHolder),
 				connectionStringValue);
 	}
 
-	private <T> T computeSettingsValue(T defaultValue, T fromSettings, T fromConnectionString) {
+	private <T> @Nullable T computeSettingsValue(@Nullable T defaultValue, T fromSettings, @Nullable T fromConnectionString) {
 
 		boolean fromSettingsIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromSettings);
 		boolean fromConnectionStringIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromConnectionString);
@@ -337,7 +336,7 @@ private MongoClient createMongoClient(MongoClientSettings settings) throws Unkno
 		return MongoClients.create(settings, SpringDataMongoDB.driverInformation());
 	}
 
-	private String getOrDefault(Object value, String defaultValue) {
+	private String getOrDefault(@Nullable Object value, String defaultValue) {
 
 		if(value == null) {
 			return defaultValue;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java
index 02913b4303..0bce8db304 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java
@@ -25,10 +25,9 @@
 
 import org.bson.UuidRepresentation;
 import org.bson.codecs.configuration.CodecRegistry;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.config.AbstractFactoryBean;
 import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
-import org.springframework.lang.Nullable;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java
index eab6b5d7f4..0a62b7aa49 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java
@@ -15,12 +15,13 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.aop.framework.ProxyFactory;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.support.PersistenceExceptionTranslator;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -132,6 +133,7 @@ public void destroy() throws Exception {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public MongoDatabaseFactory withSession(ClientSession session) {
 		return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java
index 7aef5a3a82..f361b19bba 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java
@@ -19,8 +19,8 @@
 import java.util.Map;
 
 import org.bson.BsonDocument;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.FactoryBean;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.AutoEncryptionSettings;
 import com.mongodb.MongoClientSettings;
@@ -34,11 +34,11 @@
 public class MongoEncryptionSettingsFactoryBean implements FactoryBean<AutoEncryptionSettings> {
 
 	private boolean bypassAutoEncryption;
-	private String keyVaultNamespace;
-	private Map<String, Object> extraOptions;
-	private MongoClientSettings keyVaultClientSettings;
-	private Map<String, Map<String, Object>> kmsProviders;
-	private Map<String, BsonDocument> schemaMap;
+	private @Nullable String keyVaultNamespace;
+	private @Nullable Map<String, Object> extraOptions;
+	private @Nullable MongoClientSettings keyVaultClientSettings;
+	private @Nullable Map<String, Map<String, Object>> kmsProviders;
+	private @Nullable Map<String, BsonDocument> schemaMap;
 
 	/**
 	 * @param bypassAutoEncryption
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java
index 1ec7d3ffc0..2bde873c2f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java
@@ -18,7 +18,7 @@
 import java.util.Set;
 
 import org.bson.BsonInvalidOperationException;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.DataAccessResourceFailureException;
 import org.springframework.dao.DataIntegrityViolationException;
@@ -31,7 +31,6 @@
 import org.springframework.data.mongodb.TransientClientSessionException;
 import org.springframework.data.mongodb.UncategorizedMongoDbException;
 import org.springframework.data.mongodb.util.MongoDbErrorCodes;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.MongoBulkWriteException;
@@ -69,12 +68,12 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator
 	private static final Set<String> SECURITY_EXCEPTIONS = Set.of("MongoCryptException");
 
 	@Override
-	@Nullable
-	public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+	public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
 		return doTranslateException(ex);
 	}
 
 	@Nullable
+	@SuppressWarnings("NullAway")
 	DataAccessException doTranslateException(RuntimeException ex) {
 
 		// Check for well-known MongoException subclasses.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
index 66b1cf209e..84c395bf2f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
@@ -139,8 +139,7 @@ interface JsonSchemaPropertyContext {
 		 * @return {@literal null} if the property is not an entity. It is nevertheless recommend to check
 		 *         {@link PersistentProperty#isEntity()} first.
 		 */
-		@Nullable
-		<T> MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property);
+		<T> @Nullable MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property);
 
 	}
 
@@ -162,6 +161,7 @@ public boolean test(JsonSchemaPropertyContext context) {
 				return extracted(context.getProperty(), context);
 			}
 
+			@SuppressWarnings("NullAway")
 			private boolean extracted(MongoPersistentProperty property, JsonSchemaPropertyContext context) {
 				if (property.isAnnotationPresent(Encrypted.class)) {
 					return true;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
index f400b35a68..6eb29fe0bf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
@@ -24,6 +24,7 @@
 import java.util.stream.Stream;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.KeysetScrollPosition;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Window;
@@ -49,7 +50,6 @@
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.util.Lock;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -195,7 +195,8 @@ default SessionScoped withSession(Supplier<ClientSession> sessionProvider) {
 			private @Nullable ClientSession session;
 
 			@Override
-			public <T> T execute(SessionCallback<T> action, Consumer<ClientSession> onComplete) {
+			@SuppressWarnings("NullAway")
+			public <T> @Nullable T execute(SessionCallback<T> action, Consumer<ClientSession> onComplete) {
 
 				lock.executeWithoutResult(() -> {
 
@@ -732,8 +733,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
 	 * @param entityClass the parametrized type of the returned list.
 	 * @return the converted object.
 	 */
-	@Nullable
-	<T> T findOne(Query query, Class<T> entityClass);
+	<T> @Nullable T findOne(Query query, Class<T> entityClass);
 
 	/**
 	 * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified
@@ -749,8 +749,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
 	 * @param collectionName name of the collection to retrieve the objects from.
 	 * @return the converted object.
 	 */
-	@Nullable
-	<T> T findOne(Query query, Class<T> entityClass, String collectionName);
+	<T> @Nullable T findOne(Query query, Class<T> entityClass, String collectionName);
 
 	/**
 	 * Determine result of given {@link Query} contains at least one element. <br />
@@ -870,8 +869,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
 	 * @param entityClass the type the document shall be converted into. Must not be {@literal null}.
 	 * @return the document with the given id mapped onto the given target class.
 	 */
-	@Nullable
-	<T> T findById(Object id, Class<T> entityClass);
+	<T> @Nullable T findById(Object id, Class<T> entityClass);
 
 	/**
 	 * Returns the document with the given id from the given collection mapped onto the given target class.
@@ -881,8 +879,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin
 	 * @param collectionName the collection to query for the document.
 	 * @return he converted object or {@literal null} if document does not exist.
 	 */
-	@Nullable
-	<T> T findById(Object id, Class<T> entityClass, String collectionName);
+	<T> @Nullable T findById(Object id, Class<T> entityClass, String collectionName);
 
 	/**
 	 * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and
@@ -959,8 +956,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
 	 * @see Update
 	 * @see AggregationUpdate
 	 */
-	@Nullable
-	<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
+	<T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass);
 
 	/**
 	 * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a>
@@ -979,8 +975,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
 	 * @see Update
 	 * @see AggregationUpdate
 	 */
-	@Nullable
-	<T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
+	<T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName);
 
 	/**
 	 * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a>
@@ -1002,8 +997,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
 	 * @see Update
 	 * @see AggregationUpdate
 	 */
-	@Nullable
-	<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
+	<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass);
 
 	/**
 	 * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a>
@@ -1026,8 +1020,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C
 	 * @see Update
 	 * @see AggregationUpdate
 	 */
-	@Nullable
-	<T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
+	<T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
 			String collectionName);
 
 	/**
@@ -1047,8 +1040,7 @@ <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions o
 	 *           {@link #getCollectionName(Class) derived} from the given replacement value.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <T> T findAndReplace(Query query, T replacement) {
+	default <T> @Nullable T findAndReplace(Query query, T replacement) {
 		return findAndReplace(query, replacement, FindAndReplaceOptions.empty());
 	}
 
@@ -1067,8 +1059,7 @@ default <T> T findAndReplace(Query query, T replacement) {
 	 * @return the converted object that was updated or {@literal null}, if not found.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <T> T findAndReplace(Query query, T replacement, String collectionName) {
+	default <T> @Nullable T findAndReplace(Query query, T replacement, String collectionName) {
 		return findAndReplace(query, replacement, FindAndReplaceOptions.empty(), collectionName);
 	}
 
@@ -1090,8 +1081,7 @@ default <T> T findAndReplace(Query query, T replacement, String collectionName)
 	 *           {@link #getCollectionName(Class) derived} from the given replacement value.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) {
+	default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) {
 		return findAndReplace(query, replacement, options, getCollectionName(ClassUtils.getUserClass(replacement)));
 	}
 
@@ -1111,8 +1101,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o
 	 *         as it is after the update.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) {
+	default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) {
 
 		Assert.notNull(replacement, "Replacement must not be null");
 		return findAndReplace(query, replacement, options, (Class<T>) ClassUtils.getUserClass(replacement), collectionName);
@@ -1136,8 +1125,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o
 	 *         as it is after the update.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class<T> entityType,
+	default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class<T> entityType,
 			String collectionName) {
 
 		return findAndReplace(query, replacement, options, entityType, collectionName, entityType);
@@ -1165,8 +1153,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o
 	 *           {@link #getCollectionName(Class) derived} from the given replacement value.
 	 * @since 2.1
 	 */
-	@Nullable
-	default <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
+	default <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
 			Class<T> resultType) {
 
 		return findAndReplace(query, replacement, options, entityType,
@@ -1193,8 +1180,7 @@ default <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOption
 	 *         as it is after the update.
 	 * @since 2.1
 	 */
-	@Nullable
-	<S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
+	<S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
 			String collectionName, Class<T> resultType);
 
 	/**
@@ -1210,8 +1196,7 @@ <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions option
 	 * @param entityClass the parametrized type of the returned list.
 	 * @return the converted object
 	 */
-	@Nullable
-	<T> T findAndRemove(Query query, Class<T> entityClass);
+	<T> @Nullable T findAndRemove(Query query, Class<T> entityClass);
 
 	/**
 	 * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified
@@ -1228,8 +1213,7 @@ <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions option
 	 * @param collectionName name of the collection to retrieve the objects from.
 	 * @return the converted object.
 	 */
-	@Nullable
-	<T> T findAndRemove(Query query, Class<T> entityClass, String collectionName);
+	<T> @Nullable T findAndRemove(Query query, Class<T> entityClass, String collectionName);
 
 	/**
 	 * Returns the number of documents for the given {@link Query} by querying the collection of the given entity class.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java
index 37001faa4e..574c0c8931 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.FactoryBean;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 import com.mongodb.ServerApi;
@@ -31,7 +31,7 @@
  */
 public class MongoServerApiFactoryBean implements FactoryBean<ServerApi> {
 
-	private String version;
+	private @Nullable String version;
 	private @Nullable Boolean deprecationErrors;
 	private @Nullable Boolean strict;
 
@@ -59,9 +59,8 @@ public void setStrict(@Nullable Boolean strict) {
 		this.strict = strict;
 	}
 
-	@Nullable
 	@Override
-	public ServerApi getObject() throws Exception {
+	public @Nullable ServerApi getObject() throws Exception {
 
 		Builder builder = ServerApi.builder().version(version());
 
@@ -81,6 +80,11 @@ public Class<?> getObjectType() {
 	}
 
 	private ServerApiVersion version() {
+
+		if(version == null) {
+			return ServerApiVersion.V1;
+		}
+
 		try {
 			// lookup by name eg. 'V1'
 			return ObjectUtils.caseInsensitiveValueOf(ServerApiVersion.values(), version);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
index 67ef3a3081..6ec454cd93 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
@@ -15,12 +15,22 @@
  */
 package org.springframework.data.mongodb.core;
 
-import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
+import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely;
 
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiPredicate;
 import java.util.stream.Collectors;
@@ -30,7 +40,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.bson.Document;
 import org.bson.conversions.Bson;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -85,17 +95,25 @@
 import org.springframework.data.mongodb.core.convert.MongoWriter;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.convert.UpdateMapper;
-import org.springframework.data.mongodb.core.index.DefaultSearchIndexOperations;
 import org.springframework.data.mongodb.core.index.IndexOperations;
 import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
 import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
 import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
-import org.springframework.data.mongodb.core.index.SearchIndexOperations;
-import org.springframework.data.mongodb.core.index.SearchIndexOperationsProvider;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.data.mongodb.core.mapping.event.*;
+import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
+import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
+import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback;
+import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback;
+import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
+import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
 import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
 import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
 import org.springframework.data.mongodb.core.query.BasicQuery;
@@ -111,7 +129,7 @@
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.util.CloseableIterator;
 import org.springframework.data.util.Optionals;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.CollectionUtils;
@@ -134,7 +152,21 @@
 import com.mongodb.client.MongoCursor;
 import com.mongodb.client.MongoDatabase;
 import com.mongodb.client.MongoIterable;
-import com.mongodb.client.model.*;
+import com.mongodb.client.model.CountOptions;
+import com.mongodb.client.model.CreateCollectionOptions;
+import com.mongodb.client.model.CreateViewOptions;
+import com.mongodb.client.model.DeleteOptions;
+import com.mongodb.client.model.EstimatedDocumentCountOptions;
+import com.mongodb.client.model.FindOneAndDeleteOptions;
+import com.mongodb.client.model.FindOneAndReplaceOptions;
+import com.mongodb.client.model.FindOneAndUpdateOptions;
+import com.mongodb.client.model.ReturnDocument;
+import com.mongodb.client.model.TimeSeriesGranularity;
+import com.mongodb.client.model.TimeSeriesOptions;
+import com.mongodb.client.model.UpdateOptions;
+import com.mongodb.client.model.ValidationAction;
+import com.mongodb.client.model.ValidationLevel;
+import com.mongodb.client.model.ValidationOptions;
 import com.mongodb.client.result.DeleteResult;
 import com.mongodb.client.result.UpdateResult;
 
@@ -184,10 +216,9 @@
  * @author Bartłomiej Mazur
  * @author Michael Krog
  * @author Jakub Zurawa
- * @author Florian Lüdiger
  */
-public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider,
-		SearchIndexOperationsProvider, ReadPreferenceAware {
+public class MongoTemplate
+		implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, ReadPreferenceAware {
 
 	private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
 	private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
@@ -343,7 +374,7 @@ public boolean hasReadPreference() {
 	}
 
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 		return this.readPreference;
 	}
 
@@ -479,7 +510,7 @@ public <T> Stream<T> stream(Query query, Class<T> entityType, String collectionN
 		return doStream(query, entityType, collectionName, entityType);
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected <T> Stream<T> doStream(Query query, Class<?> entityType, String collectionName, Class<T> returnType) {
 
 		Assert.notNull(query, "Query must not be null");
@@ -512,7 +543,7 @@ public String getCollectionName(Class<?> entityClass) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public Document executeCommand(String jsonCommand) {
 
 		Assert.hasText(jsonCommand, "JsonCommand must not be null nor empty");
@@ -521,7 +552,7 @@ public Document executeCommand(String jsonCommand) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public Document executeCommand(Document command) {
 
 		Assert.notNull(command, "Command must not be null");
@@ -530,7 +561,7 @@ public Document executeCommand(Document command) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public Document executeCommand(Document command, @Nullable ReadPreference readPreference) {
 
 		Assert.notNull(command, "Command must not be null");
@@ -577,7 +608,7 @@ protected void executeQuery(Query query, String collectionName, DocumentCallback
 	}
 
 	@Override
-	public <T> T execute(DbCallback<T> action) {
+	public <T> @Nullable T execute(DbCallback<T> action) {
 
 		Assert.notNull(action, "DbCallback must not be null");
 
@@ -590,14 +621,14 @@ public <T> T execute(DbCallback<T> action) {
 	}
 
 	@Override
-	public <T> T execute(Class<?> entityClass, CollectionCallback<T> callback) {
+	public <T> @Nullable T execute(Class<?> entityClass, CollectionCallback<T> callback) {
 
 		Assert.notNull(entityClass, "EntityClass must not be null");
 		return execute(getCollectionName(entityClass), callback);
 	}
 
 	@Override
-	public <T> T execute(String collectionName, CollectionCallback<T> callback) {
+	public <T> @Nullable T execute(String collectionName, CollectionCallback<T> callback) {
 
 		Assert.notNull(collectionName, "CollectionName must not be null");
 		Assert.notNull(callback, "CollectionCallback must not be null");
@@ -619,6 +650,7 @@ public SessionScoped withSession(ClientSessionOptions options) {
 	}
 
 	@Override
+	@Contract("_ -> new")
 	public MongoTemplate withSession(ClientSession session) {
 
 		Assert.notNull(session, "ClientSession must not be null");
@@ -692,6 +724,7 @@ private MongoCollection<Document> createView(String name, String source, Aggrega
 		return doCreateView(name, source, aggregation.getAggregationPipeline(), options);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected MongoCollection<Document> doCreateView(String name, String source, List<Document> pipeline,
 			@Nullable ViewOptions options) {
 
@@ -707,8 +740,9 @@ protected MongoCollection<Document> doCreateView(String name, String source, Lis
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
-	public MongoCollection<Document> getCollection(String collectionName) {
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
+	@Contract("null -> fail")
+	public MongoCollection<Document> getCollection(@Nullable String collectionName) {
 
 		Assert.notNull(collectionName, "CollectionName must not be null");
 
@@ -721,7 +755,7 @@ public <T> boolean collectionExists(Class<T> entityClass) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public boolean collectionExists(String collectionName) {
 
 		Assert.notNull(collectionName, "CollectionName must not be null");
@@ -772,21 +806,6 @@ public IndexOperations indexOps(Class<?> entityClass) {
 		return indexOps(getCollectionName(entityClass), entityClass);
 	}
 
-	@Override
-	public SearchIndexOperations searchIndexOps(String collectionName) {
-		return searchIndexOps(null, collectionName);
-	}
-
-	@Override
-	public SearchIndexOperations searchIndexOps(Class<?> type) {
-		return new DefaultSearchIndexOperations(this, type);
-	}
-
-	@Override
-	public SearchIndexOperations searchIndexOps(@Nullable Class<?> type, String collectionName) {
-		return new DefaultSearchIndexOperations(this, collectionName, type);
-	}
-
 	@Override
 	public BulkOperations bulkOps(BulkMode mode, String collectionName) {
 		return bulkOps(mode, null, collectionName);
@@ -819,15 +838,13 @@ public ScriptOperations scriptOps() {
 
 	// Find methods that take a Query to express the query and that return a single object.
 
-	@Nullable
 	@Override
-	public <T> T findOne(Query query, Class<T> entityClass) {
+	public <T> @Nullable T findOne(Query query, Class<T> entityClass) {
 		return findOne(query, entityClass, getCollectionName(entityClass));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findOne(Query query, Class<T> entityClass, String collectionName) {
+	public <T> @Nullable T findOne(Query query, Class<T> entityClass, String collectionName) {
 
 		Assert.notNull(query, "Query must not be null");
 		Assert.notNull(entityClass, "EntityClass must not be null");
@@ -855,7 +872,7 @@ public boolean exists(Query query, String collectionName) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public boolean exists(Query query, @Nullable Class<?> entityClass, String collectionName) {
 
 		if (query == null) {
@@ -931,15 +948,13 @@ sourceClass, new QueryCursorPreparer(query, query.getSortObject(), limit, query.
 		return ScrollUtils.createWindow(result, query.getLimit(), OffsetScrollPosition.positionFunction(query.getSkip()));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findById(Object id, Class<T> entityClass) {
+	public <T> @Nullable T findById(Object id, Class<T> entityClass) {
 		return findById(id, entityClass, getCollectionName(entityClass));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findById(Object id, Class<T> entityClass, String collectionName) {
+	public <T> @Nullable T findById(Object id, Class<T> entityClass, String collectionName) {
 
 		Assert.notNull(id, "Id must not be null");
 		Assert.notNull(entityClass, "EntityClass must not be null");
@@ -957,7 +972,7 @@ public <T> List<T> findDistinct(Query query, String field, Class<?> entityClass,
 	}
 
 	@Override
-	@SuppressWarnings("unchecked")
+	@SuppressWarnings({ "unchecked", "NullAway" })
 	public <T> List<T> findDistinct(Query query, String field, String collectionName, Class<?> entityClass,
 			Class<T> resultClass) {
 
@@ -1067,28 +1082,26 @@ public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String col
 		return new GeoResults<>(result, avgDistance);
 	}
 
-	@Nullable
 	@Override
-	public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass) {
+	public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass) {
 		return findAndModify(query, update, new FindAndModifyOptions(), entityClass, getCollectionName(entityClass));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName) {
+	public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass,
+			String collectionName) {
 		return findAndModify(query, update, new FindAndModifyOptions(), entityClass, collectionName);
 	}
 
-	@Nullable
 	@Override
-	public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass) {
+	public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
+			Class<T> entityClass) {
 		return findAndModify(query, update, options, entityClass, getCollectionName(entityClass));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass,
-			String collectionName) {
+	public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options,
+			Class<T> entityClass, String collectionName) {
 
 		Assert.notNull(query, "Query must not be null");
 		Assert.notNull(update, "Update must not be null");
@@ -1112,8 +1125,8 @@ public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOp
 	}
 
 	@Override
-	public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
-			String collectionName, Class<T> resultType) {
+	public <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options,
+			Class<S> entityType, String collectionName, Class<T> resultType) {
 
 		Assert.notNull(query, "Query must not be null");
 		Assert.notNull(replacement, "Replacement must not be null");
@@ -1154,15 +1167,13 @@ public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions
 	// Find methods that take a Query to express the query and that return a single object that is also removed from the
 	// collection in the database.
 
-	@Nullable
 	@Override
-	public <T> T findAndRemove(Query query, Class<T> entityClass) {
+	public <T> @Nullable T findAndRemove(Query query, Class<T> entityClass) {
 		return findAndRemove(query, entityClass, getCollectionName(entityClass));
 	}
 
-	@Nullable
 	@Override
-	public <T> T findAndRemove(Query query, Class<T> entityClass, String collectionName) {
+	public <T> @Nullable T findAndRemove(Query query, Class<T> entityClass, String collectionName) {
 
 		Assert.notNull(query, "Query must not be null");
 		Assert.notNull(entityClass, "EntityClass must not be null");
@@ -1224,6 +1235,7 @@ public long estimatedCount(String collectionName) {
 		return doEstimatedCount(CollectionPreparerDelegate.of(this), collectionName, new EstimatedDocumentCountOptions());
 	}
 
+	@SuppressWarnings("NullAway")
 	protected long doEstimatedCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
 			String collectionName, EstimatedDocumentCountOptions options) {
 		return execute(collectionName,
@@ -1241,6 +1253,7 @@ public long exactCount(Query query, @Nullable Class<?> entityClass, String colle
 		return doExactCount(createDelegate(query), collectionName, mappedQuery, options);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected long doExactCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
 			Document filter, CountOptions options) {
 		return execute(collectionName, collection -> collectionPreparer.prepare(collection)
@@ -1320,15 +1333,13 @@ protected MongoCollection<Document> prepareCollection(MongoCollection<Document>
 	 * @param mongoAction any MongoAction already configured or null
 	 * @return The prepared WriteConcern or null
 	 */
-	@Nullable
-	protected WriteConcern prepareWriteConcern(MongoAction mongoAction) {
+	protected @Nullable WriteConcern prepareWriteConcern(MongoAction mongoAction) {
 
 		WriteConcern wc = writeConcernResolver.resolve(mongoAction);
 		return potentiallyForceAcknowledgedWrite(wc);
 	}
 
-	@Nullable
-	private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) {
+	private @Nullable WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) {
 
 		if (ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking)) {
 			if (wc == null || wc.getWObject() == null
@@ -1541,7 +1552,7 @@ protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> wri
 		return maybeCallAfterSave(saved, dbDoc, collectionName);
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected Object insertDocument(String collectionName, Document document, Class<?> entityClass) {
 
 		if (LOGGER.isDebugEnabled()) {
@@ -1595,6 +1606,7 @@ protected List<Object> insertDocumentList(String collectionName, List<Document>
 		return MappedDocument.toIds(documents);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected Object saveDocument(String collectionName, Document dbDoc, Class<?> entityClass) {
 
 		if (LOGGER.isDebugEnabled()) {
@@ -1693,7 +1705,7 @@ public UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> e
 		return doUpdate(collectionName, query, update, entityClass, false, true);
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefinition update,
 			@Nullable Class<?> entityClass, boolean upsert, boolean multi) {
 
@@ -1701,6 +1713,12 @@ protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefini
 		Assert.notNull(query, "Query must not be null");
 		Assert.notNull(update, "Update must not be null");
 
+		if (query.isSorted() && LOGGER.isWarnEnabled()) {
+
+			LOGGER.warn(String.format("%s does not support sort ('%s'); Please use findAndModify() instead",
+					upsert ? "Upsert" : "UpdateFirst", serializeToJsonSafely(query.getSortObject())));
+		}
+
 		MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass);
 
 		UpdateContext updateContext = multi ? queryOperations.updateContext(update, query, upsert)
@@ -1708,7 +1726,7 @@ protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefini
 		updateContext.increaseVersionForUpdateIfNecessary(entity);
 
 		Document queryObj = updateContext.getMappedQuery(entity);
-		UpdateOptions opts = updateContext.getUpdateOptions(entityClass, query);
+		UpdateOptions opts = updateContext.getUpdateOptions(entityClass);
 
 		if (updateContext.isAggregationUpdate()) {
 
@@ -1803,7 +1821,7 @@ public DeleteResult remove(Query query, Class<?> entityClass, String collectionN
 		return doRemove(collectionName, query, entityClass, true);
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected <T> DeleteResult doRemove(String collectionName, Query query, @Nullable Class<T> entityClass,
 			boolean multi) {
 
@@ -2129,6 +2147,7 @@ protected <S, T> UpdateResult replace(Query query, Class<S> entityType, T replac
 	 * @param entityClass
 	 * @return
 	 */
+	@SuppressWarnings("NullAway")
 	protected <T> List<T> doFindAndDelete(String collectionName, Query query, Class<T> entityClass) {
 
 		List<T> result = find(query, entityClass, collectionName);
@@ -2162,7 +2181,7 @@ private <O> AggregationResults<O> doAggregate(Aggregation aggregation, String co
 		return doAggregate(aggregation, collectionName, outputType, context.getAggregationOperationContext());
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected <O> AggregationResults<O> doAggregate(Aggregation aggregation, String collectionName, Class<O> outputType,
 			AggregationOperationContext context) {
 
@@ -2245,7 +2264,7 @@ protected <O> AggregationResults<O> doAggregate(Aggregation aggregation, String
 		});
 	}
 
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected <O> Stream<O> aggregateStream(Aggregation aggregation, String collectionName, Class<O> outputType,
 			@Nullable AggregationOperationContext context) {
 
@@ -2360,7 +2379,7 @@ protected String replaceWithResourceIfNecessary(String function) {
 	}
 
 	@Override
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	public Set<String> getCollectionNames() {
 		return execute(db -> {
 			Set<String> result = new LinkedHashSet<>();
@@ -2444,7 +2463,7 @@ protected MongoCollection<Document> doCreateCollection(String collectionName, Do
 	 * @return the collection that was created
 	 * @since 3.3.3
 	 */
-	@SuppressWarnings("ConstantConditions")
+	@SuppressWarnings({ "ConstantConditions", "NullAway" })
 	protected MongoCollection<Document> doCreateCollection(String collectionName,
 			CreateCollectionOptions collectionOptions) {
 
@@ -2522,9 +2541,9 @@ private CreateCollectionOptions getCreateCollectionOptions(Document document) {
 	 * @param entityClass the parameterized type of the returned list.
 	 * @return the converted object or {@literal null} if none exists.
 	 */
-	@Nullable
-	protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
-			Document query, Document fields, Class<T> entityClass) {
+	protected <T> @Nullable T doFindOne(String collectionName,
+			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
+			Class<T> entityClass) {
 		return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
 	}
 
@@ -2541,10 +2560,10 @@ protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollect
 	 * @return the converted object or {@literal null} if none exists.
 	 * @since 2.2
 	 */
-	@Nullable
 	@SuppressWarnings("ConstantConditions")
-	protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
-			Document query, Document fields, CursorPreparer preparer, Class<T> entityClass) {
+	protected <T> @Nullable T doFindOne(String collectionName,
+			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
+			CursorPreparer preparer, Class<T> entityClass) {
 
 		MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
 
@@ -2610,7 +2629,7 @@ protected <S, T> List<T> doFind(String collectionName,
 
 		if (LOGGER.isDebugEnabled()) {
 
-			Document mappedSort = preparer instanceof SortingQueryCursorPreparer sqcp ?  getMappedSortObject(sqcp.getSortObject(), entity) : null;
+			Document mappedSort = getMappedSortObject(query, entityClass);
 			LOGGER.debug(String.format("find using query: %s fields: %s sort: %s for class: %s in collection: %s",
 					serializeToJsonSafely(mappedQuery), mappedFields, serializeToJsonSafely(mappedSort), entityClass,
 					collectionName));
@@ -2635,12 +2654,9 @@ <S, T> List<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPr
 		QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields));
 		Document mappedFields = queryContext.getMappedFields(entity, projection);
 		Document mappedQuery = queryContext.getMappedQuery(entity);
+		Document mappedSort = getMappedSortObject(query, sourceClass);
 
 		if (LOGGER.isDebugEnabled()) {
-
-			Document mappedSort = preparer instanceof SortingQueryCursorPreparer sqcp
-					? getMappedSortObject(sqcp.getSortObject(), entity)
-					: null;
 			LOGGER.debug(String.format("find using query: %s fields: %s sort: %s for class: %s in collection: %s",
 					serializeToJsonSafely(mappedQuery), mappedFields, serializeToJsonSafely(mappedSort), sourceClass,
 					collectionName));
@@ -2720,8 +2736,9 @@ Document getMappedValidator(Validator validator, Class<?> domainType) {
 	 * @return the List of converted objects.
 	 */
 	@SuppressWarnings("ConstantConditions")
-	protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, Document query,
-			Document fields, Document sort, @Nullable Collation collation, Class<T> entityClass) {
+	protected <T> @Nullable T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName,
+			Document query, @Nullable Document fields, @Nullable Document sort, @Nullable Collation collation,
+			Class<T> entityClass) {
 
 		if (LOGGER.isDebugEnabled()) {
 			LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s",
@@ -2736,8 +2753,8 @@ protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String co
 	}
 
 	@SuppressWarnings("ConstantConditions")
-	protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, Document query,
-			Document fields, Document sort, Class<T> entityClass, UpdateDefinition update,
+	protected <T> @Nullable T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName,
+			Document query, @Nullable Document fields, @Nullable Document sort, Class<T> entityClass, UpdateDefinition update,
 			@Nullable FindAndModifyOptions options) {
 
 		if (options == null) {
@@ -2781,10 +2798,10 @@ protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String co
 	 * @return {@literal null} if object does not exist, {@link FindAndReplaceOptions#isReturnNew() return new} is
 	 *         {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
 	 */
-	@Nullable
-	protected <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
-			Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
-			Class<?> entityType, Document replacement, FindAndReplaceOptions options, Class<T> resultType) {
+	protected <T> @Nullable T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName,
+			Document mappedQuery, Document mappedFields, Document mappedSort,
+			com.mongodb.client.model.@Nullable Collation collation, Class<?> entityType, Document replacement,
+			FindAndReplaceOptions options, Class<T> resultType) {
 
 		EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
 
@@ -2823,10 +2840,10 @@ CollectionPreparer<MongoCollection<Document>> createCollectionPreparer(Query que
 	 *         {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
 	 * @since 3.4
 	 */
-	@Nullable
-	private <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
-			Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
-			Class<?> entityType, Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
+	private <T> @Nullable T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName,
+			Document mappedQuery, Document mappedFields, Document mappedSort,
+			com.mongodb.client.model.@Nullable Collation collation, Class<?> entityType, Document replacement,
+			FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
 
 		if (LOGGER.isDebugEnabled()) {
 			LOGGER
@@ -2842,6 +2859,7 @@ private <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String col
 				collectionName);
 	}
 
+	@SuppressWarnings("NullAway")
 	private <S> UpdateResult doReplace(ReplaceOptions options, Class<S> entityType, String collectionName,
 			UpdateContext updateContext, CollectionPreparer<MongoCollection<Document>> collectionPreparer,
 			Document replacement) {
@@ -2896,8 +2914,7 @@ private MongoCollection<Document> getAndPrepareCollection(MongoDatabase db, Stri
 	 * @param collectionName the collection to be queried
 	 * @return
 	 */
-	@Nullable
-	private <T> T executeFindOneInternal(CollectionCallback<Document> collectionCallback,
+	private <T> @Nullable T executeFindOneInternal(CollectionCallback<Document> collectionCallback,
 			DocumentCallback<T> documentCallback, String collectionName) {
 
 		try {
@@ -2992,8 +3009,7 @@ private static MongoConverter getDefaultMongoConverter(MongoDatabaseFactory fact
 		return converter;
 	}
 
-	@Nullable
-	private Document getMappedSortObject(@Nullable Query query, Class<?> type) {
+	private @Nullable Document getMappedSortObject(@Nullable Query query, Class<?> type) {
 
 		if (query == null) {
 			return null;
@@ -3002,19 +3018,13 @@ private Document getMappedSortObject(@Nullable Query query, Class<?> type) {
 		return getMappedSortObject(query.getSortObject(), type);
 	}
 
-	@Nullable
-	private Document getMappedSortObject(Document sortObject, Class<?> type) {
-		return getMappedSortObject(sortObject, mappingContext.getPersistentEntity(type));
-	}
-
-	@Nullable
-	private Document getMappedSortObject(Document sortObject, @Nullable MongoPersistentEntity<?> entity) {
+	private @Nullable Document getMappedSortObject(Document sortObject, Class<?> type) {
 
 		if (ObjectUtils.isEmpty(sortObject)) {
 			return null;
 		}
 
-		return queryMapper.getMappedSort(sortObject, entity);
+		return queryMapper.getMappedSort(sortObject, mappingContext.getPersistentEntity(type));
 	}
 
 	/**
@@ -3084,10 +3094,10 @@ private static class FindCallback implements CollectionCallback<FindIterable<Doc
 		private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
 		private final Document query;
 		private final Document fields;
-		private final @Nullable com.mongodb.client.model.Collation collation;
+		private final com.mongodb.client.model.@Nullable Collation collation;
 
 		public FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, @Nullable com.mongodb.client.model.Collation collation) {
+				Document fields, com.mongodb.client.model.@Nullable Collation collation) {
 
 			Assert.notNull(query, "Query must not be null");
 			Assert.notNull(fields, "Fields must not be null");
@@ -3123,10 +3133,10 @@ private class ExistsCallback implements CollectionCallback<Boolean> {
 
 		private final CollectionPreparer collectionPreparer;
 		private final Document mappedQuery;
-		private final com.mongodb.client.model.Collation collation;
+		private final com.mongodb.client.model.@Nullable Collation collation;
 
 		ExistsCallback(CollectionPreparer collectionPreparer, Document mappedQuery,
-				com.mongodb.client.model.Collation collation) {
+				com.mongodb.client.model.@Nullable Collation collation) {
 
 			this.collectionPreparer = collectionPreparer;
 			this.mappedQuery = mappedQuery;
@@ -3151,12 +3161,12 @@ private static class FindAndRemoveCallback implements CollectionCallback<Documen
 
 		private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
 		private final Document query;
-		private final Document fields;
-		private final Document sort;
+		private final @Nullable Document fields;
+		private final @Nullable Document sort;
 		private final Optional<Collation> collation;
 
 		FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, @Nullable Collation collation) {
+				@Nullable Document fields, @Nullable Document sort, @Nullable Collation collation) {
 			this.collectionPreparer = collectionPreparer;
 
 			this.query = query;
@@ -3179,14 +3189,15 @@ private static class FindAndModifyCallback implements CollectionCallback<Documen
 
 		private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
 		private final Document query;
-		private final Document fields;
-		private final Document sort;
+		private final @Nullable Document fields;
+		private final @Nullable Document sort;
 		private final Object update;
 		private final List<Document> arrayFilters;
 		private final FindAndModifyOptions options;
 
 		FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
+				@Nullable Document fields, @Nullable Document sort, Object update, List<Document> arrayFilters,
+				FindAndModifyOptions options) {
 
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
@@ -3240,11 +3251,11 @@ private static class FindAndReplaceCallback implements CollectionCallback<Docume
 		private final Document fields;
 		private final Document sort;
 		private final Document update;
-		private final @Nullable com.mongodb.client.model.Collation collation;
+		private final com.mongodb.client.model.@Nullable Collation collation;
 		private final FindAndReplaceOptions options;
 
 		FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, Document update, @Nullable com.mongodb.client.model.Collation collation,
+				Document fields, Document sort, Document update, com.mongodb.client.model.@Nullable Collation collation,
 				FindAndReplaceOptions options) {
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
@@ -3350,10 +3361,6 @@ private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
 		@SuppressWarnings("unchecked")
 		public T doWith(Document document) {
 
-			if (document == null) {
-				return null;
-			}
-
 			maybeEmitEvent(new AfterLoadEvent<>(document, projection.getMappedType().getType(), collectionName));
 
 			Object entity = mongoConverter.project(projection, document);
@@ -3367,11 +3374,14 @@ public T doWith(Document document) {
 		}
 	}
 
-	class QueryCursorPreparer implements SortingQueryCursorPreparer {
+	class QueryCursorPreparer implements CursorPreparer {
 
 		private final Query query;
+
 		private final Document sortObject;
+
 		private final int limit;
+
 		private final long skip;
 		private final @Nullable Class<?> type;
 
@@ -3462,11 +3472,6 @@ public FindIterable<Document> prepare(FindIterable<Document> iterable) {
 			return cursorToUse;
 		}
 
-		@Nullable
-		@Override
-		public Document getSortObject() {
-			return sortObject;
-		}
 	}
 
 	/**
@@ -3568,9 +3573,8 @@ public boolean hasNext() {
 			}
 		}
 
-		@Nullable
 		@Override
-		public T next() {
+		public @Nullable T next() {
 
 			if (cursor == null) {
 				return null;
@@ -3598,8 +3602,6 @@ public void close() {
 				throw potentiallyConvertRuntimeException(ex, exceptionTranslator);
 			} finally {
 				cursor = null;
-				exceptionTranslator = null;
-				objectReadCallback = null;
 			}
 		}
 	}
@@ -3631,7 +3633,7 @@ static class SessionBoundMongoTemplate extends MongoTemplate {
 		}
 
 		@Override
-		public MongoCollection<Document> getCollection(String collectionName) {
+		public MongoCollection<Document> getCollection(@Nullable String collectionName) {
 
 			// native MongoDB objects that offer methods with ClientSession must not be proxied.
 			return delegate.getCollection(collectionName);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
index 28ca85fbd7..4ae618eaa1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
@@ -31,6 +31,7 @@
 import org.bson.codecs.Codec;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PropertyPath;
 import org.springframework.data.mapping.PropertyReferenceException;
 import org.springframework.data.mapping.context.MappingContext;
@@ -62,7 +63,7 @@
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.client.model.CountOptions;
@@ -283,6 +284,7 @@ <T> MappedDocument prepareId(Class<T> type) {
 		 * @param <T>
 		 * @return the {@link MappedDocument} containing the changes.
 		 */
+		@SuppressWarnings("NullAway")
 		<T> MappedDocument prepareId(@Nullable MongoPersistentEntity<T> entity) {
 
 			if (entity == null || source.hasId()) {
@@ -361,6 +363,7 @@ <T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> entity) {
 			return queryMapper.getMappedObject(getQueryObject(), entity);
 		}
 
+		@SuppressWarnings("NullAway")
 		Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, EntityProjection<?, ?> projection) {
 
 			Document fields = evaluateFields(entity);
@@ -888,6 +891,8 @@ Document getMappedShardKey(MongoPersistentEntity<?> entity) {
 		 */
 		List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
 
+			Assert.isInstanceOf(AggregationUpdate.class, update);
+
 			Class<?> type = domainType != null ? domainType : Object.class;
 
 			AggregationOperationContext context = new RelaxedTypeBasedAggregationOperationContext(type, mappingContext,
@@ -901,6 +906,7 @@ List<Document> getUpdatePipeline(@Nullable Class<?> domainType) {
 		 * @param entity
 		 * @return
 		 */
+		@SuppressWarnings("NullAway")
 		Document getMappedUpdate(@Nullable MongoPersistentEntity<?> entity) {
 
 			if (update != null) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java
index 954fd61716..978aa9634f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Flux;
 
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
@@ -59,11 +60,11 @@ static class ReactiveAggregationSupport<T>
 
 		private final ReactiveMongoTemplate template;
 		private final Class<T> domainType;
-		private final Aggregation aggregation;
-		private final String collection;
+		private final @Nullable Aggregation aggregation;
+		private final @Nullable String collection;
 
-		ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, Aggregation aggregation,
-				String collection) {
+		ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, @Nullable Aggregation aggregation,
+				@Nullable String collection) {
 
 			this.template = template;
 			this.domainType = domainType;
@@ -89,6 +90,9 @@ public TerminatingAggregationOperation<T> by(Aggregation aggregation) {
 
 		@Override
 		public Flux<T> all() {
+
+			Assert.notNull(aggregation, "Aggregation must be set first");
+
 			return template.aggregate(aggregation, getCollectionName(aggregation), domainType);
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java
index afeb6c5e0e..589f264f17 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java
@@ -24,11 +24,11 @@
 import org.bson.BsonTimestamp;
 import org.bson.BsonValue;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.aggregation.MatchOperation;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java
index d1aec8af36..9445dbdadb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java
@@ -19,6 +19,7 @@
 import reactor.core.publisher.Mono;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.data.domain.Window;
 import org.springframework.data.domain.ScrollPosition;
@@ -26,7 +27,6 @@
 import org.springframework.data.mongodb.core.query.NearQuery;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -67,10 +67,10 @@ static class ReactiveFindSupport<T>
 		private final ReactiveMongoTemplate template;
 		private final Class<?> domainType;
 		private final Class<T> returnType;
-		private final String collection;
+		private final @Nullable String collection;
 		private final Query query;
 
-		ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType, String collection,
+		ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType, @Nullable String collection,
 				Query query) {
 
 			this.template = template;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java
index 06d3c6eae7..9d424c2446 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -50,9 +51,9 @@ static class ReactiveInsertSupport<T> implements ReactiveInsert<T> {
 
 		private final ReactiveMongoTemplate template;
 		private final Class<T> domainType;
-		private final String collection;
+		private final @Nullable String collection;
 
-		ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> domainType, String collection) {
+		ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> domainType, @Nullable String collection) {
 
 			this.template = template;
 			this.domainType = domainType;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java
index 4f0d395950..4e3379bad0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java
@@ -17,9 +17,9 @@
 
 import reactor.core.publisher.Flux;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -89,8 +89,11 @@ static class ReactiveMapReduceSupport<T>
 		@Override
 		public Flux<T> all() {
 
+			Assert.notNull(mapFunction, "MapFunction must be set first");
+			Assert.notNull(reduceFunction, "ReduceFunction must be set first");
+
 			return template.mapReduce(query, domainType, getCollectionName(), returnType, mapFunction, reduceFunction,
-					options);
+					options != null ? options : MapReduceOptions.options());
 		}
 
 		/*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java
index 89d1cd78ac..89caf3273c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java
@@ -16,10 +16,10 @@
 
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.config.AbstractFactoryBean;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.support.PersistenceExceptionTranslator;
-import org.springframework.lang.Nullable;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.MongoClientSettings;
@@ -89,7 +89,7 @@ public void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exce
 	}
 
 	@Override
-	public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+	public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) {
 		return exceptionTranslator.translateExceptionIfPossible(ex);
 	}
 
@@ -124,7 +124,9 @@ protected MongoClient createInstance() throws Exception {
 
 	@Override
 	protected void destroyInstance(@Nullable MongoClient instance) throws Exception {
-		instance.close();
+		if (instance != null) {
+			instance.close();
+		}
 	}
 
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java
index 90f2d2345d..ebec41e3aa 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java
@@ -23,6 +23,7 @@
 import java.util.function.Supplier;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.reactivestreams.Subscription;
 import org.springframework.data.domain.KeysetScrollPosition;
@@ -48,7 +49,6 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
index ea427a3e1f..c203e33a55 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
+import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -44,9 +44,9 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.reactivestreams.Subscriber;
-
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -109,7 +109,18 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
-import org.springframework.data.mongodb.core.mapping.event.*;
+import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent;
+import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent;
+import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent;
+import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
+import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback;
+import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback;
+import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback;
+import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback;
 import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
 import org.springframework.data.mongodb.core.query.BasicQuery;
 import org.springframework.data.mongodb.core.query.Collation;
@@ -121,7 +132,7 @@
 import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.util.Optionals;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.CollectionUtils;
@@ -345,7 +356,8 @@ public void setWriteConcern(@Nullable WriteConcern writeConcern) {
 	 * @param writeConcernResolver can be {@literal null}.
 	 */
 	public void setWriteConcernResolver(@Nullable WriteConcernResolver writeConcernResolver) {
-		this.writeConcernResolver = writeConcernResolver;
+		this.writeConcernResolver = writeConcernResolver != null ? writeConcernResolver
+				: DefaultWriteConcernResolver.INSTANCE;
 	}
 
 	/**
@@ -739,10 +751,11 @@ public <T> Mono<Boolean> collectionExists(Class<T> entityClass) {
 
 	@Override
 	public Mono<Boolean> collectionExists(String collectionName) {
-		return createMono(db -> Flux.from(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames()) //
-				.filter(s -> s.equals(collectionName)) //
-				.map(s -> true) //
-				.single(false));
+		return createMono(
+				db -> Flux.from(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames()) //
+						.filter(s -> s.equals(collectionName)) //
+						.map(s -> true) //
+						.single(false));
 	}
 
 	@Override
@@ -899,7 +912,7 @@ <T> Mono<Window<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetC
 			Mono<List<T>> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query),
 					keysetPaginationQuery.query(), keysetPaginationQuery.fields(), sourceClass,
 					new QueryFindPublisherPreparer(query, keysetPaginationQuery.sort(), limit, 0, sourceClass), callback)
-							.collectList();
+					.collectList();
 
 			return result.map(it -> ScrollUtils.createWindow(query, it, sourceClass, operations));
 		}
@@ -907,7 +920,7 @@ <T> Mono<Window<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetC
 		Mono<List<T>> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(),
 				query.getFieldsObject(), sourceClass,
 				new QueryFindPublisherPreparer(query, query.getSortObject(), limit, query.getSkip(), sourceClass), callback)
-						.collectList();
+				.collectList();
 
 		return result.map(
 				it -> ScrollUtils.createWindow(it, query.getLimit(), OffsetScrollPosition.positionFunction(query.getSkip())));
@@ -1146,6 +1159,7 @@ public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndMo
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType,
 			String collectionName, Class<T> resultType) {
 
@@ -1351,6 +1365,7 @@ public <T> Mono<T> insert(T objectToSave, String collectionName) {
 		return doInsert(collectionName, objectToSave, this.mongoConverter);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected <T> Mono<T> doInsert(String collectionName, T objectToSave, MongoWriter<Object> writer) {
 
 		return Mono.just(PersistableEntityModel.of(objectToSave, collectionName)) //
@@ -1401,6 +1416,7 @@ public <T> Flux<T> insertAll(Mono<? extends Collection<? extends T>> objectsToSa
 		return Flux.from(objectsToSave).flatMapSequential(this::insertAll);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected <T> Flux<T> doInsertAll(Collection<? extends T> listToSave, MongoWriter<Object> writer) {
 
 		Map<String, List<T>> elementsByCollection = new HashMap<>();
@@ -1417,6 +1433,7 @@ protected <T> Flux<T> doInsertAll(Collection<? extends T> listToSave, MongoWrite
 				.concatMap(collectionName -> doInsertBatch(collectionName, elementsByCollection.get(collectionName), writer));
 	}
 
+	@SuppressWarnings("NullAway")
 	protected <T> Flux<T> doInsertBatch(String collectionName, Collection<? extends T> batchToSave,
 			MongoWriter<Object> writer) {
 
@@ -1532,6 +1549,7 @@ private <T> Mono<T> doSaveVersioned(AdaptibleEntity<T> source, String collection
 		});
 	}
 
+	@SuppressWarnings("NullAway")
 	protected <T> Mono<T> doSave(String collectionName, T objectToSave, MongoWriter<Object> writer) {
 
 		assertUpdateableIdIfNotSet(objectToSave);
@@ -1625,6 +1643,7 @@ private MongoCollection<Document> prepareCollection(MongoCollection<Document> co
 		return collectionToUse;
 	}
 
+	@SuppressWarnings("NullAway")
 	protected Mono<Object> saveDocument(String collectionName, Document document, Class<?> entityClass) {
 
 		if (LOGGER.isDebugEnabled()) {
@@ -1728,7 +1747,8 @@ public Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Clas
 		return doUpdate(collectionName, query, update, entityClass, false, true);
 	}
 
-	protected Mono<UpdateResult> doUpdate(String collectionName, Query query, @Nullable UpdateDefinition update,
+	@SuppressWarnings("NullAway")
+	protected Mono<UpdateResult> doUpdate(String collectionName, Query query, UpdateDefinition update,
 			@Nullable Class<?> entityClass, boolean upsert, boolean multi) {
 
 		MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass);
@@ -1810,7 +1830,8 @@ protected Mono<UpdateResult> doUpdate(String collectionName, Query query, @Nulla
 
 					Document updateObj = updateContext.getMappedUpdate(entity);
 					if (containsVersionProperty(queryObj, entity))
-						throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity %s to collection %s".formatted(entity.getName(),  collectionName));
+						throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity %s to collection %s"
+								.formatted(entity.getName(), collectionName));
 				}
 			}
 		});
@@ -2008,18 +2029,18 @@ public <T> Flux<T> tail(Query query, Class<T> entityClass) {
 	@Override
 	public <T> Flux<T> tail(@Nullable Query query, Class<T> entityClass, String collectionName) {
 
-		ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
 		if (query == null) {
 
 			LOGGER.debug(String.format("Tail for class: %s in collection: %s", entityClass, collectionName));
 
 			return executeFindMultiInternal(
-					collection -> new FindCallback(collectionPreparer, null).doInCollection(collection)
+					collection -> new FindCallback(CollectionPreparer.identity(), null).doInCollection(collection)
 							.cursorType(CursorType.TailableAwait),
 					FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
 					collectionName);
 		}
 
+		ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
 		return doFind(collectionName, collectionPreparer, query.getQueryObject(), query.getFieldsObject(), entityClass,
 				new TailingQueryFindPublisherPreparer(query, entityClass));
 	}
@@ -2382,8 +2403,8 @@ protected <S, T> Flux<T> doFind(String collectionName,
 					serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
 		}
 
-		return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer,
-				objectCallback, collectionName);
+		return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields),
+				preparer != null ? preparer : FindPublisherPreparer.NO_OP_PREPARER, objectCallback, collectionName);
 	}
 
 	CollectionPreparer<MongoCollection<Document>> createCollectionPreparer(Query query) {
@@ -2448,8 +2469,8 @@ protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable Col
 	 * @return the List of converted objects.
 	 */
 	protected <T> Mono<T> doFindAndRemove(String collectionName,
-			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort,
-			@Nullable Collation collation, Class<T> entityClass) {
+			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
+			@Nullable Document sort, @Nullable Collation collation, Class<T> entityClass) {
 
 		if (LOGGER.isDebugEnabled()) {
 			LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s",
@@ -2464,8 +2485,8 @@ protected <T> Mono<T> doFindAndRemove(String collectionName,
 	}
 
 	protected <T> Mono<T> doFindAndModify(String collectionName,
-			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort,
-			Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
+			CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
+			@Nullable Document sort, Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
 
 		MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
 		UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false);
@@ -2481,8 +2502,7 @@ protected <T> Mono<T> doFindAndModify(String collectionName,
 				LOGGER.debug(String.format(
 						"findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " + "in collection: %s",
 						serializeToJsonSafely(mappedQuery), fields, serializeToJsonSafely(sort), entityClass,
-						serializeToJsonSafely(mappedUpdate),
-						collectionName));
+						serializeToJsonSafely(mappedUpdate), collectionName));
 			}
 
 			return executeFindOneInternal(
@@ -2659,8 +2679,7 @@ protected MongoDatabase prepareDatabase(MongoDatabase database) {
 	 * @see #setWriteConcern(WriteConcern)
 	 * @see #setWriteConcernResolver(WriteConcernResolver)
 	 */
-	@Nullable
-	protected WriteConcern prepareWriteConcern(MongoAction mongoAction) {
+	protected @Nullable WriteConcern prepareWriteConcern(MongoAction mongoAction) {
 
 		WriteConcern wc = writeConcernResolver.resolve(mongoAction);
 		return potentiallyForceAcknowledgedWrite(wc);
@@ -2679,7 +2698,7 @@ private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc
 
 		if (ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking)) {
 			if (wc == null || wc.getWObject() == null
-					|| (wc.getWObject()instanceof Number concern && concern.intValue() < 1)) {
+					|| (wc.getWObject() instanceof Number concern && concern.intValue() < 1)) {
 				return WriteConcern.ACKNOWLEDGED;
 			}
 		}
@@ -2725,7 +2744,7 @@ private <T> Mono<T> executeFindOneInternal(ReactiveCollectionCallback<Document>
 	 * @return
 	 */
 	private <T> Flux<T> executeFindMultiInternal(ReactiveCollectionQueryCallback<Document> collectionCallback,
-			@Nullable FindPublisherPreparer preparer, DocumentCallback<T> objectCallback, String collectionName) {
+			FindPublisherPreparer preparer, DocumentCallback<T> objectCallback, String collectionName) {
 
 		return createFlux(collectionName, collection -> {
 			return Flux.from(preparer.initiateFind(collection, collectionCallback::doInCollection))
@@ -2764,8 +2783,7 @@ private static RuntimeException potentiallyConvertRuntimeException(RuntimeExcept
 		return resolved == null ? ex : resolved;
 	}
 
-	@Nullable
-	private MongoPersistentEntity<?> getPersistentEntity(@Nullable Class<?> type) {
+	private @Nullable MongoPersistentEntity<?> getPersistentEntity(@Nullable Class<?> type) {
 		return type == null ? null : mappingContext.getPersistentEntity(type);
 	}
 
@@ -2785,8 +2803,8 @@ private MappingMongoConverter getDefaultMongoConverter() {
 		return converter;
 	}
 
-	@Nullable
-	private Document getMappedSortObject(Query query, Class<?> type) {
+	@Contract("null, _ -> null")
+	private @Nullable Document getMappedSortObject(@Nullable Query query, Class<?> type) {
 
 		if (query == null) {
 			return null;
@@ -2795,8 +2813,8 @@ private Document getMappedSortObject(Query query, Class<?> type) {
 		return getMappedSortObject(query.getSortObject(), type);
 	}
 
-	@Nullable
-	private Document getMappedSortObject(Document sortObject, Class<?> type) {
+	@Contract("null, _ -> null")
+	private @Nullable Document getMappedSortObject(@Nullable Document sortObject, Class<?> type) {
 
 		if (ObjectUtils.isEmpty(sortObject)) {
 			return null;
@@ -2862,7 +2880,8 @@ private static class FindCallback implements ReactiveCollectionQueryCallback<Doc
 			this(collectionPreparer, query, null);
 		}
 
-		FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields) {
+		FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, @Nullable Document query,
+				@Nullable Document fields) {
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
 			this.fields = fields;
@@ -2898,11 +2917,11 @@ private static class FindAndRemoveCallback implements ReactiveCollectionCallback
 		private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
 		private final Document query;
 		private final Document fields;
-		private final Document sort;
+		private final @Nullable Document sort;
 		private final Optional<Collation> collation;
 
 		FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, @Nullable Collation collation) {
+				Document fields, @Nullable Document sort, @Nullable Collation collation) {
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
 			this.fields = fields;
@@ -2928,14 +2947,15 @@ private static class FindAndModifyCallback implements ReactiveCollectionCallback
 
 		private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
 		private final Document query;
-		private final Document fields;
-		private final Document sort;
+		private final @Nullable Document fields;
+		private final @Nullable Document sort;
 		private final Object update;
 		private final List<Document> arrayFilters;
 		private final FindAndModifyOptions options;
 
 		FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
+				@Nullable Document fields, @Nullable Document sort, Object update, List<Document> arrayFilters,
+				FindAndModifyOptions options) {
 
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
@@ -2973,7 +2993,7 @@ public Publisher<Document> doInCollection(MongoCollection<Document> collection)
 		}
 
 		private static FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options,
-				Document fields, Document sort, List<Document> arrayFilters) {
+				@Nullable Document fields, @Nullable Document sort, List<Document> arrayFilters) {
 
 			FindOneAndUpdateOptions result = new FindOneAndUpdateOptions();
 
@@ -3009,11 +3029,11 @@ private static class FindAndReplaceCallback implements ReactiveCollectionCallbac
 		private final Document fields;
 		private final Document sort;
 		private final Document update;
-		private final @Nullable com.mongodb.client.model.Collation collation;
+		private final com.mongodb.client.model.@Nullable Collation collation;
 		private final FindAndReplaceOptions options;
 
 		FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
-				Document fields, Document sort, Document update, com.mongodb.client.model.Collation collation,
+				Document fields, Document sort, Document update, com.mongodb.client.model.@Nullable Collation collation,
 				FindAndReplaceOptions options) {
 			this.collectionPreparer = collectionPreparer;
 			this.query = query;
@@ -3049,7 +3069,8 @@ private FindOneAndReplaceOptions convertToFindOneAndReplaceOptions(FindAndReplac
 		}
 	}
 
-	private static FindOneAndDeleteOptions convertToFindOneAndDeleteOptions(Document fields, Document sort) {
+	private static FindOneAndDeleteOptions convertToFindOneAndDeleteOptions(@Nullable Document fields,
+			@Nullable Document sort) {
 
 		FindOneAndDeleteOptions result = new FindOneAndDeleteOptions();
 		result = result.projection(fields).sort(sort);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java
index 97c9cb0d0e..5c935ec628 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -54,9 +55,9 @@ static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCo
 		private final ReactiveMongoTemplate template;
 		private final Class<T> domainType;
 		private final Query query;
-		private final String collection;
+		private final @Nullable String collection;
 
-		ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, String collection) {
+		ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, @Nullable String collection) {
 
 			this.template = template;
 			this.domainType = domainType;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java
index 51cd99dc93..75bfeef314 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java
@@ -17,9 +17,9 @@
 
 import reactor.core.publisher.Mono;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -57,16 +57,16 @@ static class ReactiveUpdateSupport<T>
 		private final ReactiveMongoTemplate template;
 		private final Class<?> domainType;
 		private final Query query;
-		private final org.springframework.data.mongodb.core.query.UpdateDefinition update;
-		@Nullable private final String collection;
-		@Nullable private final FindAndModifyOptions findAndModifyOptions;
-		@Nullable private final FindAndReplaceOptions findAndReplaceOptions;
-		@Nullable private final Object replacement;
+		private final org.springframework.data.mongodb.core.query.@Nullable UpdateDefinition update;
+		private final @Nullable String collection;
+		private final @Nullable FindAndModifyOptions findAndModifyOptions;
+		private final @Nullable FindAndReplaceOptions findAndReplaceOptions;
+		private final @Nullable Object replacement;
 		private final Class<T> targetType;
 
-		ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, UpdateDefinition update,
-				String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions,
-				Object replacement, Class<T> targetType) {
+		ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, @Nullable UpdateDefinition update,
+			@Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, @Nullable FindAndReplaceOptions findAndReplaceOptions,
+			@Nullable Object replacement, Class<T> targetType) {
 
 			this.template = template;
 			this.domainType = domainType;
@@ -108,6 +108,7 @@ public Mono<UpdateResult> upsert() {
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Mono<T> findAndModify() {
 
 			String collectionName = getCollectionName();
@@ -118,7 +119,11 @@ public Mono<T> findAndModify() {
 		}
 
 		@Override
+		@SuppressWarnings({"unchecked","rawtypes"})
 		public Mono<T> findAndReplace() {
+
+			Assert.notNull(replacement, "Replacement must be set first");
+
 			return template.findAndReplace(query, replacement,
 					findAndReplaceOptions != null ? findAndReplaceOptions : FindAndReplaceOptions.none(), (Class) domainType,
 					getCollectionName(), targetType);
@@ -186,6 +191,7 @@ public <R> FindAndReplaceWithOptions<R> as(Class<R> resultType) {
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Mono <UpdateResult> replaceFirst() {
 
 			if (replacement != null) {
@@ -197,6 +203,7 @@ public Mono <UpdateResult> replaceFirst() {
 					findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName());
 		}
 
+		@SuppressWarnings("NullAway")
 		private Mono<UpdateResult> doUpdate(boolean multi, boolean upsert) {
 			return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java
index 00c5815fc9..7a7e5fdfb2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.ReadConcern;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java
index 74bca9abea..e6f3fc0daf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.ReadPreference;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java
index a2e2ba24c0..a487cde669 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java
@@ -16,6 +16,7 @@
 package org.springframework.data.mongodb.core;
 
 import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.lang.Contract;
 
 /**
  * Options for {@link org.springframework.data.mongodb.core.MongoOperations#replace(Query, Object) replace operations}. Defaults to
@@ -69,6 +70,7 @@ public static ReplaceOptions none() {
 	 *
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public ReplaceOptions upsert() {
 
 		this.upsert = true;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java
index a01760368a..2ec71b415a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java
@@ -17,9 +17,9 @@
 
 import java.util.Set;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
 import org.springframework.data.mongodb.core.script.NamedMongoScript;
-import org.springframework.lang.Nullable;
 
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java
index 85ddce7656..62e6d6c513 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java
@@ -29,6 +29,7 @@
 import org.springframework.data.domain.Window;
 import org.springframework.data.mongodb.core.EntityOperations.Entity;
 import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.util.Assert;
 
 /**
  * Utilities to run scroll queries and create {@link Window} results.
@@ -48,7 +49,11 @@ class ScrollUtils {
 	 */
 	static KeysetScrollQuery createKeysetPaginationQuery(Query query, String idPropertyName) {
 
+
 		KeysetScrollPosition keyset = query.getKeyset();
+
+		Assert.notNull(keyset, "Query.keyset must not be null");
+
 		KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection());
 		Document sortObject = director.getSortObject(idPropertyName, query);
 		Document fieldsObject = director.getFieldsObject(query.getFieldsObject(), sortObject);
@@ -61,6 +66,9 @@ static <T> Window<T> createWindow(Query query, List<T> result, Class<?> sourceTy
 
 		Document sortObject = query.getSortObject();
 		KeysetScrollPosition keyset = query.getKeyset();
+
+		Assert.notNull(keyset, "Query.keyset must not be null");
+
 		Direction direction = keyset.getDirection();
 		KeysetScrollDirector director = KeysetScrollDirector.of(direction);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java
index 55a87ecadf..76a6d525f8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
 
 /**
  * Callback interface for executing operations within a {@link com.mongodb.session.ClientSession}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java
index 33ad9d7318..906d682685 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java
@@ -17,10 +17,10 @@
 
 import java.util.function.Consumer;
 
-import org.springframework.lang.Nullable;
-
 import com.mongodb.client.ClientSession;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * Gateway interface to execute {@link ClientSession} bound operations against MongoDB via a {@link SessionCallback}.
  * <br />
@@ -42,8 +42,7 @@ public interface SessionScoped {
 	 * @param <T> return type.
 	 * @return a result object returned by the action. Can be {@literal null}.
 	 */
-	@Nullable
-	default <T> T execute(SessionCallback<T> action) {
+	default <T> @Nullable T execute(SessionCallback<T> action) {
 		return execute(action, session -> {});
 	}
 
@@ -60,6 +59,5 @@ default <T> T execute(SessionCallback<T> action) {
 	 * @param <T> return type.
 	 * @return a result object returned by the action. Can be {@literal null}.
 	 */
-	@Nullable
-	<T> T execute(SessionCallback<T> action, Consumer<ClientSession> doFinally);
+	<T> @Nullable T execute(SessionCallback<T> action, Consumer<ClientSession> doFinally);
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java
index 84edf13d57..529f912e6c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java
@@ -18,13 +18,13 @@
 import reactor.core.publisher.Mono;
 
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.aop.framework.ProxyFactory;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.support.PersistenceExceptionTranslator;
 import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
 import org.springframework.data.mongodb.SessionAwareMethodInterceptor;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java
index e50e1088cb..b4b525fc97 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java
@@ -17,8 +17,9 @@
 
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * Immutable object holding additional options to be applied when creating a MongoDB
@@ -59,6 +60,7 @@ public Optional<Collation> getCollation() {
 	 * @param collation the {@link Collation} to use for language-specific string comparison.
 	 * @return new instance of {@link ViewOptions}.
 	 */
+	@Contract("_ -> new")
 	public ViewOptions collation(Collation collation) {
 		return new ViewOptions(collation);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java
index d6e4119b20..bdc7de6663 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.WriteConcern;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java
index 8df4171844..a72c656e47 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.WriteConcern;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java
index d4cdece411..710b570ed7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java
@@ -26,6 +26,7 @@
 
 import org.bson.Document;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Order;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
@@ -282,7 +283,7 @@ protected <T> T get(int index) {
 	 * @since 2.1
 	 */
 	@SuppressWarnings("unchecked")
-	protected <T> T get(Object key) {
+	protected <T> @Nullable T get(Object key) {
 
 		Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map");
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java
index cf6485c230..fa44656c99 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java
@@ -22,6 +22,8 @@
 import java.util.Map;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -60,8 +62,8 @@ public static AccumulatorOperatorFactory valueOf(AggregationExpression expressio
 	 */
 	public static class AccumulatorOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link AccumulatorOperatorFactory} for given {@literal fieldReference}.
@@ -93,6 +95,7 @@ public AccumulatorOperatorFactory(AggregationExpression expression) {
 		 *
 		 * @return new instance of {@link Sum}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Sum sum() {
 			return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression);
 		}
@@ -103,6 +106,7 @@ public Sum sum() {
 		 *
 		 * @return new instance of {@link Avg}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Avg avg() {
 			return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression);
 		}
@@ -113,6 +117,7 @@ public Avg avg() {
 		 *
 		 * @return new instance of {@link Max}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Max max() {
 			return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression);
 		}
@@ -134,6 +139,7 @@ public Max max(int numberOfResults) {
 		 *
 		 * @return new instance of {@link Min}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Min min() {
 			return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression);
 		}
@@ -155,6 +161,7 @@ public Min min(int numberOfResults) {
 		 *
 		 * @return new instance of {@link StdDevPop}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StdDevPop stdDevPop() {
 			return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression);
 		}
@@ -165,6 +172,7 @@ public StdDevPop stdDevPop() {
 		 *
 		 * @return new instance of {@link StdDevSamp}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StdDevSamp stdDevSamp() {
 			return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression);
 		}
@@ -193,6 +201,7 @@ public CovariancePop covariancePop(AggregationExpression expression) {
 			return covariancePop().and(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private CovariancePop covariancePop() {
 			return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression);
 		}
@@ -221,6 +230,7 @@ public CovarianceSamp covarianceSamp(AggregationExpression expression) {
 			return covarianceSamp().and(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private CovarianceSamp covarianceSamp() {
 			return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference)
 					: CovarianceSamp.covarianceSampOf(expression);
@@ -233,6 +243,7 @@ private CovarianceSamp covarianceSamp() {
 		 * @return new instance of {@link ExpMovingAvg}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ExpMovingAvgBuilder expMovingAvg() {
 
 			ExpMovingAvg expMovingAvg = usesFieldRef() ? ExpMovingAvg.expMovingAvgOf(fieldReference)
@@ -252,13 +263,14 @@ public ExpMovingAvg alpha(double exponentialDecayValue) {
 		}
 
 		/**
-		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the
-		 * associated numeric value expression.
+		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the associated numeric
+		 * value expression.
 		 *
 		 * @return new instance of {@link Percentile}.
 		 * @param percentages must not be {@literal null}.
 		 * @since 4.2
 		 */
+		@SuppressWarnings("NullAway")
 		public Percentile percentile(Double... percentages) {
 			Percentile percentile = usesFieldRef() ? Percentile.percentileOf(fieldReference)
 					: Percentile.percentileOf(expression);
@@ -271,6 +283,7 @@ public Percentile percentile(Double... percentages) {
 		 * @return new instance of {@link Median}.
 		 * @since 4.2
 		 */
+		@SuppressWarnings("NullAway")
 		public Median median() {
 			return usesFieldRef() ? Median.medianOf(fieldReference) : Median.medianOf(expression);
 		}
@@ -339,6 +352,7 @@ public static Sum sumOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Sum}.
 		 */
+		@Contract("_ -> new")
 		public static Sum sumOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -352,6 +366,7 @@ public static Sum sumOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Sum}.
 		 */
+		@Contract("_ -> new")
 		public Sum and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -365,6 +380,7 @@ public Sum and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Sum}.
 		 */
+		@Contract("_ -> new")
 		public Sum and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -379,6 +395,7 @@ public Sum and(AggregationExpression expression) {
 		 * @return new instance of {@link Sum}.
 		 * @since 2.2
 		 */
+		@Contract("_ -> new")
 		public Sum and(Number value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -386,7 +403,6 @@ public Sum and(Number value) {
 		}
 
 		@Override
-		@SuppressWarnings("unchecked")
 		public Document toDocument(Object value, AggregationOperationContext context) {
 
 			if (value instanceof List<?> list && list.size() == 1) {
@@ -444,6 +460,7 @@ public static Avg avgOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Avg}.
 		 */
+		@Contract("_ -> new")
 		public Avg and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -457,6 +474,7 @@ public Avg and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Avg}.
 		 */
+		@Contract("_ -> new")
 		public Avg and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -464,7 +482,6 @@ public Avg and(AggregationExpression expression) {
 		}
 
 		@Override
-		@SuppressWarnings("unchecked")
 		public Document toDocument(Object value, AggregationOperationContext context) {
 
 			if (value instanceof List<?> list && list.size() == 1) {
@@ -522,6 +539,7 @@ public static Max maxOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Max}.
 		 */
+		@Contract("_ -> new")
 		public Max and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -535,6 +553,7 @@ public Max and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Max}.
 		 */
+		@Contract("_ -> new")
 		public Max and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -548,11 +567,13 @@ public Max and(AggregationExpression expression) {
 		 * @param numberOfResults
 		 * @return new instance of {@link Max}.
 		 */
+		@Contract("_ -> new")
 		public Max limit(int numberOfResults) {
 			return new Max(append("n", numberOfResults));
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Document toDocument(AggregationOperationContext context) {
 			if (get("n") == null) {
 				return toDocument(get("input"), context);
@@ -619,6 +640,7 @@ public static Min minOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Min}.
 		 */
+		@Contract("_ -> new")
 		public Min and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -632,6 +654,7 @@ public Min and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Min}.
 		 */
+		@Contract("_ -> new")
 		public Min and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -645,11 +668,13 @@ public Min and(AggregationExpression expression) {
 		 * @param numberOfResults
 		 * @return new instance of {@link Min}.
 		 */
+		@Contract("_ -> new")
 		public Min limit(int numberOfResults) {
 			return new Min(append("n", numberOfResults));
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Document toDocument(AggregationOperationContext context) {
 
 			if (get("n") == null) {
@@ -659,7 +684,6 @@ public Document toDocument(AggregationOperationContext context) {
 		}
 
 		@Override
-		@SuppressWarnings("unchecked")
 		public Document toDocument(Object value, AggregationOperationContext context) {
 
 			if (value instanceof List<?> list && list.size() == 1) {
@@ -717,6 +741,7 @@ public static StdDevPop stdDevPopOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link StdDevPop}.
 		 */
+		@Contract("_ -> new")
 		public StdDevPop and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -730,6 +755,7 @@ public StdDevPop and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link StdDevPop}.
 		 */
+		@Contract("_ -> new")
 		public StdDevPop and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -737,7 +763,6 @@ public StdDevPop and(AggregationExpression expression) {
 		}
 
 		@Override
-		@SuppressWarnings("unchecked")
 		public Document toDocument(Object value, AggregationOperationContext context) {
 
 			if (value instanceof List<?> list && list.size() == 1) {
@@ -795,6 +820,7 @@ public static StdDevSamp stdDevSampOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link StdDevSamp}.
 		 */
+		@Contract("_ -> new")
 		public StdDevSamp and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -808,6 +834,7 @@ public StdDevSamp and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link StdDevSamp}.
 		 */
+		@Contract("_ -> new")
 		public StdDevSamp and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -815,7 +842,6 @@ public StdDevSamp and(AggregationExpression expression) {
 		}
 
 		@Override
-		@SuppressWarnings("unchecked")
 		public Document toDocument(Object value, AggregationOperationContext context) {
 
 			if (value instanceof List<?> list && list.size() == 1) {
@@ -866,6 +892,7 @@ public static CovariancePop covariancePopOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link CovariancePop}.
 		 */
+		@Contract("_ -> new")
 		public CovariancePop and(String fieldReference) {
 			return new CovariancePop(append(asFields(fieldReference)));
 		}
@@ -876,6 +903,7 @@ public CovariancePop and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link CovariancePop}.
 		 */
+		@Contract("_ -> new")
 		public CovariancePop and(AggregationExpression expression) {
 			return new CovariancePop(append(expression));
 		}
@@ -926,6 +954,7 @@ public static CovarianceSamp covarianceSampOf(AggregationExpression expression)
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link CovarianceSamp}.
 		 */
+		@Contract("_ -> new")
 		public CovarianceSamp and(String fieldReference) {
 			return new CovarianceSamp(append(asFields(fieldReference)));
 		}
@@ -936,6 +965,7 @@ public CovarianceSamp and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link CovarianceSamp}.
 		 */
+		@Contract("_ -> new")
 		public CovarianceSamp and(AggregationExpression expression) {
 			return new CovarianceSamp(append(expression));
 		}
@@ -986,6 +1016,7 @@ public static ExpMovingAvg expMovingAvgOf(AggregationExpression expression) {
 		 * @param numberOfHistoricalDocuments
 		 * @return new instance of {@link ExpMovingAvg}.
 		 */
+		@Contract("_ -> new")
 		public ExpMovingAvg n/*umber of historical documents*/(int numberOfHistoricalDocuments) {
 			return new ExpMovingAvg(append("N", numberOfHistoricalDocuments));
 		}
@@ -997,6 +1028,7 @@ public static ExpMovingAvg expMovingAvgOf(AggregationExpression expression) {
 		 * @param exponentialDecayValue
 		 * @return new instance of {@link ExpMovingAvg}.
 		 */
+		@Contract("_ -> new")
 		public ExpMovingAvg alpha(double exponentialDecayValue) {
 			return new ExpMovingAvg(append("alpha", exponentialDecayValue));
 		}
@@ -1055,6 +1087,7 @@ public static Percentile percentileOf(AggregationExpression expression) {
 		 * @param percentages must not be {@literal null}.
 		 * @return new instance of {@link Percentile}.
 		 */
+		@Contract("_ -> new")
 		public Percentile percentages(Double... percentages) {
 
 			Assert.notEmpty(percentages, "Percentages must not be null or empty");
@@ -1068,6 +1101,7 @@ public Percentile percentages(Double... percentages) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Percentile}.
 		 */
+		@Contract("_ -> new")
 		public Percentile and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1081,6 +1115,7 @@ public Percentile and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Percentile}.
 		 */
+		@Contract("_ -> new")
 		public Percentile and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1142,6 +1177,7 @@ public static Median medianOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Median}.
 		 */
+		@Contract("_ -> new")
 		public Median and(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1155,6 +1191,7 @@ public Median and(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Median}.
 		 */
+		@Contract("_ -> new")
 		public Median and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
index 0dc1588bf8..e76ebb894d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
@@ -19,8 +19,9 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder.ValueAppender;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * Adds new fields to documents. {@code $addFields} outputs documents that contain all existing fields from the input
@@ -82,6 +83,7 @@ public static ValueAppender addField(String field) {
 	 * @param value the value to assign.
 	 * @return new instance of {@link AddFieldsOperation}.
 	 */
+	@Contract("_ -> new")
 	public AddFieldsOperation addField(Object field, Object value) {
 
 		LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap());
@@ -95,6 +97,7 @@ public AddFieldsOperation addField(Object field, Object value) {
 	 *
 	 * @return new instance of {@link AddFieldsOperationBuilder}.
 	 */
+	@Contract("-> new")
 	public AddFieldsOperationBuilder and() {
 		return new AddFieldsOperationBuilder(getValueMap());
 	}
@@ -139,7 +142,7 @@ public ValueAppender addField(String field) {
 			return new ValueAppender() {
 
 				@Override
-				public AddFieldsOperationBuilder withValue(Object value) {
+				public AddFieldsOperationBuilder withValue(@Nullable Object value) {
 
 					valueMap.put(field, value);
 					return AddFieldsOperationBuilder.this;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java
index 00db38329f..e33c565d11 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java
@@ -16,12 +16,12 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AggregationExpressionTransformer.AggregationExpressionTransformationContext;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
 import org.springframework.data.mongodb.core.spel.ExpressionNode;
 import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
 import org.springframework.data.mongodb.core.spel.ExpressionTransformer;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
index a49c7e46d5..5027328461 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
@@ -21,10 +21,10 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeanUtils;
 import org.springframework.data.mongodb.CodecRegistryProvider;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ReflectionUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java
index fd5f7ed979..6437ec981d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java
@@ -19,12 +19,12 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
 import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
-import org.springframework.lang.Nullable;
 
 /**
  * Rendering support for {@link AggregationOperation} into a {@link List} of {@link org.bson.Document}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java
index 327d40b8c7..278da408c6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java
@@ -19,11 +19,12 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ReadConcernAware;
 import org.springframework.data.mongodb.core.ReadPreferenceAware;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadConcern;
@@ -299,7 +300,7 @@ public boolean hasReadConcern() {
 	}
 
 	@Override
-	public ReadConcern getReadConcern() {
+	public @Nullable ReadConcern getReadConcern() {
 		return readConcern.orElse(null);
 	}
 
@@ -309,7 +310,7 @@ public boolean hasReadPreference() {
 	}
 
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 		return readPreference.orElse(null);
 	}
 
@@ -426,7 +427,7 @@ static Document createCursor(int cursorBatchSize) {
 	 */
 	public static class Builder {
 
-		private Boolean allowDiskUse;
+		private @Nullable Boolean allowDiskUse;
 		private boolean explain;
 		private @Nullable Document cursor;
 		private @Nullable Collation collation;
@@ -444,6 +445,7 @@ public static class Builder {
 		 * @param allowDiskUse use {@literal true} to allow disk use during the aggregation.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public Builder allowDiskUse(boolean allowDiskUse) {
 
 			this.allowDiskUse = allowDiskUse;
@@ -456,6 +458,7 @@ public Builder allowDiskUse(boolean allowDiskUse) {
 		 * @param explain use {@literal true} to enable explain feature.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public Builder explain(boolean explain) {
 
 			this.explain = explain;
@@ -468,6 +471,7 @@ public Builder explain(boolean explain) {
 		 * @param cursor must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public Builder cursor(Document cursor) {
 
 			this.cursor = cursor;
@@ -481,6 +485,7 @@ public Builder cursor(Document cursor) {
 		 * @return this.
 		 * @since 2.0
 		 */
+		@Contract("_ -> this")
 		public Builder cursorBatchSize(int batchSize) {
 
 			this.cursor = createCursor(batchSize);
@@ -494,6 +499,7 @@ public Builder cursorBatchSize(int batchSize) {
 		 * @return this.
 		 * @since 2.0
 		 */
+		@Contract("_ -> this")
 		public Builder collation(@Nullable Collation collation) {
 
 			this.collation = collation;
@@ -507,6 +513,7 @@ public Builder collation(@Nullable Collation collation) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public Builder comment(@Nullable String comment) {
 
 			this.comment = comment;
@@ -520,6 +527,7 @@ public Builder comment(@Nullable String comment) {
 		 * @return this.
 		 * @since 3.1
 		 */
+		@Contract("_ -> this")
 		public Builder hint(@Nullable Document hint) {
 
 			this.hint = hint;
@@ -533,6 +541,7 @@ public Builder hint(@Nullable Document hint) {
 		 * @return this.
 		 * @since 4.1
 		 */
+		@Contract("_ -> this")
 		public Builder hint(@Nullable String indexName) {
 
 			this.hint = indexName;
@@ -546,6 +555,7 @@ public Builder hint(@Nullable String indexName) {
 		 * @return this.
 		 * @since 4.1
 		 */
+		@Contract("_ -> this")
 		public Builder readConcern(@Nullable ReadConcern readConcern) {
 
 			this.readConcern = readConcern;
@@ -559,6 +569,7 @@ public Builder readConcern(@Nullable ReadConcern readConcern) {
 		 * @return this.
 		 * @since 4.1
 		 */
+		@Contract("_ -> this")
 		public Builder readPreference(@Nullable ReadPreference readPreference) {
 
 			this.readPreference = readPreference;
@@ -573,6 +584,7 @@ public Builder readPreference(@Nullable ReadPreference readPreference) {
 		 * @return this.
 		 * @since 3.0
 		 */
+		@Contract("_ -> this")
 		public Builder maxTime(@Nullable Duration maxTime) {
 
 			this.maxTime = maxTime;
@@ -587,6 +599,7 @@ public Builder maxTime(@Nullable Duration maxTime) {
 		 * @return this.
 		 * @since 3.0.2
 		 */
+		@Contract("-> this")
 		public Builder skipOutput() {
 
 			this.resultOptions = ResultOptions.SKIP;
@@ -600,6 +613,7 @@ public Builder skipOutput() {
 		 * @return this.
 		 * @since 3.2
 		 */
+		@Contract("-> this")
 		public Builder strictMapping() {
 
 			this.domainTypeMapping = DomainTypeMapping.STRICT;
@@ -613,6 +627,7 @@ public Builder strictMapping() {
 		 * @return this.
 		 * @since 3.2
 		 */
+		@Contract("-> this")
 		public Builder relaxedMapping() {
 
 			this.domainTypeMapping = DomainTypeMapping.RELAXED;
@@ -625,6 +640,7 @@ public Builder relaxedMapping() {
 		 * @return this.
 		 * @since 3.2
 		 */
+		@Contract("-> this")
 		public Builder noMapping() {
 
 			this.domainTypeMapping = DomainTypeMapping.NONE;
@@ -636,6 +652,7 @@ public Builder noMapping() {
 		 *
 		 * @return new instance of {@link AggregationOptions}.
 		 */
+		@Contract("-> new")
 		public AggregationOptions build() {
 
 			AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java
index 68662ec0df..40966bcf3d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java
@@ -22,6 +22,7 @@
 import java.util.function.Predicate;
 
 import org.bson.Document;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -63,6 +64,7 @@ public AggregationPipeline(List<AggregationOperation> aggregationOperations) {
 	 * @param aggregationOperation must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public AggregationPipeline add(AggregationOperation aggregationOperation) {
 
 		Assert.notNull(aggregationOperation, "AggregationOperation must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java
index 438eb9e49f..f5a861cddd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java
@@ -20,7 +20,7 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -71,8 +71,7 @@ public List<T> getMappedResults() {
 	 * @return the single already mapped result object or raise an error if more than one found.
 	 * @throws IllegalArgumentException in case more than one result is available.
 	 */
-	@Nullable
-	public T getUniqueMappedResult() {
+	public @Nullable T getUniqueMappedResult() {
 		Assert.isTrue(mappedResults.size() < 2, "Expected unique result or null, but got more than one");
 		return mappedResults.size() == 1 ? mappedResults.get(0) : null;
 	}
@@ -101,8 +100,7 @@ public Document getRawResults() {
 		return rawResults;
 	}
 
-	@Nullable
-	private String parseServerUsed() {
+	private @Nullable String parseServerUsed() {
 
 		Object object = rawResults.get("serverUsed");
 		return object instanceof String stringValue ? stringValue : null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java
index 1626d672bc..c5b53ef0c6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java
@@ -66,6 +66,8 @@ public static AggregationSpELExpression expressionOf(String expressionString, Ob
 
 	@Override
 	public Document toDocument(AggregationOperationContext context) {
-		return (Document) TRANSFORMER.transform(rawExpression, context, parameters);
+
+		Document doc = (Document) TRANSFORMER.transform(rawExpression, context, parameters);
+		return doc != null ? doc : new Document();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java
index 15d700309e..9e8564c03e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java
@@ -25,11 +25,11 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -129,6 +129,7 @@ public static AggregationUpdate from(List<AggregationOperation> pipeline) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set/">$set Aggregation Reference</a>
 	 */
+	@Contract("_ -> this")
 	public AggregationUpdate set(SetOperation setOperation) {
 
 		Assert.notNull(setOperation, "SetOperation must not be null");
@@ -148,6 +149,7 @@ public AggregationUpdate set(SetOperation setOperation) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset/">$unset Aggregation
 	 *      Reference</a>
 	 */
+	@Contract("_ -> this")
 	public AggregationUpdate unset(UnsetOperation unsetOperation) {
 
 		Assert.notNull(unsetOperation, "UnsetOperation must not be null");
@@ -166,6 +168,7 @@ public AggregationUpdate unset(UnsetOperation unsetOperation) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">$replaceWith Aggregation
 	 *      Reference</a>
 	 */
+	@Contract("_ -> this")
 	public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) {
 
 		Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null");
@@ -179,6 +182,7 @@ public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation)
 	 * @param value must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public AggregationUpdate replaceWith(Object value) {
 
 		Assert.notNull(value, "Value must not be null");
@@ -193,6 +197,7 @@ public AggregationUpdate replaceWith(Object value) {
 	 * @return new instance of {@link SetValueAppender}.
 	 * @see #set(SetOperation)
 	 */
+	@Contract("_ -> new")
 	public SetValueAppender set(String key) {
 
 		Assert.notNull(key, "Key must not be null");
@@ -219,6 +224,7 @@ public AggregationUpdate toValueOf(Object value) {
 	 * @param keys the fields to remove.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public AggregationUpdate unset(String... keys) {
 
 		Assert.notNull(keys, "Keys must not be null");
@@ -234,6 +240,7 @@ public AggregationUpdate unset(String... keys) {
 	 *
 	 * @return never {@literal null}.
 	 */
+	@Contract("-> this")
 	public AggregationUpdate isolated() {
 
 		isolated = true;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java
index ed79202345..522dd5eae5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.aggregation;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index e2c31c6346..c7787b382c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -20,6 +20,7 @@
 import java.util.Locale;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Avg;
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovariancePop;
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovarianceSamp;
@@ -32,7 +33,7 @@
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum;
 import org.springframework.data.mongodb.core.aggregation.SetWindowFieldsOperation.WindowUnit;
 import org.springframework.data.mongodb.core.aggregation.SetWindowFieldsOperation.WindowUnits;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -84,8 +85,8 @@ public static Rand rand() {
 	 */
 	public static class ArithmeticOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}.
@@ -116,6 +117,7 @@ public ArithmeticOperatorFactory(AggregationExpression expression) {
 		 *
 		 * @return new instance of {@link Abs}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Abs abs() {
 			return usesFieldRef() ? Abs.absoluteValueOf(fieldReference) : Abs.absoluteValueOf(expression);
 		}
@@ -158,6 +160,7 @@ public Add add(Number value) {
 			return createAdd().add(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Add createAdd() {
 			return usesFieldRef() ? Add.valueOf(fieldReference) : Add.valueOf(expression);
 		}
@@ -168,6 +171,7 @@ private Add createAdd() {
 		 *
 		 * @return new instance of {@link Ceil}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Ceil ceil() {
 			return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression);
 		}
@@ -205,6 +209,7 @@ public Derivative derivative(WindowUnit unit) {
 		 * @return new instance of {@link Derivative}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Derivative derivative(@Nullable String unit) {
 
 			Derivative derivative = usesFieldRef() ? Derivative.derivativeOf(fieldReference)
@@ -250,6 +255,7 @@ public Divide divideBy(Number value) {
 			return createDivide().divideBy(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Divide createDivide() {
 			return usesFieldRef() ? Divide.valueOf(fieldReference) : Divide.valueOf(expression);
 		}
@@ -259,6 +265,7 @@ private Divide createDivide() {
 		 *
 		 * @return new instance of {@link Exp}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Exp exp() {
 			return usesFieldRef() ? Exp.expValueOf(fieldReference) : Exp.expValueOf(expression);
 		}
@@ -269,6 +276,7 @@ public Exp exp() {
 		 *
 		 * @return new instance of {@link Floor}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Floor floor() {
 			return usesFieldRef() ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression);
 		}
@@ -279,6 +287,7 @@ public Floor floor() {
 		 * @return new instance of {@link Integral}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Integral integral() {
 			return usesFieldRef() ? Integral.integralOf(fieldReference) : Integral.integralOf(expression);
 		}
@@ -318,6 +327,7 @@ public Integral integral(String unit) {
 		 *
 		 * @return new instance of {@link Ln}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Ln ln() {
 			return usesFieldRef() ? Ln.lnValueOf(fieldReference) : Ln.lnValueOf(expression);
 		}
@@ -345,7 +355,7 @@ public Log log(String fieldReference) {
 		public Log log(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
-			return createLog().log(fieldReference);
+			return createLog().log(expression);
 		}
 
 		/**
@@ -361,6 +371,7 @@ public Log log(Number base) {
 			return createLog().log(base);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Log createLog() {
 			return usesFieldRef() ? Log.valueOf(fieldReference) : Log.valueOf(expression);
 		}
@@ -370,6 +381,7 @@ private Log createLog() {
 		 *
 		 * @return new instance of {@link Log10}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Log10 log10() {
 			return usesFieldRef() ? Log10.log10ValueOf(fieldReference) : Log10.log10ValueOf(expression);
 		}
@@ -413,6 +425,7 @@ public Mod mod(Number value) {
 			return createMod().mod(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Mod createMod() {
 			return usesFieldRef() ? Mod.valueOf(fieldReference) : Mod.valueOf(expression);
 		}
@@ -453,6 +466,7 @@ public Multiply multiplyBy(Number value) {
 			return createMultiply().multiplyBy(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Multiply createMultiply() {
 			return usesFieldRef() ? Multiply.valueOf(fieldReference) : Multiply.valueOf(expression);
 		}
@@ -493,6 +507,7 @@ public Pow pow(Number value) {
 			return createPow().pow(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Pow createPow() {
 			return usesFieldRef() ? Pow.valueOf(fieldReference) : Pow.valueOf(expression);
 		}
@@ -502,6 +517,7 @@ private Pow createPow() {
 		 *
 		 * @return new instance of {@link Sqrt}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Sqrt sqrt() {
 			return usesFieldRef() ? Sqrt.sqrtOf(fieldReference) : Sqrt.sqrtOf(expression);
 		}
@@ -542,6 +558,7 @@ public Subtract subtract(Number value) {
 			return createSubtract().subtract(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Subtract createSubtract() {
 			return usesFieldRef() ? Subtract.valueOf(fieldReference) : Subtract.valueOf(expression);
 		}
@@ -551,6 +568,7 @@ private Subtract createSubtract() {
 		 *
 		 * @return new instance of {@link Trunc}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Trunc trunc() {
 			return usesFieldRef() ? Trunc.truncValueOf(fieldReference) : Trunc.truncValueOf(expression);
 		}
@@ -560,6 +578,7 @@ public Trunc trunc() {
 		 *
 		 * @return new instance of {@link Sum}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Sum sum() {
 			return usesFieldRef() ? AccumulatorOperators.Sum.sumOf(fieldReference)
 					: AccumulatorOperators.Sum.sumOf(expression);
@@ -570,6 +589,7 @@ public Sum sum() {
 		 *
 		 * @return new instance of {@link Avg}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Avg avg() {
 			return usesFieldRef() ? AccumulatorOperators.Avg.avgOf(fieldReference)
 					: AccumulatorOperators.Avg.avgOf(expression);
@@ -580,6 +600,7 @@ public Avg avg() {
 		 *
 		 * @return new instance of {@link Max}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Max max() {
 			return usesFieldRef() ? AccumulatorOperators.Max.maxOf(fieldReference)
 					: AccumulatorOperators.Max.maxOf(expression);
@@ -590,6 +611,7 @@ public Max max() {
 		 *
 		 * @return new instance of {@link Min}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Min min() {
 			return usesFieldRef() ? AccumulatorOperators.Min.minOf(fieldReference)
 					: AccumulatorOperators.Min.minOf(expression);
@@ -600,6 +622,7 @@ public Min min() {
 		 *
 		 * @return new instance of {@link StdDevPop}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StdDevPop stdDevPop() {
 			return usesFieldRef() ? AccumulatorOperators.StdDevPop.stdDevPopOf(fieldReference)
 					: AccumulatorOperators.StdDevPop.stdDevPopOf(expression);
@@ -610,6 +633,7 @@ public StdDevPop stdDevPop() {
 		 *
 		 * @return new instance of {@link StdDevSamp}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StdDevSamp stdDevSamp() {
 			return usesFieldRef() ? AccumulatorOperators.StdDevSamp.stdDevSampOf(fieldReference)
 					: AccumulatorOperators.StdDevSamp.stdDevSampOf(expression);
@@ -639,6 +663,7 @@ public CovariancePop covariancePop(AggregationExpression expression) {
 			return covariancePop().and(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private CovariancePop covariancePop() {
 			return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression);
 		}
@@ -667,6 +692,7 @@ public CovarianceSamp covarianceSamp(AggregationExpression expression) {
 			return covarianceSamp().and(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private CovarianceSamp covarianceSamp() {
 			return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference)
 					: CovarianceSamp.covarianceSampOf(expression);
@@ -679,6 +705,7 @@ private CovarianceSamp covarianceSamp() {
 		 * @return new instance of {@link Round}.
 		 * @since 3.0
 		 */
+		@SuppressWarnings("NullAway")
 		public Round round() {
 			return usesFieldRef() ? Round.roundValueOf(fieldReference) : Round.roundValueOf(expression);
 		}
@@ -712,6 +739,7 @@ public Sin sin() {
 		 * @return new instance of {@link Sin}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Sin sin(AngularUnit unit) {
 			return usesFieldRef() ? Sin.sinOf(fieldReference, unit) : Sin.sinOf(expression, unit);
 		}
@@ -734,6 +762,7 @@ public Sinh sinh() {
 		 * @return new instance of {@link Sinh}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Sinh sinh(AngularUnit unit) {
 			return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit);
 		}
@@ -744,6 +773,7 @@ public Sinh sinh(AngularUnit unit) {
 		 * @return new instance of {@link ASin}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ASin asin() {
 			return usesFieldRef() ? ASin.asinOf(fieldReference) : ASin.asinOf(expression);
 		}
@@ -754,6 +784,7 @@ public ASin asin() {
 		 * @return new instance of {@link ASinh}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ASinh asinh() {
 			return usesFieldRef() ? ASinh.asinhOf(fieldReference) : ASinh.asinhOf(expression);
 		}
@@ -777,6 +808,7 @@ public Cos cos() {
 		 * @return new instance of {@link Cos}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Cos cos(AngularUnit unit) {
 			return usesFieldRef() ? Cos.cosOf(fieldReference, unit) : Cos.cosOf(expression, unit);
 		}
@@ -799,6 +831,7 @@ public Cosh cosh() {
 		 * @return new instance of {@link Cosh}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Cosh cosh(AngularUnit unit) {
 			return usesFieldRef() ? Cosh.coshOf(fieldReference, unit) : Cosh.coshOf(expression, unit);
 		}
@@ -809,6 +842,7 @@ public Cosh cosh(AngularUnit unit) {
 		 * @return new instance of {@link ACos}.
 		 * @since 3.4
 		 */
+		@SuppressWarnings("NullAway")
 		public ACos acos() {
 			return usesFieldRef() ? ACos.acosOf(fieldReference) : ACos.acosOf(expression);
 		}
@@ -819,6 +853,7 @@ public ACos acos() {
 		 * @return new instance of {@link ACosh}.
 		 * @since 3.4
 		 */
+		@SuppressWarnings("NullAway")
 		public ACosh acosh() {
 			return usesFieldRef() ? ACosh.acoshOf(fieldReference) : ACosh.acoshOf(expression);
 		}
@@ -840,6 +875,7 @@ public Tan tan() {
 		 * @return new instance of {@link ATan}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ATan atan() {
 			return usesFieldRef() ? ATan.atanOf(fieldReference) : ATan.atanOf(expression);
 		}
@@ -852,6 +888,7 @@ public ATan atan() {
 		 * @return new instance of {@link ATan2}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ATan2 atan2(Number value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -886,8 +923,8 @@ public ATan2 atan2(AggregationExpression expression) {
 			return createATan2().atan2of(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ATan2 createATan2() {
-
 			return usesFieldRef() ? ATan2.valueOf(fieldReference) : ATan2.valueOf(expression);
 		}
 
@@ -897,6 +934,7 @@ private ATan2 createATan2() {
 		 * @return new instance of {@link ATanh}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public ATanh atanh() {
 			return usesFieldRef() ? ATanh.atanhOf(fieldReference) : ATanh.atanhOf(expression);
 		}
@@ -909,6 +947,7 @@ public ATanh atanh() {
 		 * @return new instance of {@link Tan}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Tan tan(AngularUnit unit) {
 			return usesFieldRef() ? Tan.tanOf(fieldReference, unit) : Tan.tanOf(expression, unit);
 		}
@@ -931,18 +970,19 @@ public Tanh tanh() {
 		 * @return new instance of {@link Tanh}.
 		 * @since 3.3
 		 */
+		@SuppressWarnings("NullAway")
 		public Tanh tanh(AngularUnit unit) {
 			return usesFieldRef() ? Tanh.tanhOf(fieldReference, unit) : Tanh.tanhOf(expression, unit);
 		}
 
 		/**
-		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the
-		 * numeric value.
+		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the numeric value.
 		 *
 		 * @return new instance of {@link Percentile}.
 		 * @param percentages must not be {@literal null}.
 		 * @since 4.2
 		 */
+		@SuppressWarnings("NullAway")
 		public Percentile percentile(Double... percentages) {
 			Percentile percentile = usesFieldRef() ? AccumulatorOperators.Percentile.percentileOf(fieldReference)
 					: AccumulatorOperators.Percentile.percentileOf(expression);
@@ -950,12 +990,12 @@ public Percentile percentile(Double... percentages) {
 		}
 
 		/**
-		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the
-		 * numeric value.
+		 * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the numeric value.
 		 *
 		 * @return new instance of {@link Median}.
 		 * @since 4.2
 		 */
+		@SuppressWarnings("NullAway")
 		public Median median() {
 			return usesFieldRef() ? AccumulatorOperators.Median.medianOf(fieldReference)
 					: AccumulatorOperators.Median.medianOf(expression);
@@ -1077,6 +1117,7 @@ public static Add valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Add}.
 		 */
+		@Contract("_ -> new")
 		public Add add(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1089,6 +1130,7 @@ public Add add(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Add}.
 		 */
+		@Contract("_ -> new")
 		public Add add(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1101,6 +1143,7 @@ public Add add(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Add}.
 		 */
+		@Contract("_ -> new")
 		public Add add(Number value) {
 			return new Add(append(value));
 		}
@@ -1217,6 +1260,7 @@ public static Divide valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Divide}.
 		 */
+		@Contract("_ -> new")
 		public Divide divideBy(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1229,6 +1273,7 @@ public Divide divideBy(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Divide}.
 		 */
+		@Contract("_ -> new")
 		public Divide divideBy(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1241,6 +1286,7 @@ public Divide divideBy(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Divide}.
 		 */
+		@Contract("_ -> new")
 		public Divide divideBy(Number value) {
 			return new Divide(append(value));
 		}
@@ -1463,6 +1509,7 @@ public static Log valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Log}.
 		 */
+		@Contract("_ -> new")
 		public Log log(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1475,6 +1522,7 @@ public Log log(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Log}.
 		 */
+		@Contract("_ -> new")
 		public Log log(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1487,6 +1535,7 @@ public Log log(AggregationExpression expression) {
 		 * @param base must not be {@literal null}.
 		 * @return new instance of {@link Log}.
 		 */
+		@Contract("_ -> new")
 		public Log log(Number base) {
 			return new Log(append(base));
 		}
@@ -1603,6 +1652,7 @@ public static Mod valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Mod}.
 		 */
+		@Contract("_ -> new")
 		public Mod mod(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1615,6 +1665,7 @@ public Mod mod(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Mod}.
 		 */
+		@Contract("_ -> new")
 		public Mod mod(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1627,6 +1678,7 @@ public Mod mod(AggregationExpression expression) {
 		 * @param base must not be {@literal null}.
 		 * @return new instance of {@link Mod}.
 		 */
+		@Contract("_ -> new")
 		public Mod mod(Number base) {
 			return new Mod(append(base));
 		}
@@ -1690,6 +1742,7 @@ public static Multiply valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Multiply}.
 		 */
+		@Contract("_ -> new")
 		public Multiply multiplyBy(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1702,6 +1755,7 @@ public Multiply multiplyBy(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Multiply}.
 		 */
+		@Contract("_ -> new")
 		public Multiply multiplyBy(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1714,6 +1768,7 @@ public Multiply multiplyBy(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Multiply}.
 		 */
+		@Contract("_ -> new")
 		public Multiply multiplyBy(Number value) {
 			return new Multiply(append(value));
 		}
@@ -1777,6 +1832,7 @@ public static Pow valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Pow pow(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1789,6 +1845,7 @@ public Pow pow(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Pow pow(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1801,6 +1858,7 @@ public Pow pow(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Pow pow(Number value) {
 			return new Pow(append(value));
 		}
@@ -1917,6 +1975,7 @@ public static Subtract valueOf(Number value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Subtract subtract(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1929,6 +1988,7 @@ public Subtract subtract(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Subtract subtract(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1941,6 +2001,7 @@ public Subtract subtract(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Pow}.
 		 */
+		@Contract("_ -> new")
 		public Subtract subtract(Number value) {
 			return new Subtract(append(value));
 		}
@@ -2060,6 +2121,7 @@ public static Round round(Number value) {
 		 * @param place value between -20 and 100, exclusive.
 		 * @return new instance of {@link Round}.
 		 */
+		@Contract("_ -> new")
 		public Round place(int place) {
 			return new Round(append(place));
 		}
@@ -2070,6 +2132,7 @@ public Round place(int place) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Round}.
 		 */
+		@Contract("_ -> new")
 		public Round placeOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2083,6 +2146,7 @@ public Round placeOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Round}.
 		 */
+		@Contract("_ -> new")
 		public Round placeOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -2133,6 +2197,7 @@ public static Derivative derivativeOfValue(Number value) {
 			return new Derivative(Collections.singletonMap("input", value));
 		}
 
+		@Contract("_ -> new")
 		public Derivative unit(String unit) {
 			return new Derivative(append("unit", unit));
 		}
@@ -2183,6 +2248,7 @@ public static Integral integralOf(AggregationExpression expression) {
 		 * @param unit the unit of measure.
 		 * @return new instance of {@link Integral}.
 		 */
+		@Contract("_ -> new")
 		public Integral unit(String unit) {
 			return new Integral(append("unit", unit));
 		}
@@ -2217,8 +2283,7 @@ private Sin(Object value) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
-		 * {@link AngularUnit#RADIANS radians}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS radians}. <br />
 		 * Use {@code sinhOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -2330,8 +2395,7 @@ public static Sinh sinhOf(String fieldReference) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * <br />
+		 * the given {@link AngularUnit unit}. <br />
 		 * Use {@code sinhOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -2350,8 +2414,7 @@ public static Sinh sinhOf(String fieldReference, AngularUnit unit) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS}. <br />
 		 * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
 		 * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
 		 *
@@ -2434,8 +2497,7 @@ public static ASin asinOf(String fieldReference) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value.
-		 * <br />
+		 * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value. <br />
 		 *
 		 * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
 		 * @return new instance of {@link ASin}.
@@ -2484,8 +2546,7 @@ public static ASinh asinhOf(String fieldReference) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
-		 * <br />
+		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value. <br />
 		 *
 		 * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
 		 * @return new instance of {@link ASinh}.
@@ -2525,8 +2586,7 @@ private Cos(Object value) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
-		 * {@link AngularUnit#RADIANS radians}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS radians}. <br />
 		 * Use {@code cosOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -2636,8 +2696,7 @@ public static Cosh coshOf(String fieldReference) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * <br />
+		 * the given {@link AngularUnit unit}. <br />
 		 * Use {@code coshOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -2654,8 +2713,7 @@ public static Cosh coshOf(String fieldReference, AngularUnit unit) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS}. <br />
 		 * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
 		 * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
 		 *
@@ -2738,8 +2796,7 @@ public static ACos acosOf(String fieldReference) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value.
-		 * <br />
+		 * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value. <br />
 		 *
 		 * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
 		 * @return new instance of {@link ACos}.
@@ -2788,8 +2845,7 @@ public static ACosh acoshOf(String fieldReference) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value.
-		 * <br />
+		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value. <br />
 		 *
 		 * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
 		 * @return new instance of {@link ACosh}.
@@ -2829,8 +2885,7 @@ private Tan(Object value) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
-		 * {@link AngularUnit#RADIANS radians}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS radians}. <br />
 		 * Use {@code tanOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -3008,8 +3063,8 @@ public static ATan2 valueOf(AggregationExpression expression) {
 		 * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
 		 * the first and second values passed to the expression respectively.
 		 *
-		 * @param fieldReference anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
-		 *          numeric value.
+		 * @param fieldReference anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves
+		 *          to a numeric value.
 		 * @return new instance of {@link ATan2}.
 		 */
 		public ATan2 atan2of(String fieldReference) {
@@ -3022,8 +3077,8 @@ public ATan2 atan2of(String fieldReference) {
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
 		 * {@link AngularUnit#RADIANS}.
 		 *
-		 * @param expression anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
-		 *          numeric value.
+		 * @param expression anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to
+		 *          a numeric value.
 		 * @return new instance of {@link ATan2}.
 		 */
 		public ATan2 atan2of(AggregationExpression expression) {
@@ -3075,8 +3130,7 @@ public static Tanh tanhOf(String fieldReference) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * <br />
+		 * the given {@link AngularUnit unit}. <br />
 		 * Use {@code tanhOf("angle", DEGREES)} as shortcut for
 		 *
 		 * <pre>
@@ -3093,8 +3147,7 @@ public static Tanh tanhOf(String fieldReference, AngularUnit unit) {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * <br />
+		 * {@link AngularUnit#RADIANS}. <br />
 		 * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
 		 * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
 		 *
@@ -3165,11 +3218,9 @@ private ATanh(Object value) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse
-		 * hyperbolic tangent of a value.
+		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value.
 		 *
-		 * @param fieldReference the name of the {@link Field field} that resolves to a
-		 *                       numeric value.
+		 * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
 		 * @return new instance of {@link ATanh}.
 		 */
 		public static ATanh atanhOf(String fieldReference) {
@@ -3177,8 +3228,7 @@ public static ATanh atanhOf(String fieldReference) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value.
-		 * <br />
+		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. <br />
 		 *
 		 * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
 		 * @return new instance of {@link ATanh}.
@@ -3188,11 +3238,10 @@ public static ATanh atanhOf(AggregationExpression expression) {
 		}
 
 		/**
-		 * Creates a new {@link AggregationExpression} that calculates the inverse
-		 * hyperbolic tangent of a value.
+		 * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value.
 		 *
-		 * @param value anything ({@link Field field}, {@link AggregationExpression
-		 *              expression}, ...) that resolves to a numeric value.
+		 * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+		 *          numeric value.
 		 * @return new instance of {@link ATanh}.
 		 */
 		public static ATanh atanhOf(Object value) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java
index 41688bfc62..7770728fe3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java
@@ -22,12 +22,13 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder;
 import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -158,6 +159,7 @@ public ArrayElemAt elementAt(String fieldReference) {
 			return createArrayElemAt().elementAt(fieldReference);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ArrayElemAt createArrayElemAt() {
 
 			if (usesFieldRef()) {
@@ -193,6 +195,7 @@ public ConcatArrays concat(AggregationExpression expression) {
 			return createConcatArrays().concat(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ConcatArrays createConcatArrays() {
 
 			if (usesFieldRef()) {
@@ -208,6 +211,7 @@ private ConcatArrays createConcatArrays() {
 		 *
 		 * @return new instance of {@link AsBuilder} to create a {@link Filter}.
 		 */
+		@SuppressWarnings("NullAway")
 		public AsBuilder filter() {
 
 			if (usesFieldRef()) {
@@ -227,6 +231,7 @@ public AsBuilder filter() {
 		 *
 		 * @return new instance of {@link IsArray}.
 		 */
+		@SuppressWarnings("NullAway")
 		public IsArray isArray() {
 
 			Assert.state(values == null, "Does it make sense to call isArray on an array; Maybe just skip it");
@@ -239,6 +244,7 @@ public IsArray isArray() {
 		 *
 		 * @return new instance of {@link Size}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Size length() {
 
 			if (usesFieldRef()) {
@@ -253,6 +259,7 @@ public Size length() {
 		 *
 		 * @return new instance of {@link Slice}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Slice slice() {
 
 			if (usesFieldRef()) {
@@ -269,6 +276,7 @@ public Slice slice() {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link IndexOfArray}.
 		 */
+		@SuppressWarnings("NullAway")
 		public IndexOfArray indexOf(Object value) {
 
 			if (usesFieldRef()) {
@@ -284,6 +292,7 @@ public IndexOfArray indexOf(Object value) {
 		 *
 		 * @return new instance of {@link ReverseArray}.
 		 */
+		@SuppressWarnings("NullAway")
 		public ReverseArray reverse() {
 
 			if (usesFieldRef()) {
@@ -301,6 +310,7 @@ public ReverseArray reverse() {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}.
 		 */
+		@SuppressWarnings("NullAway")
 		public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(AggregationExpression expression) {
 
 			return initialValue -> (usesFieldRef() ? Reduce.arrayOf(fieldReference)
@@ -314,6 +324,7 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(AggregationExpressi
 		 * @param expressions must not be {@literal null}.
 		 * @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}.
 		 */
+		@SuppressWarnings("NullAway")
 		public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression... expressions) {
 
 			return initialValue -> (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression))
@@ -327,6 +338,7 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression.
 		 * @return new instance of {@link SortArray}.
 		 * @since 4.0
 		 */
+		@SuppressWarnings("NullAway")
 		public SortArray sort(Sort sort) {
 
 			if (usesFieldRef()) {
@@ -344,6 +356,7 @@ public SortArray sort(Sort sort) {
 		 * @param arrays must not be {@literal null}.
 		 * @return new instance of {@link Zip}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Zip zipWith(Object... arrays) {
 
 			if (usesFieldRef()) {
@@ -360,6 +373,7 @@ public Zip zipWith(Object... arrays) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link In}.
 		 */
+		@SuppressWarnings("NullAway")
 		public In containsValue(Object value) {
 
 			if (usesFieldRef()) {
@@ -376,6 +390,7 @@ public In containsValue(Object value) {
 		 * @return new instance of {@link ArrayToObject}.
 		 * @since 2.1
 		 */
+		@SuppressWarnings("NullAway")
 		public ArrayToObject toObject() {
 
 			if (usesFieldRef()) {
@@ -392,6 +407,7 @@ public ArrayToObject toObject() {
 		 * @return new instance of {@link First}.
 		 * @since 3.4
 		 */
+		@SuppressWarnings("NullAway")
 		public First first() {
 
 			if (usesFieldRef()) {
@@ -408,6 +424,7 @@ public First first() {
 		 * @return new instance of {@link Last}.
 		 * @since 3.4
 		 */
+		@SuppressWarnings("NullAway")
 		public Last last() {
 
 			if (usesFieldRef()) {
@@ -506,6 +523,7 @@ public static ArrayElemAt arrayOf(Collection<?> values) {
 		 * @param index the index number
 		 * @return new instance of {@link ArrayElemAt}.
 		 */
+		@Contract("_ -> new")
 		public ArrayElemAt elementAt(int index) {
 			return new ArrayElemAt(append(index));
 		}
@@ -516,6 +534,7 @@ public ArrayElemAt elementAt(int index) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ArrayElemAt}.
 		 */
+		@Contract("_ -> new")
 		public ArrayElemAt elementAt(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -528,6 +547,7 @@ public ArrayElemAt elementAt(AggregationExpression expression) {
 		 * @param arrayFieldReference the field name.
 		 * @return new instance of {@link ArrayElemAt}.
 		 */
+		@Contract("_ -> new")
 		public ArrayElemAt elementAt(String arrayFieldReference) {
 
 			Assert.notNull(arrayFieldReference, "ArrayReference must not be null");
@@ -594,6 +614,7 @@ public static ConcatArrays arrayOf(Collection<?> values) {
 		 * @param arrayFieldReference must not be {@literal null}.
 		 * @return new instance of {@link ConcatArrays}.
 		 */
+		@Contract("_ -> new")
 		public ConcatArrays concat(String arrayFieldReference) {
 
 			Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null");
@@ -606,6 +627,7 @@ public ConcatArrays concat(String arrayFieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ConcatArrays}.
 		 */
+		@Contract("_ -> new")
 		public ConcatArrays concat(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -681,9 +703,12 @@ public static AsBuilder filter(List<?> values) {
 
 		@Override
 		public Document toDocument(final AggregationOperationContext context) {
+
+			Assert.notNull(as, "As must be set first");
 			return toFilter(ExposedFields.from(as), context);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Document toFilter(ExposedFields exposedFields, AggregationOperationContext context) {
 
 			Document filterExpression = new Document();
@@ -697,7 +722,7 @@ private Document toFilter(ExposedFields exposedFields, AggregationOperationConte
 			return new Document("$filter", filterExpression);
 		}
 
-		private Object getMappedInput(AggregationOperationContext context) {
+		private @Nullable Object getMappedInput(AggregationOperationContext context) {
 
 			if (input instanceof Field field) {
 				return context.getReference(field).toString();
@@ -710,7 +735,7 @@ private Object getMappedInput(AggregationOperationContext context) {
 			return input;
 		}
 
-		private Object getMappedCondition(AggregationOperationContext context) {
+		private @Nullable Object getMappedCondition(AggregationOperationContext context) {
 
 			if (!(condition instanceof AggregationExpression aggregationExpression)) {
 				return condition;
@@ -817,6 +842,7 @@ public static InputBuilder newBuilder() {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public AsBuilder filter(List<?> array) {
 
 				Assert.notNull(array, "Array must not be null");
@@ -825,6 +851,7 @@ public AsBuilder filter(List<?> array) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public AsBuilder filter(Field field) {
 
 				Assert.notNull(field, "Field must not be null");
@@ -833,6 +860,7 @@ public AsBuilder filter(Field field) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public AsBuilder filter(AggregationExpression expression) {
 
 				Assert.notNull(expression, "Expression must not be null");
@@ -841,6 +869,7 @@ public AsBuilder filter(AggregationExpression expression) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ConditionBuilder as(String variableName) {
 
 				Assert.notNull(variableName, "Variable name  must not be null");
@@ -1028,6 +1057,7 @@ public static Slice sliceArrayOf(Collection<?> values) {
 		 * @param count number of elements to slice.
 		 * @return new instance of {@link Slice}.
 		 */
+		@Contract("_ -> new")
 		public Slice itemCount(int count) {
 			return new Slice(append(count));
 		}
@@ -1040,6 +1070,7 @@ public Slice itemCount(int count) {
 		 * @return new instance of {@link Slice}.
 		 * @since 4.5
 		 */
+		@Contract("_ -> new")
 		public Slice itemCount(AggregationExpression count) {
 			return new Slice(append(count));
 		}
@@ -1158,6 +1189,7 @@ public static IndexOfArrayBuilder arrayOf(Collection<?> values) {
 		 * @param range the lookup range.
 		 * @return new instance of {@link IndexOfArray}.
 		 */
+		@Contract("_ -> new")
 		public IndexOfArray within(Range<Long> range) {
 			return new IndexOfArray(append(AggregationUtils.toRangeValues(range)));
 		}
@@ -1233,6 +1265,7 @@ public static RangeOperatorBuilder rangeStartingAt(long value) {
 			return new RangeOperatorBuilder(value);
 		}
 
+		@Contract("_ -> new")
 		public RangeOperator withStepSize(long stepSize) {
 			return new RangeOperator(append(stepSize));
 		}
@@ -1365,6 +1398,7 @@ public Document toDocument(AggregationOperationContext context) {
 			return new Document("$reduce", document);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object getMappedValue(Object value, AggregationOperationContext context) {
 
 			if (value instanceof Document) {
@@ -1684,6 +1718,7 @@ public static ZipBuilder arrayOf(Collection<?> values) {
 		 *
 		 * @return new instance of {@link Zip}.
 		 */
+		@Contract("-> new")
 		public Zip useLongestLength() {
 			return new Zip(append("useLongestLength", true));
 		}
@@ -1694,6 +1729,7 @@ public Zip useLongestLength() {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Zip}.
 		 */
+		@Contract("_ -> new")
 		public Zip defaultTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1706,6 +1742,7 @@ public Zip defaultTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Zip}.
 		 */
+		@Contract("_ -> new")
 		public Zip defaultTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1718,6 +1755,7 @@ public Zip defaultTo(AggregationExpression expression) {
 		 * @param array must not be {@literal null}.
 		 * @return new instance of {@link Zip}.
 		 */
+		@Contract("_ -> new")
 		public Zip defaultTo(Object[] array) {
 
 			Assert.notNull(array, "Array must not be null");
@@ -2055,6 +2093,7 @@ public static SortArray sortArrayOf(AggregationExpression expression) {
 		 * @param sort must not be {@literal null}.
 		 * @return new instance of {@link SortArray}.
 		 */
+		@Contract("_ -> new")
 		public SortArray by(Sort sort) {
 			return new SortArray(append("sortBy", sort));
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java
index 69689908c9..f3ffdb7ad1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java
@@ -19,6 +19,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -77,8 +79,8 @@ public static Not not(AggregationExpression expression) {
 	 */
 	public static class BooleanOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}.
@@ -130,6 +132,7 @@ public And and(String fieldReference) {
 			return createAnd().andField(fieldReference);
 		}
 
+		@SuppressWarnings("NullAway")
 		private And createAnd() {
 			return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression);
 		}
@@ -160,6 +163,7 @@ public Or or(String fieldReference) {
 			return createOr().orField(fieldReference);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Or createOr() {
 			return usesFieldRef() ? Or.or(Fields.field(fieldReference)) : Or.or(expression);
 		}
@@ -169,6 +173,7 @@ private Or createOr() {
 		 *
 		 * @return new instance of {@link Not}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Not not() {
 			return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression);
 		}
@@ -211,6 +216,7 @@ public static And and(Object... expressions) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link And}.
 		 */
+		@Contract("_ -> new")
 		public And andExpression(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -223,6 +229,7 @@ public And andExpression(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link And}.
 		 */
+		@Contract("_ -> new")
 		public And andField(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -235,6 +242,7 @@ public And andField(String fieldReference) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link And}.
 		 */
+		@Contract("_ -> new")
 		public And andValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -277,6 +285,7 @@ public static Or or(Object... expressions) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Or}.
 		 */
+		@Contract("_ -> new")
 		public Or orExpression(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -289,6 +298,7 @@ public Or orExpression(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Or}.
 		 */
+		@Contract("_ -> new")
 		public Or orField(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -301,6 +311,7 @@ public Or orField(String fieldReference) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Or}.
 		 */
+		@Contract("_ -> new")
 		public Or orValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java
index 36492e2a81..16eca4ec22 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java
@@ -16,6 +16,7 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.BucketAutoOperationOutputBuilder;
 import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder;
 import org.springframework.util.Assert;
@@ -38,7 +39,7 @@ public class BucketAutoOperation extends BucketOperationSupport<BucketAutoOperat
 		implements FieldsExposingAggregationOperation {
 
 	private final int buckets;
-	private final String granularity;
+	private final @Nullable String granularity;
 
 	/**
 	 * Creates a new {@link BucketAutoOperation} given a {@link Field group-by field}.
@@ -80,7 +81,7 @@ private BucketAutoOperation(BucketAutoOperation bucketOperation, Outputs outputs
 		this.granularity = bucketOperation.granularity;
 	}
 
-	private BucketAutoOperation(BucketAutoOperation bucketOperation, int buckets, String granularity) {
+	private BucketAutoOperation(BucketAutoOperation bucketOperation, int buckets, @Nullable String granularity) {
 
 		super(bucketOperation);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java
index 525789e628..6ed686c086 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java
@@ -21,7 +21,9 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.BucketOperation.BucketOperationOutputBuilder;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -40,7 +42,7 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc
 		implements FieldsExposingAggregationOperation {
 
 	private final List<Object> boundaries;
-	private final Object defaultBucket;
+	private final @Nullable Object defaultBucket;
 
 	/**
 	 * Creates a new {@link BucketOperation} given a {@link Field group-by field}.
@@ -76,7 +78,7 @@ private BucketOperation(BucketOperation bucketOperation, Outputs outputs) {
 		this.defaultBucket = bucketOperation.defaultBucket;
 	}
 
-	private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, Object defaultBucket) {
+	private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, @Nullable Object defaultBucket) {
 
 		super(bucketOperation);
 
@@ -111,6 +113,7 @@ public String getOperator() {
 	 * @param literal must not be {@literal null}.
 	 * @return new instance of {@link BucketOperation}.
 	 */
+	@Contract("_ -> new")
 	public BucketOperation withDefaultBucket(Object literal) {
 
 		Assert.notNull(literal, "Default bucket literal must not be null");
@@ -124,6 +127,7 @@ public BucketOperation withDefaultBucket(Object literal) {
 	 * @param boundaries must not be {@literal null}.
 	 * @return new instance of {@link BucketOperation}.
 	 */
+	@Contract("_ -> new")
 	public BucketOperation withBoundaries(Object... boundaries) {
 
 		Assert.notNull(boundaries, "Boundaries must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java
index e19ad59a3f..3d5ded05c2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java
@@ -22,6 +22,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
@@ -40,8 +41,8 @@
 public abstract class BucketOperationSupport<T extends BucketOperationSupport<T, B>, B extends OutputBuilder<B, T>>
 		implements FieldsExposingAggregationOperation {
 
-	private final Field groupByField;
-	private final AggregationExpression groupByExpression;
+	private final @Nullable Field groupByField;
+	private final @Nullable AggregationExpression groupByExpression;
 	private final Outputs outputs;
 
 	/**
@@ -142,12 +143,17 @@ public Document toDocument(AggregationOperationContext context) {
 	}
 
 	@Override
+
 	public Document toDocument(AggregationOperationContext context) {
 
 		Document document = new Document();
 
-		document.put("groupBy", groupByExpression == null ? context.getReference(groupByField).toString()
-				: groupByExpression.toDocument(context));
+		if(groupByExpression != null) {
+			document.put("groupBy", groupByExpression.toDocument(context));
+		} else if (groupByField != null) {
+			document.put("groupBy", context.getReference(groupByField).toString());
+
+		}
 
 		if (!outputs.isEmpty()) {
 			document.put("output", outputs.toDocument(context));
@@ -625,7 +631,9 @@ public SpelExpressionOutput(String expression, Object[] parameters) {
 
 		@Override
 		public Document toDocument(AggregationOperationContext context) {
-			return (Document) TRANSFORMER.transform(expression, context, params);
+
+			Object o =  TRANSFORMER.transform(expression, context, params);
+			return o instanceof Document document ? document : new Document();
 		}
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java
index f27b7f16cb..e2626c3a16 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java
@@ -18,6 +18,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -50,8 +52,8 @@ public static ComparisonOperatorFactory valueOf(AggregationExpression expression
 
 	public static class ComparisonOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}.
@@ -107,6 +109,7 @@ public Cmp compareToValue(Object value) {
 			return createCmp().compareToValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Cmp createCmp() {
 			return usesFieldRef() ? Cmp.valueOf(fieldReference) : Cmp.valueOf(expression);
 		}
@@ -144,6 +147,7 @@ public Eq equalToValue(Object value) {
 			return createEq().equalToValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Eq createEq() {
 			return usesFieldRef() ? Eq.valueOf(fieldReference) : Eq.valueOf(expression);
 		}
@@ -181,6 +185,7 @@ public Gt greaterThanValue(Object value) {
 			return createGt().greaterThanValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Gt createGt() {
 			return usesFieldRef() ? Gt.valueOf(fieldReference) : Gt.valueOf(expression);
 		}
@@ -218,6 +223,7 @@ public Gte greaterThanEqualToValue(Object value) {
 			return createGte().greaterThanEqualToValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Gte createGte() {
 			return usesFieldRef() ? Gte.valueOf(fieldReference) : Gte.valueOf(expression);
 		}
@@ -255,6 +261,7 @@ public Lt lessThanValue(Object value) {
 			return createLt().lessThanValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Lt createLt() {
 			return usesFieldRef() ? Lt.valueOf(fieldReference) : Lt.valueOf(expression);
 		}
@@ -292,6 +299,7 @@ public Lte lessThanEqualToValue(Object value) {
 			return createLte().lessThanEqualToValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Lte createLte() {
 			return usesFieldRef() ? Lte.valueOf(fieldReference) : Lte.valueOf(expression);
 		}
@@ -329,6 +337,7 @@ public Ne notEqualToValue(Object value) {
 			return createNe().notEqualToValue(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Ne createNe() {
 			return usesFieldRef() ? Ne.valueOf(fieldReference) : Ne.valueOf(expression);
 		}
@@ -384,6 +393,7 @@ public static Cmp valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Cmp}.
 		 */
+		@Contract("_ -> new")
 		public Cmp compareTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -396,6 +406,7 @@ public Cmp compareTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Cmp}.
 		 */
+		@Contract("_ -> new")
 		public Cmp compareTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -408,6 +419,7 @@ public Cmp compareTo(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Cmp}.
 		 */
+		@Contract("_ -> new")
 		public Cmp compareToValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -461,6 +473,7 @@ public static Eq valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Eq}.
 		 */
+		@Contract("_ -> new")
 		public Eq equalTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -473,6 +486,7 @@ public Eq equalTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Eq}.
 		 */
+		@Contract("_ -> new")
 		public Eq equalTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -485,6 +499,7 @@ public Eq equalTo(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Eq}.
 		 */
+		@Contract("_ -> new")
 		public Eq equalToValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -538,6 +553,7 @@ public static Gt valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Gt}.
 		 */
+		@Contract("_ -> new")
 		public Gt greaterThan(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -550,6 +566,7 @@ public Gt greaterThan(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Gt}.
 		 */
+		@Contract("_ -> new")
 		public Gt greaterThan(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -562,6 +579,7 @@ public Gt greaterThan(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Gt}.
 		 */
+		@Contract("_ -> new")
 		public Gt greaterThanValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -615,6 +633,7 @@ public static Lt valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Lt}.
 		 */
+		@Contract("_ -> new")
 		public Lt lessThan(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -627,6 +646,7 @@ public Lt lessThan(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Lt}.
 		 */
+		@Contract("_ -> new")
 		public Lt lessThan(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -639,6 +659,7 @@ public Lt lessThan(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Lt}.
 		 */
+		@Contract("_ -> new")
 		public Lt lessThanValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -692,6 +713,7 @@ public static Gte valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Gte}.
 		 */
+		@Contract("_ -> new")
 		public Gte greaterThanEqualTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -704,6 +726,7 @@ public Gte greaterThanEqualTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Gte}.
 		 */
+		@Contract("_ -> new")
 		public Gte greaterThanEqualTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -716,6 +739,7 @@ public Gte greaterThanEqualTo(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Gte}.
 		 */
+		@Contract("_ -> new")
 		public Gte greaterThanEqualToValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -769,6 +793,7 @@ public static Lte valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Lte}.
 		 */
+		@Contract("_ -> new")
 		public Lte lessThanEqualTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -781,6 +806,7 @@ public Lte lessThanEqualTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Lte}.
 		 */
+		@Contract("_ -> new")
 		public Lte lessThanEqualTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -793,6 +819,7 @@ public Lte lessThanEqualTo(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Lte}.
 		 */
+		@Contract("_ -> new")
 		public Lte lessThanEqualToValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -846,6 +873,7 @@ public static Ne valueOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Ne}.
 		 */
+		@Contract("_ -> new")
 		public Ne notEqualTo(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -858,6 +886,7 @@ public Ne notEqualTo(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Ne}.
 		 */
+		@Contract("_ -> new")
 		public Ne notEqualTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -870,6 +899,7 @@ public Ne notEqualTo(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Ne}.
 		 */
+		@Contract("_ -> new")
 		public Ne notEqualToValue(Object value) {
 
 			Assert.notNull(value, "Value must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java
index 323a11895b..462d94d6f1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java
@@ -22,12 +22,13 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -211,6 +212,7 @@ public OtherwiseBuilder thenValueOf(String fieldReference) {
 			return createThenBuilder().thenValueOf(fieldReference);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ThenBuilder createThenBuilder() {
 
 			if (usesFieldRef()) {
@@ -303,6 +305,7 @@ private Object mapCondition(Object condition, AggregationOperationContext contex
 			}
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object resolve(Object value, AggregationOperationContext context) {
 
 			if (value instanceof Field field) {
@@ -389,7 +392,7 @@ public interface ThenBuilder extends OrBuilder {
 		 */
 		static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder {
 
-			private @Nullable List<Object> conditions;
+			private List<Object> conditions;
 
 			private IfNullOperatorBuilder() {
 				conditions = new ArrayList<>();
@@ -404,6 +407,7 @@ public static IfNullOperatorBuilder newBuilder() {
 				return new IfNullOperatorBuilder();
 			}
 
+			@Contract("_ -> this")
 			public ThenBuilder ifNull(String fieldReference) {
 
 				Assert.hasText(fieldReference, "FieldReference name must not be null or empty");
@@ -412,6 +416,7 @@ public ThenBuilder ifNull(String fieldReference) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder ifNull(AggregationExpression expression) {
 
 				Assert.notNull(expression, "AggregationExpression name must not be null or empty");
@@ -420,25 +425,30 @@ public ThenBuilder ifNull(AggregationExpression expression) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder orIfNull(String fieldReference) {
 				return ifNull(fieldReference);
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder orIfNull(AggregationExpression expression) {
 				return ifNull(expression);
 			}
 
+			@Contract("_ -> new")
 			public IfNull then(Object value) {
 				return new IfNull(conditions, value);
 			}
 
+			@Contract("_ -> new")
 			public IfNull thenValueOf(String fieldReference) {
 
 				Assert.notNull(fieldReference, "FieldReference must not be null");
 				return new IfNull(conditions, Fields.field(fieldReference));
 			}
 
+			@Contract("_ -> new")
 			public IfNull thenValueOf(AggregationExpression expression) {
 
 				Assert.notNull(expression, "Expression must not be null");
@@ -491,6 +501,7 @@ public static Switch switchCases(List<CaseOperator> conditions) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Switch}.
 		 */
+		@Contract("_ -> new")
 		public Switch defaultTo(Object value) {
 			return new Switch(append("default", value));
 		}
@@ -623,6 +634,7 @@ public Document toDocument(AggregationOperationContext context) {
 			return new Document("$cond", condObject);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object resolveValue(AggregationOperationContext context, Object value) {
 
 			if (value instanceof Document || value instanceof Field) {
@@ -886,6 +898,7 @@ public static ConditionalExpressionBuilder newBuilder() {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ConditionalExpressionBuilder when(Document booleanExpression) {
 
 				Assert.notNull(booleanExpression, "'Boolean expression' must not be null");
@@ -895,6 +908,7 @@ public ConditionalExpressionBuilder when(Document booleanExpression) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder when(CriteriaDefinition criteria) {
 
 				Assert.notNull(criteria, "Criteria must not be null");
@@ -903,6 +917,7 @@ public ThenBuilder when(CriteriaDefinition criteria) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder when(AggregationExpression expression) {
 
 				Assert.notNull(expression, "AggregationExpression field must not be null");
@@ -911,6 +926,7 @@ public ThenBuilder when(AggregationExpression expression) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public ThenBuilder when(String booleanField) {
 
 				Assert.hasText(booleanField, "Boolean field name must not be null or empty");
@@ -919,6 +935,7 @@ public ThenBuilder when(String booleanField) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public OtherwiseBuilder then(Object thenValue) {
 
 				Assert.notNull(thenValue, "Then-value must not be null");
@@ -927,6 +944,7 @@ public OtherwiseBuilder then(Object thenValue) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public OtherwiseBuilder thenValueOf(String fieldReference) {
 
 				Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -935,6 +953,7 @@ public OtherwiseBuilder thenValueOf(String fieldReference) {
 			}
 
 			@Override
+			@Contract("_ -> this")
 			public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
 
 				Assert.notNull(expression, "AggregationExpression must not be null");
@@ -943,23 +962,32 @@ public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
 			}
 
 			@Override
+			@Contract("_ -> new")
 			public Cond otherwise(Object otherwiseValue) {
 
 				Assert.notNull(otherwiseValue, "Value must not be null");
+				Assert.notNull(condition, "Condition value needs to be set first");
+				Assert.notNull(thenValue, "Then value needs to be set first");
 				return new Cond(condition, thenValue, otherwiseValue);
 			}
 
 			@Override
+			@Contract("_ -> new")
 			public Cond otherwiseValueOf(String fieldReference) {
 
 				Assert.notNull(fieldReference, "FieldReference must not be null");
+				Assert.notNull(condition, "Condition value needs to be set first");
+				Assert.notNull(thenValue, "Then value needs to be set first");
 				return new Cond(condition, thenValue, Fields.field(fieldReference));
 			}
 
 			@Override
+			@Contract("_ -> new")
 			public Cond otherwiseValueOf(AggregationExpression expression) {
 
 				Assert.notNull(expression, "AggregationExpression must not be null");
+				Assert.notNull(condition, "Condition value needs to be set first");
+				Assert.notNull(thenValue, "Then value needs to be set first");
 				return new Cond(condition, thenValue, expression);
 			}
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
index aa085b2a29..35a6ad061c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
@@ -17,8 +17,8 @@
 
 import java.util.Collections;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -242,10 +242,12 @@ public DegreesToRadians convertDegreesToRadians() {
 			return DegreesToRadians.degreesToRadians(valueObject());
 		}
 
+		@SuppressWarnings("NullAway")
 		private Convert createConvert() {
 			return usesFieldRef() ? Convert.convertValueOf(fieldReference) : Convert.convertValueOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object valueObject() {
 			return usesFieldRef() ? Fields.field(fieldReference) : expression;
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
index ff6ed7e983..7bf8a231ff 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
@@ -26,7 +26,8 @@
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -848,6 +849,7 @@ public TsSecond tsSecond() {
 			return TsSecond.tsSecond(dateReference());
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object dateReference() {
 
 			if (usesFieldRef()) {
@@ -1076,6 +1078,7 @@ public static DayOfYear dayOfYear(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DayOfYear withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1148,6 +1151,7 @@ public static DayOfMonth dayOfMonth(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DayOfMonth withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1220,6 +1224,7 @@ public static DayOfWeek dayOfWeek(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DayOfWeek withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1292,6 +1297,7 @@ public static Year yearOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Year withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1364,6 +1370,7 @@ public static Month monthOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Month withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1436,6 +1443,7 @@ public static Week weekOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Week withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1508,6 +1516,7 @@ public static Hour hourOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Hour withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1580,6 +1589,7 @@ public static Minute minuteOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Minute withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1652,6 +1662,7 @@ public static Second secondOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Second withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1724,6 +1735,7 @@ public static Millisecond millisecondOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public Millisecond withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1810,6 +1822,7 @@ public static FormatBuilder dateOf(final AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DateToString withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -1824,6 +1837,7 @@ public DateToString withTimezone(Timezone timezone) {
 		 * @return new instance of {@link DateToString}.
 		 * @since 2.1
 		 */
+		@Contract("_ -> new")
 		public DateToString onNullReturn(Object value) {
 			return new DateToString(append("onNull", value));
 		}
@@ -1836,6 +1850,7 @@ public DateToString onNullReturn(Object value) {
 		 * @return new instance of {@link DateToString}.
 		 * @since 2.1
 		 */
+		@Contract("_ -> new")
 		public DateToString onNullReturnValueOf(String fieldReference) {
 			return onNullReturn(Fields.field(fieldReference));
 		}
@@ -1848,6 +1863,7 @@ public DateToString onNullReturnValueOf(String fieldReference) {
 		 * @return new instance of {@link DateToString}.
 		 * @since 2.1
 		 */
+		@Contract("_ -> new")
 		public DateToString onNullReturnValueOf(AggregationExpression expression) {
 			return onNullReturn(expression);
 		}
@@ -1973,6 +1989,7 @@ public static IsoDayOfWeek isoDayOfWeek(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public IsoDayOfWeek withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -2045,6 +2062,7 @@ public static IsoWeek isoWeekOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public IsoWeek withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -2117,6 +2135,7 @@ public static IsoWeekYear isoWeekYearOf(AggregationExpression expression) {
 		 * @since 2.1
 		 */
 		@Override
+		@Contract("_ -> new")
 		public IsoWeekYear withTimezone(Timezone timezone) {
 
 			Assert.notNull(timezone, "Timezone must not be null");
@@ -2301,6 +2320,7 @@ public static DateFromPartsWithYear dateFromParts() {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal month} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts month(Object month) {
 			return new DateFromParts(append("month", month));
 		}
@@ -2312,6 +2332,7 @@ public DateFromParts month(Object month) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts monthOf(String fieldReference) {
 			return month(Fields.field(fieldReference));
 		}
@@ -2323,6 +2344,7 @@ public DateFromParts monthOf(String fieldReference) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts monthOf(AggregationExpression expression) {
 			return month(expression);
 		}
@@ -2335,6 +2357,7 @@ public DateFromParts monthOf(AggregationExpression expression) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal day} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts day(Object day) {
 			return new DateFromParts(append("day", day));
 		}
@@ -2346,6 +2369,7 @@ public DateFromParts day(Object day) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts dayOf(String fieldReference) {
 			return day(Fields.field(fieldReference));
 		}
@@ -2357,26 +2381,31 @@ public DateFromParts dayOf(String fieldReference) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromParts dayOf(AggregationExpression expression) {
 			return day(expression);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateFromParts hour(Object hour) {
 			return new DateFromParts(append("hour", hour));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateFromParts minute(Object minute) {
 			return new DateFromParts(append("minute", minute));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateFromParts second(Object second) {
 			return new DateFromParts(append("second", second));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateFromParts millisecond(Object millisecond) {
 			return new DateFromParts(append("millisecond", millisecond));
 		}
@@ -2390,6 +2419,7 @@ public DateFromParts millisecond(Object millisecond) {
 		 * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}.
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DateFromParts withTimezone(Timezone timezone) {
 			return new DateFromParts(appendTimezone(argumentMap(), timezone));
 		}
@@ -2477,6 +2507,7 @@ public static IsoDateFromPartsWithYear dateFromParts() {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal isoWeek} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoWeek(Object isoWeek) {
 			return new IsoDateFromParts(append("isoWeek", isoWeek));
 		}
@@ -2488,6 +2519,7 @@ public IsoDateFromParts isoWeek(Object isoWeek) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoWeekOf(String fieldReference) {
 			return isoWeek(Fields.field(fieldReference));
 		}
@@ -2499,6 +2531,7 @@ public IsoDateFromParts isoWeekOf(String fieldReference) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoWeekOf(AggregationExpression expression) {
 			return isoWeek(expression);
 		}
@@ -2511,6 +2544,7 @@ public IsoDateFromParts isoWeekOf(AggregationExpression expression) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal isoWeek} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoDayOfWeek(Object day) {
 			return new IsoDateFromParts(append("isoDayOfWeek", day));
 		}
@@ -2522,6 +2556,7 @@ public IsoDateFromParts isoDayOfWeek(Object day) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoDayOfWeekOf(String fieldReference) {
 			return isoDayOfWeek(Fields.field(fieldReference));
 		}
@@ -2533,26 +2568,31 @@ public IsoDateFromParts isoDayOfWeekOf(String fieldReference) {
 		 * @return new instance.
 		 * @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public IsoDateFromParts isoDayOfWeekOf(AggregationExpression expression) {
 			return isoDayOfWeek(expression);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public IsoDateFromParts hour(Object hour) {
 			return new IsoDateFromParts(append("hour", hour));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public IsoDateFromParts minute(Object minute) {
 			return new IsoDateFromParts(append("minute", minute));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public IsoDateFromParts second(Object second) {
 			return new IsoDateFromParts(append("second", second));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public IsoDateFromParts millisecond(Object millisecond) {
 			return new IsoDateFromParts(append("millisecond", millisecond));
 		}
@@ -2566,6 +2606,7 @@ public IsoDateFromParts millisecond(Object millisecond) {
 		 * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}.
 		 */
 		@Override
+		@Contract("_ -> new")
 		public IsoDateFromParts withTimezone(Timezone timezone) {
 			return new IsoDateFromParts(appendTimezone(argumentMap(), timezone));
 		}
@@ -2676,6 +2717,7 @@ public static DateToParts datePartsOf(AggregationExpression expression) {
 		 *
 		 * @return new instance of {@link DateToParts}.
 		 */
+		@Contract("_ -> new")
 		public DateToParts iso8601() {
 			return new DateToParts(append("iso8601", true));
 		}
@@ -2689,6 +2731,7 @@ public DateToParts iso8601() {
 		 * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}.
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DateToParts withTimezone(Timezone timezone) {
 			return new DateToParts(appendTimezone(argumentMap(), timezone));
 		}
@@ -2733,6 +2776,7 @@ public static DateFromString fromString(Object value) {
 		 * @return new instance of {@link DateFromString}.
 		 * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public static DateFromString fromStringOf(String fieldReference) {
 			return fromString(Fields.field(fieldReference));
 		}
@@ -2744,6 +2788,7 @@ public static DateFromString fromStringOf(String fieldReference) {
 		 * @return new instance of {@link DateFromString}.
 		 * @throws IllegalArgumentException if given {@literal expression} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public static DateFromString fromStringOf(AggregationExpression expression) {
 			return fromString(expression);
 		}
@@ -2757,6 +2802,7 @@ public static DateFromString fromStringOf(AggregationExpression expression) {
 		 * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}.
 		 */
 		@Override
+		@Contract("_ -> new")
 		public DateFromString withTimezone(Timezone timezone) {
 			return new DateFromString(appendTimezone(argumentMap(), timezone));
 		}
@@ -2769,6 +2815,7 @@ public DateFromString withTimezone(Timezone timezone) {
 		 * @return new instance of {@link DateFromString}.
 		 * @throws IllegalArgumentException if given {@literal format} is {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public DateFromString withFormat(String format) {
 
 			Assert.notNull(format, "Format must not be null");
@@ -2838,6 +2885,7 @@ public static DateAdd addValue(Object value, String unit) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateAdd toDateOf(AggregationExpression expression) {
 			return toDate(expression);
 		}
@@ -2848,6 +2896,7 @@ public DateAdd toDateOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateAdd toDateOf(String fieldReference) {
 			return toDate(Fields.field(fieldReference));
 		}
@@ -2858,6 +2907,7 @@ public DateAdd toDateOf(String fieldReference) {
 		 * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateAdd toDate(Object dateExpression) {
 			return new DateAdd(append("startDate", dateExpression));
 		}
@@ -2868,6 +2918,7 @@ public DateAdd toDate(Object dateExpression) {
 		 * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateAdd withTimezone(Timezone timezone) {
 			return new DateAdd(appendTimezone(argumentMap(), timezone));
 		}
@@ -2935,6 +2986,7 @@ public static DateSubtract subtractValue(Object value, String unit) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link DateSubtract}.
 		 */
+		@Contract("_ -> new")
 		public DateSubtract fromDateOf(AggregationExpression expression) {
 			return fromDate(expression);
 		}
@@ -2945,6 +2997,7 @@ public DateSubtract fromDateOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link DateSubtract}.
 		 */
+		@Contract("_ -> new")
 		public DateSubtract fromDateOf(String fieldReference) {
 			return fromDate(Fields.field(fieldReference));
 		}
@@ -2955,6 +3008,7 @@ public DateSubtract fromDateOf(String fieldReference) {
 		 * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
 		 * @return new instance of {@link DateSubtract}.
 		 */
+		@Contract("_ -> new")
 		public DateSubtract fromDate(Object dateExpression) {
 			return new DateSubtract(append("startDate", dateExpression));
 		}
@@ -2965,6 +3019,7 @@ public DateSubtract fromDate(Object dateExpression) {
 		 * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
 		 * @return new instance of {@link DateSubtract}.
 		 */
+		@Contract("_ -> new")
 		public DateSubtract withTimezone(Timezone timezone) {
 			return new DateSubtract(appendTimezone(argumentMap(), timezone));
 		}
@@ -3032,6 +3087,7 @@ public static DateDiff diffValue(Object value, String unit) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateDiff toDateOf(AggregationExpression expression) {
 			return toDate(expression);
 		}
@@ -3042,6 +3098,7 @@ public DateDiff toDateOf(AggregationExpression expression) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateDiff toDateOf(String fieldReference) {
 			return toDate(Fields.field(fieldReference));
 		}
@@ -3052,6 +3109,7 @@ public DateDiff toDateOf(String fieldReference) {
 		 * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateDiff toDate(Object dateExpression) {
 			return new DateDiff(append("startDate", dateExpression));
 		}
@@ -3062,6 +3120,7 @@ public DateDiff toDate(Object dateExpression) {
 		 * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
 		 * @return new instance of {@link DateAdd}.
 		 */
+		@Contract("_ -> new")
 		public DateDiff withTimezone(Timezone timezone) {
 			return new DateDiff(appendTimezone(argumentMap(), timezone));
 		}
@@ -3073,6 +3132,7 @@ public DateDiff withTimezone(Timezone timezone) {
 		 * @param day must not be {@literal null}.
 		 * @return new instance of {@link DateDiff}.
 		 */
+		@Contract("_ -> new")
 		public DateDiff startOfWeek(Object day) {
 			return new DateDiff(append("startOfWeek", day));
 		}
@@ -3132,6 +3192,7 @@ public static DateTrunc truncateValue(Object value) {
 		 * @param unit must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc to(String unit) {
 			return new DateTrunc(append("unit", unit));
 		}
@@ -3142,6 +3203,7 @@ public DateTrunc to(String unit) {
 		 * @param unit must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc to(AggregationExpression unit) {
 			return new DateTrunc(append("unit", unit));
 		}
@@ -3152,6 +3214,7 @@ public DateTrunc to(AggregationExpression unit) {
 		 * @param day must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc startOfWeek(java.time.DayOfWeek day) {
 			return startOfWeek(day.name().toLowerCase(Locale.US));
 		}
@@ -3162,6 +3225,7 @@ public DateTrunc startOfWeek(java.time.DayOfWeek day) {
 		 * @param day must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc startOfWeek(String day) {
 			return new DateTrunc(append("startOfWeek", day));
 		}
@@ -3172,6 +3236,7 @@ public DateTrunc startOfWeek(String day) {
 		 * @param binSize must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc binSize(int binSize) {
 			return binSize((Object) binSize);
 		}
@@ -3182,6 +3247,7 @@ public DateTrunc binSize(int binSize) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc binSize(AggregationExpression expression) {
 			return binSize((Object) expression);
 		}
@@ -3192,6 +3258,7 @@ public DateTrunc binSize(AggregationExpression expression) {
 		 * @param binSize must not be {@literal null}.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc binSize(Object binSize) {
 			return new DateTrunc(append("binSize", binSize));
 		}
@@ -3202,6 +3269,7 @@ public DateTrunc binSize(Object binSize) {
 		 * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
 		 * @return new instance of {@link DateTrunc}.
 		 */
+		@Contract("_ -> new")
 		public DateTrunc withTimezone(Timezone timezone) {
 			return new DateTrunc(appendTimezone(argumentMap(), timezone));
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java
index 0da9343ddf..1a559fd26e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java
@@ -25,7 +25,8 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -60,6 +61,9 @@ public static DensifyOperationBuilder builder() {
 	@Override
 	public Document toDocument(AggregationOperationContext context) {
 
+		Assert.notNull(field, "Field must be set first");
+		Assert.notNull(range, "Range must be set first");
+
 		Document densify = new Document();
 		densify.put("field", context.getReference(field).getRaw());
 		if (!ObjectUtils.isEmpty(partitionBy)) {
@@ -149,9 +153,9 @@ default Document toDocument() {
 	public static abstract class DensifyRange implements Range {
 
 		private @Nullable DensifyUnit unit;
-		private Number step;
+		private @Nullable Number step;
 
-		public DensifyRange(DensifyUnit unit) {
+		public DensifyRange(@Nullable DensifyUnit unit) {
 			this.unit = unit;
 		}
 
@@ -172,6 +176,7 @@ public Document toDocument(AggregationOperationContext ctx) {
 		 * @param step must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public DensifyRange incrementBy(Number step) {
 			this.step = step;
 			return this;
@@ -183,6 +188,7 @@ public DensifyRange incrementBy(Number step) {
 		 * @param step must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_, _ -> this")
 		public DensifyRange incrementBy(Number step, DensifyUnit unit) {
 			this.step = step;
 			return unit(unit);
@@ -194,6 +200,7 @@ public DensifyRange incrementBy(Number step, DensifyUnit unit) {
 		 * @param unit
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public DensifyRange unit(DensifyUnit unit) {
 
 			this.unit = unit;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java
index 7f260c3785..431215e852 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java
@@ -22,6 +22,7 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
 import org.springframework.util.Assert;
@@ -105,7 +106,11 @@ private static Document toSetEntry(Entry<Object, Object> entry, AggregationOpera
 		return new Document(field, value);
 	}
 
-	private static Object computeValue(Object value, AggregationOperationContext context) {
+	private static @Nullable Object computeValue(@Nullable Object value, AggregationOperationContext context) {
+
+		if(value == null) {
+			return value;
+		}
 
 		if (value instanceof Field field) {
 			return context.getReference(field).toString();
@@ -154,7 +159,7 @@ static class ExpressionProjection {
 			this.params = parameters.clone();
 		}
 
-		Object toExpression(AggregationOperationContext context) {
+		@Nullable Object toExpression(AggregationOperationContext context) {
 			return TRANSFORMER.transform(expression, context, params);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java
index ff63ad834d..a0cd1d056a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java
@@ -18,6 +18,7 @@
 import java.util.Collections;
 
 import org.bson.Document;
+import org.springframework.lang.Contract;
 
 /**
  * Gateway to {@literal document expressions} such as {@literal $rank, $documentNumber, etc.}
@@ -190,6 +191,7 @@ public static Shift shift(AggregationExpression expression) {
 		 * @param shiftBy value to add to the current position.
 		 * @return new instance of {@link Shift}.
 		 */
+		@Contract("_ -> new")
 		public Shift by(int shiftBy) {
 			return new Shift(append("by", shiftBy));
 		}
@@ -200,6 +202,7 @@ public Shift by(int shiftBy) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Shift}.
 		 */
+		@Contract("_ -> new")
 		public Shift defaultTo(Object value) {
 			return new Shift(append("default", value));
 		}
@@ -210,6 +213,7 @@ public Shift defaultTo(Object value) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Shift}.
 		 */
+		@Contract("_ -> new")
 		public Shift defaultToValueOf(AggregationExpression expression) {
 			return defaultTo(expression);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
index 56f20dde17..dfdc2d620c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
@@ -16,6 +16,7 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
 import org.springframework.util.Assert;
 
@@ -50,8 +51,8 @@ public static EvaluationOperatorFactory valueOf(AggregationExpression expression
 
 	public static class EvaluationOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link EvaluationOperatorFactory} for given {@literal fieldReference}.
@@ -82,6 +83,7 @@ public EvaluationOperatorFactory(AggregationExpression expression) {
 		 *
 		 * @return new instance of {@link Expr}.
 		 */
+		@SuppressWarnings("NullAway")
 		public Expr expr() {
 			return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression);
 		}
@@ -91,6 +93,7 @@ public Expr expr() {
 		 *
 		 * @return new instance of {@link Expr}.
 		 */
+		@SuppressWarnings("NullAway")
 		public LastObservationCarriedForward locf() {
 			return usesFieldRef() ? LastObservationCarriedForward.locfValueOf(fieldReference)
 					: LastObservationCarriedForward.locfValueOf(expression);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java
index 458bc43437..703d5d5f06 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java
@@ -21,8 +21,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.CompositeIterator;
 import org.springframework.util.ObjectUtils;
@@ -154,8 +154,7 @@ public ExposedFields and(ExposedField field) {
 	 * @param name must not be {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public ExposedField getField(String name) {
+	public @Nullable ExposedField getField(String name) {
 
 		for (ExposedField field : this) {
 			if (field.canBeReferredToBy(name)) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
index 131fa8a845..1639a54d48 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
@@ -17,11 +17,10 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -119,8 +118,7 @@ private FieldReference getReference(@Nullable Field field, String name) {
 	 * @param name must not be {@literal null}.
 	 * @return the resolved reference or {@literal null}.
 	 */
-	@Nullable
-	protected FieldReference resolveExposedField(@Nullable Field field, String name) {
+	protected @Nullable FieldReference resolveExposedField(@Nullable Field field, String name) {
 
 		ExposedField exposedField = exposedFields.getField(name);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java
index f5c73dd09c..d1ca95f659 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java
@@ -23,6 +23,7 @@
 import org.bson.Document;
 import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Output;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
index 83fc7c2b87..7f574a850e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
@@ -23,8 +23,9 @@
 import java.util.List;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -145,14 +146,17 @@ private Fields(Fields existing, Field tail) {
 	 * @param name must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> new")
 	public Fields and(String name) {
 		return and(new AggregationField(name));
 	}
 
+	@Contract("_ -> new")
 	public Fields and(String name, String target) {
 		return and(new AggregationField(name, target));
 	}
 
+	@Contract("_ -> new")
 	public Fields and(Field field) {
 		return new Fields(this, field);
 	}
@@ -172,8 +176,7 @@ public int size() {
 		return fields.size();
 	}
 
-	@Nullable
-	public Field getField(String name) {
+	public @Nullable Field getField(String name) {
 
 		for (Field field : fields) {
 			if (field.getName().equals(name)) {
@@ -206,7 +209,7 @@ static class AggregationField implements Field {
 
 		private final String raw;
 		private final String name;
-		private final String target;
+		private final @Nullable String target;
 
 		/**
 		 * Creates an aggregation field with the given {@code name}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java
index f4a5fb4498..bcfc64f2b4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java
@@ -19,8 +19,9 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.NearQuery;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -80,6 +81,7 @@ private GeoNearOperation(NearQuery nearQuery, String distanceField, @Nullable St
 	 * @return new instance of {@link GeoNearOperation}.
 	 * @since 2.1
 	 */
+	@Contract("_ -> new")
 	public GeoNearOperation useIndex(String key) {
 		return new GeoNearOperation(nearQuery, distanceField, key);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java
index 72a917c599..ad1f8ae643 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java
@@ -21,10 +21,11 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -35,14 +36,16 @@
  * We recommend to use the static factory method {@link Aggregation#graphLookup(String)} instead of creating instances
  * of this class directly.
  *
- * @see <a href="https://docs.mongodb.org/manual/reference/aggregation/graphLookup/">https://docs.mongodb.org/manual/reference/aggregation/graphLookup/</a>
+ * @see <a href=
+ *      "https://docs.mongodb.org/manual/reference/aggregation/graphLookup/">https://docs.mongodb.org/manual/reference/aggregation/graphLookup/</a>
  * @author Mark Paluch
  * @author Christoph Strobl
  * @since 1.10
  */
 public class GraphLookupOperation implements InheritsFieldsAggregationOperation {
 
-	private static final Set<Class<?>> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class, Field.class, Document.class);
+	private static final Set<Class<?>> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class,
+			Field.class, Document.class);
 
 	private final String from;
 	private final List<Object> startWith;
@@ -126,7 +129,7 @@ public ExposedFields getFields() {
 
 		List<ExposedField> fields = new ArrayList<>(2);
 		fields.add(new ExposedField(as, true));
-		if(depthField != null) {
+		if (depthField != null) {
 			fields.add(new ExposedField(depthField, true));
 		}
 		return ExposedFields.from(fields.toArray(new ExposedField[0]));
@@ -217,10 +220,11 @@ static final class GraphLookupOperationFromBuilder
 			implements FromBuilder, StartWithBuilder, ConnectFromBuilder, ConnectToBuilder {
 
 		private @Nullable String from;
-		private @Nullable List<? extends Object> startWith;
+		private @Nullable List<?> startWith;
 		private @Nullable String connectFrom;
 
 		@Override
+		@Contract("_ -> this")
 		public StartWithBuilder from(String collectionName) {
 
 			Assert.hasText(collectionName, "CollectionName must not be null or empty");
@@ -230,6 +234,7 @@ public StartWithBuilder from(String collectionName) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public ConnectFromBuilder startWith(String... fieldReferences) {
 
 			Assert.notNull(fieldReferences, "FieldReferences must not be null");
@@ -246,6 +251,7 @@ public ConnectFromBuilder startWith(String... fieldReferences) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public ConnectFromBuilder startWith(AggregationExpression... expressions) {
 
 			Assert.notNull(expressions, "AggregationExpressions must not be null");
@@ -256,6 +262,7 @@ public ConnectFromBuilder startWith(AggregationExpression... expressions) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public ConnectFromBuilder startWith(Object... expressions) {
 
 			Assert.notNull(expressions, "Expressions must not be null");
@@ -297,6 +304,7 @@ private void assertStartWithType(Object expression) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public ConnectToBuilder connectFrom(String fieldName) {
 
 			Assert.hasText(fieldName, "ConnectFrom must not be null or empty");
@@ -306,10 +314,14 @@ public ConnectToBuilder connectFrom(String fieldName) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public GraphLookupOperationBuilder connectTo(String fieldName) {
 
 			Assert.hasText(fieldName, "ConnectTo must not be null or empty");
 
+			Assert.notNull(from, "From must not be null");
+			Assert.notNull(startWith, "startWith must ne set first");
+			Assert.notNull(connectFrom, "ConnectFrom must be set first");
 			return new GraphLookupOperationBuilder(from, startWith, connectFrom, fieldName);
 		}
 	}
@@ -327,8 +339,7 @@ public static final class GraphLookupOperationBuilder {
 		private @Nullable Field depthField;
 		private @Nullable CriteriaDefinition restrictSearchWithMatch;
 
-		private GraphLookupOperationBuilder(String from, List<? extends Object> startWith, String connectFrom,
-				String connectTo) {
+		private GraphLookupOperationBuilder(String from, List<?> startWith, String connectFrom, String connectTo) {
 
 			this.from = from;
 			this.startWith = new ArrayList<>(startWith);
@@ -342,6 +353,7 @@ private GraphLookupOperationBuilder(String from, List<? extends Object> startWit
 		 * @param numberOfRecursions must be greater or equal to zero.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GraphLookupOperationBuilder maxDepth(long numberOfRecursions) {
 
 			Assert.isTrue(numberOfRecursions >= 0, "Max depth must be >= 0");
@@ -356,6 +368,7 @@ public GraphLookupOperationBuilder maxDepth(long numberOfRecursions) {
 		 * @param fieldName must not be {@literal null} or empty.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GraphLookupOperationBuilder depthField(String fieldName) {
 
 			Assert.hasText(fieldName, "Depth field name must not be null or empty");
@@ -370,6 +383,7 @@ public GraphLookupOperationBuilder depthField(String fieldName) {
 		 * @param criteriaDefinition must not be {@literal null}.
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public GraphLookupOperationBuilder restrict(CriteriaDefinition criteriaDefinition) {
 
 			Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null");
@@ -385,6 +399,7 @@ public GraphLookupOperationBuilder restrict(CriteriaDefinition criteriaDefinitio
 		 * @param fieldName must not be {@literal null} or empty.
 		 * @return the final {@link GraphLookupOperation}.
 		 */
+		@Contract("_ -> new")
 		public GraphLookupOperation as(String fieldName) {
 
 			Assert.hasText(fieldName, "As field name must not be null or empty");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java
index 10d58a7682..b6d36f1baf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java
@@ -20,10 +20,10 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
 import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -497,6 +497,7 @@ public Operation withAlias(String key) {
 		}
 
 		public ExposedField asField() {
+			Assert.notNull(key, "Key must be set first");
 			return new ExposedField(key, true);
 		}
 
@@ -506,10 +507,12 @@ public Document toDocument(AggregationOperationContext context) {
 			if(op == null && value instanceof Document) {
 				return new Document(key, value);
 			}
+
+			Assert.notNull(op, "Operation keyword must be set");
 			return new Document(key, new Document(op.toString(), value));
 		}
 
-		public Object getValue(AggregationOperationContext context) {
+		public @Nullable Object getValue(AggregationOperationContext context) {
 
 			if (reference == null) {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java
index ca6a2e2754..739b7c52a9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java
@@ -16,8 +16,8 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link ExposedFieldsAggregationOperationContext} that inherits fields from its parent
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java
index 282ffbd9e0..a0e0cc03cd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java
@@ -22,6 +22,7 @@
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
 import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let;
 import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
+import org.springframework.lang.Contract;
 import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
@@ -281,6 +282,7 @@ public static FromBuilder newBuilder() {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public LocalFieldBuilder from(String name) {
 
 			Assert.hasText(name, "'From' must not be null or empty");
@@ -289,6 +291,7 @@ public LocalFieldBuilder from(String name) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public AsBuilder foreignField(String name) {
 
 			Assert.hasText(name, "'ForeignField' must not be null or empty");
@@ -297,6 +300,7 @@ public AsBuilder foreignField(String name) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public ForeignFieldBuilder localField(String name) {
 
 			Assert.hasText(name, "'LocalField' must not be null or empty");
@@ -305,6 +309,7 @@ public ForeignFieldBuilder localField(String name) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public PipelineBuilder let(Let let) {
 
 			Assert.notNull(let, "Let must not be null");
@@ -313,6 +318,7 @@ public PipelineBuilder let(Let let) {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public AsBuilder pipeline(AggregationPipeline pipeline) {
 
 			Assert.notNull(pipeline, "Pipeline must not be null");
@@ -321,9 +327,11 @@ public AsBuilder pipeline(AggregationPipeline pipeline) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public LookupOperation as(String name) {
 
 			Assert.hasText(name, "'As' must not be null or empty");
+			Assert.notNull(from, "From must be set first");
 			as = new ExposedField(Fields.field(name), true);
 			return new LookupOperation(from, localField, foreignField, let, pipeline, as);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
index da1dbfc027..5f736b55a0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
@@ -17,6 +17,7 @@
 
 import org.bson.Document;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
 import org.springframework.util.Assert;
 
@@ -37,8 +38,8 @@
  */
 public class MatchOperation implements AggregationOperation {
 
-	private final CriteriaDefinition criteriaDefinition;
-	private final AggregationExpression expression;
+	private final @Nullable CriteriaDefinition criteriaDefinition;
+	private final @Nullable AggregationExpression expression;
 
 	/**
 	 * Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}.
@@ -68,6 +69,7 @@ public MatchOperation(AggregationExpression expression) {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public Document toDocument(AggregationOperationContext context) {
 
 		return new Document(getOperator(),
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java
index 314f83fc7c..bda9a3330d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java
@@ -22,10 +22,11 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
 import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -415,7 +416,7 @@ public Document toDocument(AggregationOperationContext context) {
 	 */
 	public static class MergeOperationBuilder {
 
-		private String collection;
+		private @Nullable String collection;
 		private @Nullable String database;
 		private UniqueMergeId id = UniqueMergeId.id();
 		private @Nullable Let let;
@@ -430,6 +431,7 @@ public MergeOperationBuilder() {}
 		 * @param collection must not be {@literal null} nor empty.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder intoCollection(String collection) {
 
 			Assert.hasText(collection, "Collection must not be null nor empty");
@@ -444,6 +446,7 @@ public MergeOperationBuilder intoCollection(String collection) {
 		 * @param database must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder inDatabase(String database) {
 
 			this.database = database;
@@ -456,6 +459,7 @@ public MergeOperationBuilder inDatabase(String database) {
 		 * @param into must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder into(MergeOperationTarget into) {
 
 			this.database = into.database;
@@ -469,6 +473,7 @@ public MergeOperationBuilder into(MergeOperationTarget into) {
 		 * @param target must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder target(MergeOperationTarget target) {
 			return into(target);
 		}
@@ -482,6 +487,7 @@ public MergeOperationBuilder target(MergeOperationTarget target) {
 		 * @param fields must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder on(String... fields) {
 			return id(UniqueMergeId.ofIdFields(fields));
 		}
@@ -493,6 +499,7 @@ public MergeOperationBuilder on(String... fields) {
 		 * @param id must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder id(UniqueMergeId id) {
 
 			this.id = id;
@@ -506,6 +513,7 @@ public MergeOperationBuilder id(UniqueMergeId id) {
 		 * @param let the variable expressions
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder let(Let let) {
 
 			this.let = let;
@@ -519,6 +527,7 @@ public MergeOperationBuilder let(Let let) {
 		 * @param let the variable expressions
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder exposeVariablesOf(Let let) {
 			return let(let);
 		}
@@ -529,6 +538,7 @@ public MergeOperationBuilder exposeVariablesOf(Let let) {
 		 * @param whenMatched must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder whenMatched(WhenDocumentsMatch whenMatched) {
 
 			this.whenMatched = whenMatched;
@@ -541,6 +551,7 @@ public MergeOperationBuilder whenMatched(WhenDocumentsMatch whenMatched) {
 		 * @param whenMatched must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder whenDocumentsMatch(WhenDocumentsMatch whenMatched) {
 			return whenMatched(whenMatched);
 		}
@@ -551,6 +562,7 @@ public MergeOperationBuilder whenDocumentsMatch(WhenDocumentsMatch whenMatched)
 		 * @param aggregation must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder whenDocumentsMatchApply(Aggregation aggregation) {
 			return whenMatched(WhenDocumentsMatch.updateWith(aggregation));
 		}
@@ -561,6 +573,7 @@ public MergeOperationBuilder whenDocumentsMatchApply(Aggregation aggregation) {
 		 * @param whenNotMatched must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder whenNotMatched(WhenDocumentsDontMatch whenNotMatched) {
 
 			this.whenNotMatched = whenNotMatched;
@@ -573,6 +586,7 @@ public MergeOperationBuilder whenNotMatched(WhenDocumentsDontMatch whenNotMatche
 		 * @param whenNotMatched must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MergeOperationBuilder whenDocumentsDontMatch(WhenDocumentsDontMatch whenNotMatched) {
 			return whenNotMatched(whenNotMatched);
 		}
@@ -580,7 +594,10 @@ public MergeOperationBuilder whenDocumentsDontMatch(WhenDocumentsDontMatch whenN
 		/**
 		 * @return new instance of {@link MergeOperation}.
 		 */
+		@Contract("-> new")
 		public MergeOperation build() {
+			
+			Assert.notNull(collection, "Collection must not be null");
 			return new MergeOperation(new MergeOperationTarget(database, collection), id, let, whenMatched, whenNotMatched);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
index c553a7be02..a5124320f6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
@@ -20,6 +20,7 @@
 import org.bson.Document;
 
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExpressionFieldReference;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
 import org.springframework.util.Assert;
@@ -57,7 +58,7 @@ public Document getMappedObject(Document document) {
 	}
 
 	@Override
-	public Document getMappedObject(Document document, Class<?> type) {
+	public Document getMappedObject(Document document, @Nullable Class<?> type) {
 		return delegate.getMappedObject(document, type);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java
index 25189241b7..4e21ab7bde 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java
@@ -20,6 +20,7 @@
 import java.util.Collections;
 
 import org.bson.Document;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -277,7 +278,7 @@ public Document toDocument(Object value, AggregationOperationContext context) {
 			return super.toDocument(potentiallyExtractSingleValue(value), context);
 		}
 
-		@SuppressWarnings("unchecked")
+		@SuppressWarnings("NullAway")
 		private Object potentiallyExtractSingleValue(Object value) {
 
 			if (value instanceof Collection<?> collection && collection.size() == 1) {
@@ -385,6 +386,7 @@ public static GetField getField(Field field) {
 		 * @param fieldRef must not be {@literal null}.
 		 * @return new instance of {@link GetField}.
 		 */
+		@Contract("_ -> new")
 		public GetField of(String fieldRef) {
 			return of(Fields.field(fieldRef));
 		}
@@ -396,6 +398,7 @@ public GetField of(String fieldRef) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link GetField}.
 		 */
+		@Contract("_ -> new")
 		public GetField of(AggregationExpression expression) {
 			return of((Object) expression);
 		}
@@ -459,6 +462,7 @@ public static SetField field(Field field) {
 		 * @param fieldRef must not be {@literal null}.
 		 * @return new instance of {@link GetField}.
 		 */
+		@Contract("_ -> new")
 		public SetField input(String fieldRef) {
 			return input(Fields.field(fieldRef));
 		}
@@ -470,6 +474,7 @@ public SetField input(String fieldRef) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link SetField}.
 		 */
+		@Contract("_ -> new")
 		public SetField input(AggregationExpression expression) {
 			return input((Object) expression);
 		}
@@ -481,6 +486,7 @@ public SetField input(AggregationExpression expression) {
 		 * @param fieldRef must not be {@literal null}.
 		 * @return new instance of {@link SetField}.
 		 */
+		@Contract("_ -> new")
 		private SetField input(Object fieldRef) {
 			return new SetField(append("input", fieldRef));
 		}
@@ -491,6 +497,7 @@ private SetField input(Object fieldRef) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link SetField}.
 		 */
+		@Contract("_ -> new")
 		public SetField toValueOf(String fieldReference) {
 			return toValue(Fields.field(fieldReference));
 		}
@@ -502,6 +509,7 @@ public SetField toValueOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link SetField}.
 		 */
+		@Contract("_ -> new")
 		public SetField toValueOf(AggregationExpression expression) {
 			return toValue(expression);
 		}
@@ -512,6 +520,7 @@ public SetField toValueOf(AggregationExpression expression) {
 		 * @param value
 		 * @return new instance of {@link SetField}.
 		 */
+		@Contract("_ -> new")
 		public SetField toValue(Object value) {
 			return new SetField(append("value", value));
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java
index 51520f0868..7dbed3a855 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java
@@ -16,8 +16,9 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -73,6 +74,7 @@ private OutOperation(@Nullable String databaseName, String collectionName, @Null
 	 * @return new instance of {@link OutOperation}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> new")
 	public OutOperation in(@Nullable String database) {
 		return new OutOperation(database, collectionName, uniqueKey, mode);
 	}
@@ -102,6 +104,7 @@ public OutOperation in(@Nullable String database) {
 	 * @return new instance of {@link OutOperation}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> new")
 	public OutOperation uniqueKey(@Nullable String key) {
 
 		Document uniqueKey = key == null ? null : BsonUtils.toDocumentOrElse(key, it -> new Document(it, 1));
@@ -126,6 +129,7 @@ public OutOperation uniqueKey(@Nullable String key) {
 	 * @return new instance of {@link OutOperation}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> new")
 	public OutOperation uniqueKeyOf(Iterable<String> fields) {
 
 		Assert.notNull(fields, "Fields must not be null");
@@ -144,6 +148,7 @@ public OutOperation uniqueKeyOf(Iterable<String> fields) {
 	 * @return new instance of {@link OutOperation}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> new")
 	public OutOperation mode(OutMode mode) {
 
 		Assert.notNull(mode, "Mode must not be null");
@@ -158,6 +163,7 @@ public OutOperation mode(OutMode mode) {
 	 * @see OutMode#REPLACE_COLLECTION
 	 * @since 2.2
 	 */
+	@Contract("-> new")
 	public OutOperation replaceCollection() {
 		return mode(OutMode.REPLACE_COLLECTION);
 	}
@@ -170,6 +176,7 @@ public OutOperation replaceCollection() {
 	 * @see OutMode#REPLACE
 	 * @since 2.2
 	 */
+	@Contract("-> new")
 	public OutOperation replaceDocuments() {
 		return mode(OutMode.REPLACE);
 	}
@@ -182,6 +189,7 @@ public OutOperation replaceDocuments() {
 	 * @see OutMode#INSERT
 	 * @since 2.2
 	 */
+	@Contract("-> new")
 	public OutOperation insertDocuments() {
 		return mode(OutMode.INSERT);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
index 9524171fed..54ed40b035 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
@@ -25,8 +25,8 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link AggregationOperationContext} implementation prefixing non-command keys on root level with the given prefix.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
index 35db2214f5..af7cf5bfb2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
@@ -23,13 +23,14 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
 import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
 import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -149,6 +150,7 @@ public ProjectionOperationBuilder and(AggregationExpression expression) {
 	 * @param fieldNames must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> new")
 	public ProjectionOperation andExclude(String... fieldNames) {
 
 		List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fieldNames), false);
@@ -161,6 +163,7 @@ public ProjectionOperation andExclude(String... fieldNames) {
 	 * @param fieldNames must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> new")
 	public ProjectionOperation andInclude(String... fieldNames) {
 
 		List<FieldProjection> projections = FieldProjection.from(Fields.fields(fieldNames), true);
@@ -173,6 +176,7 @@ public ProjectionOperation andInclude(String... fieldNames) {
 	 * @param fields must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> new")
 	public ProjectionOperation andInclude(Fields fields) {
 		return new ProjectionOperation(this.projections, FieldProjection.from(fields, true));
 	}
@@ -185,6 +189,7 @@ public ProjectionOperation andInclude(Fields fields) {
 	 * @return new instance of {@link ProjectionOperation}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> new")
 	public ProjectionOperation asArray(String name) {
 
 		return new ProjectionOperation(Collections.emptyList(),
@@ -402,7 +407,7 @@ public Document toDocument(AggregationOperationContext context) {
 				return new Document(getExposedField().getName(), toMongoExpression(context, expression, params));
 			}
 
-			protected static Object toMongoExpression(AggregationOperationContext context, String expression,
+			protected static @Nullable Object toMongoExpression(AggregationOperationContext context, String expression,
 					Object[] params) {
 				return TRANSFORMER.transform(expression, context, params);
 			}
@@ -1780,6 +1785,7 @@ public ArrayProjectionOperationBuilder(ProjectionOperation target) {
 		 * @param expression
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public ArrayProjectionOperationBuilder and(AggregationExpression expression) {
 
 			Assert.notNull(expression, "AggregationExpression must not be null");
@@ -1794,6 +1800,7 @@ public ArrayProjectionOperationBuilder and(AggregationExpression expression) {
 		 * @param field
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public ArrayProjectionOperationBuilder and(Field field) {
 
 			Assert.notNull(field, "Field must not be null");
@@ -1808,6 +1815,7 @@ public ArrayProjectionOperationBuilder and(Field field) {
 		 * @param value
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public ArrayProjectionOperationBuilder and(Object value) {
 
 			this.projections.add(value);
@@ -1820,6 +1828,7 @@ public ArrayProjectionOperationBuilder and(Object value) {
 		 * @param name The target property name. Must not be {@literal null}.
 		 * @return new instance of {@link ArrayProjectionOperationBuilder}.
 		 */
+		@Contract("_ -> new")
 		public ProjectionOperation as(String name) {
 
 			return new ProjectionOperation(target.projections,
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java
index a370016356..5f16fcfc16 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java
@@ -16,8 +16,10 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -33,7 +35,8 @@
  * </pre>
  *
  * @author Christoph Strobl
- * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/redact/">https://docs.mongodb.com/manual/reference/operator/aggregation/redact/</a>
+ * @see <a href=
+ *      "https://docs.mongodb.com/manual/reference/operator/aggregation/redact/">https://docs.mongodb.com/manual/reference/operator/aggregation/redact/</a>
  * @since 3.0
  */
 public class RedactOperation implements AggregationOperation {
@@ -94,9 +97,9 @@ public static RedactOperationBuilder builder() {
 	 */
 	public static class RedactOperationBuilder {
 
-		private Object when;
-		private Object then;
-		private Object otherwise;
+		private @Nullable Object when;
+		private @Nullable Object then;
+		private @Nullable Object otherwise;
 
 		private RedactOperationBuilder() {
 
@@ -108,6 +111,7 @@ private RedactOperationBuilder() {
 		 * @param criteria must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RedactOperationBuilder when(CriteriaDefinition criteria) {
 
 			this.when = criteria;
@@ -120,6 +124,7 @@ public RedactOperationBuilder when(CriteriaDefinition criteria) {
 		 * @param condition must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RedactOperationBuilder when(AggregationExpression condition) {
 
 			this.when = condition;
@@ -132,6 +137,7 @@ public RedactOperationBuilder when(AggregationExpression condition) {
 		 * @param condition must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RedactOperationBuilder when(Document condition) {
 
 			this.when = condition;
@@ -143,6 +149,7 @@ public RedactOperationBuilder when(Document condition) {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder thenDescend() {
 			return then(DESCEND);
 		}
@@ -152,6 +159,7 @@ public RedactOperationBuilder thenDescend() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder thenKeep() {
 			return then(KEEP);
 		}
@@ -161,6 +169,7 @@ public RedactOperationBuilder thenKeep() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder thenPrune() {
 			return then(PRUNE);
 		}
@@ -172,6 +181,7 @@ public RedactOperationBuilder thenPrune() {
 		 * @param then must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RedactOperationBuilder then(Object then) {
 
 			this.then = then;
@@ -183,6 +193,7 @@ public RedactOperationBuilder then(Object then) {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder otherwiseDescend() {
 			return otherwise(DESCEND);
 		}
@@ -192,6 +203,7 @@ public RedactOperationBuilder otherwiseDescend() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder otherwiseKeep() {
 			return otherwise(KEEP);
 		}
@@ -201,6 +213,7 @@ public RedactOperationBuilder otherwiseKeep() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RedactOperationBuilder otherwisePrune() {
 			return otherwise(PRUNE);
 		}
@@ -212,6 +225,7 @@ public RedactOperationBuilder otherwisePrune() {
 		 * @param otherwise must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RedactOperationBuilder otherwise(Object otherwise) {
 			this.otherwise = otherwise;
 			return this;
@@ -220,7 +234,12 @@ public RedactOperationBuilder otherwise(Object otherwise) {
 		/**
 		 * @return new instance of {@link RedactOperation}.
 		 */
+		@Contract("-> new")
 		public RedactOperation build() {
+
+			Assert.notNull(then, "Then must be set first");
+			Assert.notNull(otherwise, "Otherwise must be set first");
+
 			return new RedactOperation(when().then(then).otherwise(otherwise));
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
index 130182a001..ec306eb6c5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
@@ -23,6 +23,7 @@
 import org.bson.Document;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
 import org.springframework.expression.spel.ast.Projection;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -457,6 +458,7 @@ public DocumentContributor(Object value) {
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Document toDocument(AggregationOperationContext context) {
 
 			Document document = new Document("$set", value);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java
index 9eab041e88..ed615d9863 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java
@@ -22,15 +22,15 @@
 import java.util.List;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorBuilder;
 import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorInitBuilder;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
 /**
- * Gateway to {@literal $function} and {@literal $accumulator} aggregation operations.
- * <br />
+ * Gateway to {@literal $function} and {@literal $accumulator} aggregation operations. <br />
  * Using {@link ScriptOperators} as part of the {@link Aggregation} requires MongoDB server to have
  * <a href="https://docs.mongodb.com/master/core/server-side-javascript/">server-side JavaScript</a> execution
  * <a href="https://docs.mongodb.com/master/reference/configuration-options/#security.javascriptEnabled">enabled</a>.
@@ -53,8 +53,8 @@ public static Function function(String body) {
 	}
 
 	/**
-	 * Create a custom <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator operator</a>
-	 * in Javascript.
+	 * Create a custom <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator
+	 * operator</a> in Javascript.
 	 *
 	 * @return new instance of {@link AccumulatorInitBuilder}.
 	 */
@@ -74,8 +74,7 @@ public static AccumulatorInitBuilder accumulatorBuilder() {
 	 *     lang: "js"
 	 *   }
 	 * }
-	 * </code>
-	 * <br />
+	 * </code> <br />
 	 * {@link Function} cannot be used as part of {@link org.springframework.data.mongodb.core.schema.MongoJsonSchema
 	 * schema} validation query expression. <br />
 	 * <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a>
@@ -150,10 +149,12 @@ List<Object> getArgs() {
 			return get(Fields.ARGS.toString());
 		}
 
+		@Nullable
 		String getBody() {
 			return get(Fields.BODY.toString());
 		}
 
+		@Nullable
 		String getLang() {
 			return get(Fields.LANG.toString());
 		}
@@ -178,8 +179,7 @@ public String toString() {
 	 * {@link Accumulator} defines a custom aggregation
 	 * <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator operator</a>,
 	 * one that maintains its state (e.g. totals, maximums, minimums, and related data) as documents progress through the
-	 * pipeline, in JavaScript.
-	 * <br />
+	 * pipeline, in JavaScript. <br />
 	 * <code class="java">
 	 * {
 	 *   $accumulator: {
@@ -192,8 +192,7 @@ public String toString() {
 	 *     lang: "js"
 	 *   }
 	 * }
-	 * </code>
-	 * <br />
+	 * </code> <br />
 	 * {@link Accumulator} can be used as part of {@link GroupOperation $group}, {@link BucketOperation $bucket} and
 	 * {@link BucketAutoOperation $bucketAuto} pipeline stages. <br />
 	 * <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a>
@@ -240,8 +239,7 @@ public interface AccumulatorInitBuilder {
 
 			/**
 			 * Define the {@code init} {@link Function} for the {@link Accumulator accumulators} initial state. The function
-			 * receives its arguments from the {@link Function#args(Object...) initArgs} array expression.
-			 * <br />
+			 * receives its arguments from the {@link Function#args(Object...) initArgs} array expression. <br />
 			 * <code class="java">
 			 * function(initArg1, initArg2, ...) {
 			 *   ...
@@ -253,13 +251,16 @@ public interface AccumulatorInitBuilder {
 			 * @return this.
 			 */
 			default AccumulatorAccumulateBuilder init(Function function) {
-				return init(function.getBody()).initArgs(function.getArgs());
+
+				Assert.notNull(function.getBody(), "Function body must not be null");
+
+				List<Object> args = function.getArgs();
+				return init(function.getBody()).initArgs(args != null ? args : List.of());
 			}
 
 			/**
 			 * Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives
-			 * its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression.
-			 * <br />
+			 * its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression. <br />
 			 * <code class="java">
 			 * function(initArg1, initArg2, ...) {
 			 *   ...
@@ -307,8 +308,7 @@ public interface AccumulatorAccumulateBuilder {
 			/**
 			 * Set the {@code accumulate} {@link Function} that updates the state for each document. The functions first
 			 * argument is the current {@code state}, additional arguments can be defined via {@link Function#args(Object...)
-			 * accumulateArgs}.
-			 * <br />
+			 * accumulateArgs}. <br />
 			 * <code class="java">
 			 * function(state, accumArg1, accumArg2, ...) {
 			 *   ...
@@ -320,14 +320,17 @@ public interface AccumulatorAccumulateBuilder {
 			 * @return this.
 			 */
 			default AccumulatorMergeBuilder accumulate(Function function) {
-				return accumulate(function.getBody()).accumulateArgs(function.getArgs());
+
+				Assert.notNull(function.getBody(), "Function body must not be null");
+
+				List<Object> args = function.getArgs();
+				return accumulate(function.getBody()).accumulateArgs(args != null ? args : List.of());
 			}
 
 			/**
 			 * Set the {@code accumulate} function that updates the state for each document. The functions first argument is
 			 * the current {@code state}, additional arguments can be defined via
-			 * {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}.
-			 * <br />
+			 * {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}. <br />
 			 * <code class="java">
 			 * function(state, accumArg1, accumArg2, ...) {
 			 *   ...
@@ -369,8 +372,7 @@ public interface AccumulatorMergeBuilder {
 			/**
 			 * Set the {@code merge} function used to merge two internal states. <br />
 			 * This might be required because the operation is run on a sharded cluster or when the operator exceeds its
-			 * memory limit.
-			 * <br />
+			 * memory limit. <br />
 			 * <code class="java">
 			 * function(state1, state2) {
 			 *   ...
@@ -388,8 +390,7 @@ public interface AccumulatorFinalizeBuilder {
 
 			/**
 			 * Set the {@code finalize} function used to update the result of the accumulation when all documents have been
-			 * processed.
-			 * <br />
+			 * processed. <br />
 			 * <code class="java">
 			 * function(state) {
 			 *   ...
@@ -414,18 +415,17 @@ static class AccumulatorBuilder
 				implements AccumulatorInitBuilder, AccumulatorInitArgsBuilder, AccumulatorAccumulateBuilder,
 				AccumulatorAccumulateArgsBuilder, AccumulatorMergeBuilder, AccumulatorFinalizeBuilder {
 
-			private List<Object> initArgs;
-			private String initFunction;
-			private List<Object> accumulateArgs;
-			private String accumulateFunction;
-			private String mergeFunction;
-			private String finalizeFunction;
+			private @Nullable List<Object> initArgs;
+			private @Nullable String initFunction;
+			private @Nullable List<Object> accumulateArgs;
+			private @Nullable String accumulateFunction;
+			private @Nullable String mergeFunction;
+			private @Nullable String finalizeFunction;
 			private String lang = "js";
 
 			/**
 			 * Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives
-			 * its arguments from the {@link #initArgs(Object...)} array expression.
-			 * <br />
+			 * its arguments from the {@link #initArgs(Object...)} array expression. <br />
 			 * <code class="java">
 			 * function(initArg1, initArg2, ...) {
 			 *   ...
@@ -437,6 +437,7 @@ static class AccumulatorBuilder
 			 * @return this.
 			 */
 			@Override
+			@Contract("_ -> this")
 			public AccumulatorBuilder init(String function) {
 
 				this.initFunction = function;
@@ -450,6 +451,7 @@ public AccumulatorBuilder init(String function) {
 			 * @return this.
 			 */
 			@Override
+			@Contract("_ -> this")
 			public AccumulatorBuilder initArgs(List<Object> args) {
 
 				Assert.notNull(args, "Args must not be null");
@@ -460,8 +462,7 @@ public AccumulatorBuilder initArgs(List<Object> args) {
 
 			/**
 			 * Set the {@code accumulate} function that updates the state for each document. The functions first argument is
-			 * the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}.
-			 * <br />
+			 * the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}. <br />
 			 * <code class="java">
 			 * function(state, accumArg1, accumArg2, ...) {
 			 *   ...
@@ -473,6 +474,7 @@ public AccumulatorBuilder initArgs(List<Object> args) {
 			 * @return this.
 			 */
 			@Override
+			@Contract("_ -> this")
 			public AccumulatorBuilder accumulate(String function) {
 
 				Assert.notNull(function, "Accumulate function must not be null");
@@ -488,6 +490,7 @@ public AccumulatorBuilder accumulate(String function) {
 			 * @return this.
 			 */
 			@Override
+			@Contract("_ -> this")
 			public AccumulatorBuilder accumulateArgs(List<Object> args) {
 
 				Assert.notNull(args, "Args must not be null");
@@ -499,8 +502,7 @@ public AccumulatorBuilder accumulateArgs(List<Object> args) {
 			/**
 			 * Set the {@code merge} function used to merge two internal states. <br />
 			 * This might be required because the operation is run on a sharded cluster or when the operator exceeds its
-			 * memory limit.
-			 * <br />
+			 * memory limit. <br />
 			 * <code class="java">
 			 * function(state1, state2) {
 			 *   ...
@@ -512,6 +514,7 @@ public AccumulatorBuilder accumulateArgs(List<Object> args) {
 			 * @return this.
 			 */
 			@Override
+			@Contract("_ -> this")
 			public AccumulatorBuilder merge(String function) {
 
 				Assert.notNull(function, "Merge function must not be null");
@@ -526,6 +529,7 @@ public AccumulatorBuilder merge(String function) {
 			 * @param lang must not be {@literal null}. Default is {@literal js}.
 			 * @return this.
 			 */
+			@Contract("_ -> this")
 			public AccumulatorBuilder lang(String lang) {
 
 				Assert.hasText(lang, "Lang must not be null nor empty; The default would be 'js'");
@@ -536,8 +540,7 @@ public AccumulatorBuilder lang(String lang) {
 
 			/**
 			 * Set the {@code finalize} function used to update the result of the accumulation when all documents have been
-			 * processed.
-			 * <br />
+			 * processed. <br />
 			 * <code class="java">
 			 * function(state) {
 			 *   ...
@@ -549,6 +552,7 @@ public AccumulatorBuilder lang(String lang) {
 			 * @return new instance of {@link Accumulator}.
 			 */
 			@Override
+			@Contract("_ -> new")
 			public Accumulator finalize(String function) {
 
 				Assert.notNull(function, "Finalize function must not be null");
@@ -562,6 +566,7 @@ public Accumulator finalize(String function) {
 			}
 
 			@Override
+			@Contract("-> new")
 			public Accumulator build() {
 				return new Accumulator(createArgumentMap());
 			}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java
index 9da80c4668..4db0181d39 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java
@@ -19,6 +19,7 @@
 import java.util.Collections;
 
 import org.springframework.data.domain.Sort;
+import org.springframework.lang.Contract;
 
 /**
  * Gateway to {@literal selection operators} such as {@literal $bottom}.
@@ -69,6 +70,7 @@ public static Bottom bottom(int numberOfResults) {
 		 * @param numberOfResults
 		 * @return new instance of {@link Bottom}.
 		 */
+		@Contract("_ -> new")
 		public Bottom limit(int numberOfResults) {
 			return limit((Object) numberOfResults);
 		}
@@ -80,10 +82,12 @@ public Bottom limit(int numberOfResults) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Bottom}.
 		 */
+		@Contract("_ -> new")
 		public Bottom limit(AggregationExpression expression) {
 			return limit((Object) expression);
 		}
 
+		@Contract("_ -> new")
 		private Bottom limit(Object value) {
 			return new Bottom(append("n", value));
 		}
@@ -94,6 +98,7 @@ private Bottom limit(Object value) {
 		 * @param sort must not be {@literal null}.
 		 * @return new instance of {@link Bottom}.
 		 */
+		@Contract("_ -> new")
 		public Bottom sortBy(Sort sort) {
 			return new Bottom(append("sortBy", sort));
 		}
@@ -104,6 +109,7 @@ public Bottom sortBy(Sort sort) {
 		 * @param out must not be {@literal null}.
 		 * @return new instance of {@link Bottom}.
 		 */
+		@Contract("_ -> new")
 		public Bottom output(Fields out) {
 			return new Bottom(append("output", out));
 		}
@@ -115,6 +121,7 @@ public Bottom output(Fields out) {
 		 * @return new instance of {@link Bottom}.
 		 * @see #output(Fields)
 		 */
+		@Contract("_ -> new")
 		public Bottom output(String... fieldNames) {
 			return output(Fields.fields(fieldNames));
 		}
@@ -126,6 +133,7 @@ public Bottom output(String... fieldNames) {
 		 * @return new instance of {@link Bottom}.
 		 * @see #output(Fields)
 		 */
+		@Contract("_ -> new")
 		public Bottom output(AggregationExpression... out) {
 			return new Bottom(append("output", Arrays.asList(out)));
 		}
@@ -172,6 +180,7 @@ public static Top top(int numberOfResults) {
 		 * @param numberOfResults
 		 * @return new instance of {@link Top}.
 		 */
+		@Contract("_ -> new")
 		public Top limit(int numberOfResults) {
 			return limit((Object) numberOfResults);
 		}
@@ -183,6 +192,7 @@ public Top limit(int numberOfResults) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Top}.
 		 */
+		@Contract("_ -> new")
 		public Top limit(AggregationExpression expression) {
 			return limit((Object) expression);
 		}
@@ -197,6 +207,7 @@ private Top limit(Object value) {
 		 * @param sort must not be {@literal null}.
 		 * @return new instance of {@link Top}.
 		 */
+		@Contract("_ -> new")
 		public Top sortBy(Sort sort) {
 			return new Top(append("sortBy", sort));
 		}
@@ -207,6 +218,7 @@ public Top sortBy(Sort sort) {
 		 * @param out must not be {@literal null}.
 		 * @return new instance of {@link Top}.
 		 */
+		@Contract("_ -> new")
 		public Top output(Fields out) {
 			return new Top(append("output", out));
 		}
@@ -218,6 +230,7 @@ public Top output(Fields out) {
 		 * @return new instance of {@link Top}.
 		 * @see #output(Fields)
 		 */
+		@Contract("_ -> new")
 		public Top output(String... fieldNames) {
 			return output(Fields.fields(fieldNames));
 		}
@@ -229,6 +242,7 @@ public Top output(String... fieldNames) {
 		 * @return new instance of {@link Top}.
 		 * @see #output(Fields)
 		 */
+		@Contract("_ -> new")
 		public Top output(AggregationExpression... out) {
 			return new Top(append("output", Arrays.asList(out)));
 		}
@@ -263,6 +277,7 @@ public static First first(int numberOfResults) {
 		 * @param numberOfResults
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First limit(int numberOfResults) {
 			return limit((Object) numberOfResults);
 		}
@@ -274,6 +289,7 @@ public First limit(int numberOfResults) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First limit(AggregationExpression expression) {
 			return limit((Object) expression);
 		}
@@ -288,6 +304,7 @@ private First limit(Object value) {
 		 * @param fieldName must not be {@literal null}.
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First of(String fieldName) {
 			return input(fieldName);
 		}
@@ -298,6 +315,7 @@ public First of(String fieldName) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First of(AggregationExpression expression) {
 			return input(expression);
 		}
@@ -308,6 +326,7 @@ public First of(AggregationExpression expression) {
 		 * @param fieldName must not be {@literal null}.
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First input(String fieldName) {
 			return new First(append("input", Fields.field(fieldName)));
 		}
@@ -318,6 +337,7 @@ public First input(String fieldName) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link First}.
 		 */
+		@Contract("_ -> new")
 		public First input(AggregationExpression expression) {
 			return new First(append("input", expression));
 		}
@@ -357,6 +377,7 @@ public static Last last(int numberOfResults) {
 		 * @param numberOfResults
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last limit(int numberOfResults) {
 			return limit((Object) numberOfResults);
 		}
@@ -368,6 +389,7 @@ public Last limit(int numberOfResults) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last limit(AggregationExpression expression) {
 			return limit((Object) expression);
 		}
@@ -382,6 +404,7 @@ private Last limit(Object value) {
 		 * @param fieldName must not be {@literal null}.
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last of(String fieldName) {
 			return input(fieldName);
 		}
@@ -392,6 +415,7 @@ public Last of(String fieldName) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last of(AggregationExpression expression) {
 			return input(expression);
 		}
@@ -402,6 +426,7 @@ public Last of(AggregationExpression expression) {
 		 * @param fieldName must not be {@literal null}.
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last input(String fieldName) {
 			return new Last(append("input", Fields.field(fieldName)));
 		}
@@ -412,6 +437,7 @@ public Last input(String fieldName) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Last}.
 		 */
+		@Contract("_ -> new")
 		public Last input(AggregationExpression expression) {
 			return new Last(append("input", expression));
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
index 7f5c1c7722..6ef4f1323f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
@@ -19,8 +19,9 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.SetOperation.FieldAppender.ValueAppender;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input
@@ -82,6 +83,7 @@ public static ValueAppender set(String field) {
 	 * @param value the value to assign.
 	 * @return new instance of {@link SetOperation}.
 	 */
+	@Contract("_, _ -> new")
 	public SetOperation set(Object field, Object value) {
 
 		LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap());
@@ -131,7 +133,7 @@ public ValueAppender set(String field) {
 			return new ValueAppender() {
 
 				@Override
-				public SetOperation toValue(Object value) {
+				public SetOperation toValue(@Nullable Object value) {
 
 					valueMap.put(field, value);
 					return FieldAppender.this.build();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java
index 094ef7365b..a99c0926f4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java
@@ -19,7 +19,9 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -55,8 +57,8 @@ public static SetOperatorFactory arrayAsSet(AggregationExpression expression) {
 	 */
 	public static class SetOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link SetOperatorFactory} for given {@literal fieldReference}.
@@ -104,6 +106,7 @@ public SetEquals isEqualTo(AggregationExpression... expressions) {
 			return createSetEquals().isEqualTo(expressions);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SetEquals createSetEquals() {
 			return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression);
 		}
@@ -130,6 +133,7 @@ public SetIntersection intersects(AggregationExpression... expressions) {
 			return createSetIntersection().intersects(expressions);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SetIntersection createSetIntersection() {
 			return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression);
 		}
@@ -156,6 +160,7 @@ public SetUnion union(AggregationExpression... expressions) {
 			return createSetUnion().union(expressions);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SetUnion createSetUnion() {
 			return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression);
 		}
@@ -182,6 +187,7 @@ public SetDifference differenceTo(AggregationExpression expression) {
 			return createSetDifference().differenceTo(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SetDifference createSetDifference() {
 			return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression);
 		}
@@ -208,6 +214,7 @@ public SetIsSubset isSubsetOf(AggregationExpression expression) {
 			return createSetIsSubset().isSubsetOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SetIsSubset createSetIsSubset() {
 			return usesFieldRef() ? SetIsSubset.arrayAsSet(fieldReference) : SetIsSubset.arrayAsSet(expression);
 		}
@@ -218,6 +225,7 @@ private SetIsSubset createSetIsSubset() {
 		 *
 		 * @return new instance of {@link AnyElementTrue}.
 		 */
+		@SuppressWarnings("NullAway")
 		public AnyElementTrue anyElementTrue() {
 			return usesFieldRef() ? AnyElementTrue.arrayAsSet(fieldReference) : AnyElementTrue.arrayAsSet(expression);
 		}
@@ -228,6 +236,7 @@ public AnyElementTrue anyElementTrue() {
 		 *
 		 * @return new instance of {@link AllElementsTrue}.
 		 */
+		@SuppressWarnings("NullAway")
 		public AllElementsTrue allElementsTrue() {
 			return usesFieldRef() ? AllElementsTrue.arrayAsSet(fieldReference) : AllElementsTrue.arrayAsSet(expression);
 		}
@@ -283,6 +292,7 @@ public static SetEquals arrayAsSet(AggregationExpression expression) {
 		 * @param arrayReferences must not be {@literal null}.
 		 * @return new instance of {@link SetEquals}.
 		 */
+		@Contract("_ -> new")
 		public SetEquals isEqualTo(String... arrayReferences) {
 
 			Assert.notNull(arrayReferences, "ArrayReferences must not be null");
@@ -295,6 +305,7 @@ public SetEquals isEqualTo(String... arrayReferences) {
 		 * @param expressions must not be {@literal null}.
 		 * @return new instance of {@link SetEquals}.
 		 */
+		@Contract("_ -> new")
 		public SetEquals isEqualTo(AggregationExpression... expressions) {
 
 			Assert.notNull(expressions, "Expressions must not be null");
@@ -307,6 +318,7 @@ public SetEquals isEqualTo(AggregationExpression... expressions) {
 		 * @param array must not be {@literal null}.
 		 * @return new instance of {@link SetEquals}.
 		 */
+		@Contract("_ -> new")
 		public SetEquals isEqualTo(Object[] array) {
 
 			Assert.notNull(array, "Array must not be null");
@@ -360,6 +372,7 @@ public static SetIntersection arrayAsSet(AggregationExpression expression) {
 		 * @param arrayReferences must not be {@literal null}.
 		 * @return new instance of {@link SetIntersection}.
 		 */
+		@Contract("_ -> new")
 		public SetIntersection intersects(String... arrayReferences) {
 
 			Assert.notNull(arrayReferences, "ArrayReferences must not be null");
@@ -372,6 +385,7 @@ public SetIntersection intersects(String... arrayReferences) {
 		 * @param expressions must not be {@literal null}.
 		 * @return new instance of {@link SetIntersection}.
 		 */
+		@Contract("_ -> new")
 		public SetIntersection intersects(AggregationExpression... expressions) {
 
 			Assert.notNull(expressions, "Expressions must not be null");
@@ -425,6 +439,7 @@ public static SetUnion arrayAsSet(AggregationExpression expression) {
 		 * @param arrayReferences must not be {@literal null}.
 		 * @return new instance of {@link SetUnion}.
 		 */
+		@Contract("_ -> new")
 		public SetUnion union(String... arrayReferences) {
 
 			Assert.notNull(arrayReferences, "ArrayReferences must not be null");
@@ -437,6 +452,7 @@ public SetUnion union(String... arrayReferences) {
 		 * @param expressions must not be {@literal null}.
 		 * @return new instance of {@link SetUnion}.
 		 */
+		@Contract("_ -> new")
 		public SetUnion union(AggregationExpression... expressions) {
 
 			Assert.notNull(expressions, "Expressions must not be null");
@@ -490,6 +506,7 @@ public static SetDifference arrayAsSet(AggregationExpression expression) {
 		 * @param arrayReference must not be {@literal null}.
 		 * @return new instance of {@link SetDifference}.
 		 */
+		@Contract("_ -> new")
 		public SetDifference differenceTo(String arrayReference) {
 
 			Assert.notNull(arrayReference, "ArrayReference must not be null");
@@ -502,6 +519,7 @@ public SetDifference differenceTo(String arrayReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link SetDifference}.
 		 */
+		@Contract("_ -> new")
 		public SetDifference differenceTo(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -555,6 +573,7 @@ public static SetIsSubset arrayAsSet(AggregationExpression expression) {
 		 * @param arrayReference must not be {@literal null}.
 		 * @return new instance of {@link SetIsSubset}.
 		 */
+		@Contract("_ -> new")
 		public SetIsSubset isSubsetOf(String arrayReference) {
 
 			Assert.notNull(arrayReference, "ArrayReference must not be null");
@@ -567,6 +586,7 @@ public SetIsSubset isSubsetOf(String arrayReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link SetIsSubset}.
 		 */
+		@Contract("_ -> new")
 		public SetIsSubset isSubsetOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -614,6 +634,7 @@ public static AnyElementTrue arrayAsSet(AggregationExpression expression) {
 			return new AnyElementTrue(Collections.singletonList(expression));
 		}
 
+		@Contract("-> this")
 		public AnyElementTrue anyElementTrue() {
 			return this;
 		}
@@ -659,6 +680,7 @@ public static AllElementsTrue arrayAsSet(AggregationExpression expression) {
 			return new AllElementsTrue(Collections.singletonList(expression));
 		}
 
+		@Contract("-> this")
 		public AllElementsTrue allElementsTrue() {
 			return this;
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
index 2b8df539e1..e1fec17811 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
@@ -22,8 +22,9 @@
 import java.util.concurrent.TimeUnit;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -31,7 +32,8 @@
  *
  * @author Christoph Strobl
  * @since 3.3
- * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/">https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/</a>
+ * @see <a href=
+ *      "https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/">https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/</a>
  */
 public class SetWindowFieldsOperation
 		implements AggregationOperation, FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation {
@@ -137,6 +139,7 @@ public WindowOutput(ComputedField outputField) {
 		 * @param field must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public WindowOutput append(ComputedField field) {
 
 			Assert.notNull(field, "Field must not be null");
@@ -152,6 +155,7 @@ public WindowOutput append(ComputedField field) {
 		 * @return new instance of {@link ComputedFieldAppender}.
 		 * @see #append(ComputedField)
 		 */
+		@Contract("_ -> new")
 		public ComputedFieldAppender append(AggregationExpression expression) {
 
 			return new ComputedFieldAppender() {
@@ -249,8 +253,7 @@ public AggregationExpression getWindowOperator() {
 			return windowOperator;
 		}
 
-		@Nullable
-		public Window getWindow() {
+		public @Nullable Window getWindow() {
 			return window;
 		}
 	}
@@ -360,6 +363,7 @@ public static class RangeWindowBuilder {
 		 * @param lower eg. {@literal current} or {@literal unbounded}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RangeWindowBuilder from(String lower) {
 
 			this.lower = lower;
@@ -372,6 +376,7 @@ public RangeWindowBuilder from(String lower) {
 		 * @param upper eg. {@literal current} or {@literal unbounded}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RangeWindowBuilder to(String upper) {
 
 			this.upper = upper;
@@ -386,6 +391,7 @@ public RangeWindowBuilder to(String upper) {
 		 * @param lower
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RangeWindowBuilder from(Number lower) {
 
 			this.lower = lower;
@@ -400,6 +406,7 @@ public RangeWindowBuilder from(Number lower) {
 		 * @param upper
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RangeWindowBuilder to(Number upper) {
 
 			this.upper = upper;
@@ -411,6 +418,7 @@ public RangeWindowBuilder to(Number upper) {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RangeWindowBuilder fromCurrent() {
 			return from(CURRENT);
 		}
@@ -420,6 +428,7 @@ public RangeWindowBuilder fromCurrent() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RangeWindowBuilder fromUnbounded() {
 			return from(UNBOUNDED);
 		}
@@ -429,6 +438,7 @@ public RangeWindowBuilder fromUnbounded() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RangeWindowBuilder toCurrent() {
 			return to(CURRENT);
 		}
@@ -438,6 +448,7 @@ public RangeWindowBuilder toCurrent() {
 		 *
 		 * @return this.
 		 */
+		@Contract("-> this")
 		public RangeWindowBuilder toUnbounded() {
 			return to(UNBOUNDED);
 		}
@@ -448,6 +459,7 @@ public RangeWindowBuilder toUnbounded() {
 		 * @param windowUnit must not be {@literal null}. Can be on of {@link Windows}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public RangeWindowBuilder unit(WindowUnit windowUnit) {
 
 			Assert.notNull(windowUnit, "WindowUnit must not be null");
@@ -460,6 +472,7 @@ public RangeWindowBuilder unit(WindowUnit windowUnit) {
 		 *
 		 * @return new instance of {@link RangeWindow}.
 		 */
+		@Contract("-> new")
 		public RangeWindow build() {
 
 			Assert.notNull(lower, "Lower bound must not be null");
@@ -488,20 +501,24 @@ public static class DocumentWindowBuilder {
 		 * @param lower
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public DocumentWindowBuilder from(Number lower) {
 
 			this.lower = lower;
 			return this;
 		}
 
+		@Contract("-> this")
 		public DocumentWindowBuilder fromCurrent() {
 			return from(CURRENT);
 		}
 
+		@Contract("-> this")
 		public DocumentWindowBuilder fromUnbounded() {
 			return from(UNBOUNDED);
 		}
 
+		@Contract("-> this")
 		public DocumentWindowBuilder to(String upper) {
 
 			this.upper = upper;
@@ -514,6 +531,7 @@ public DocumentWindowBuilder to(String upper) {
 		 * @param lower eg. {@literal current} or {@literal unbounded}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public DocumentWindowBuilder from(String lower) {
 
 			this.lower = lower;
@@ -528,20 +546,24 @@ public DocumentWindowBuilder from(String lower) {
 		 * @param upper
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public DocumentWindowBuilder to(Number upper) {
 
 			this.upper = upper;
 			return this;
 		}
 
+		@Contract("-> this")
 		public DocumentWindowBuilder toCurrent() {
 			return to(CURRENT);
 		}
 
+		@Contract("-> this")
 		public DocumentWindowBuilder toUnbounded() {
 			return to(UNBOUNDED);
 		}
 
+		@Contract("-> new")
 		public DocumentWindow build() {
 
 			Assert.notNull(lower, "Lower bound must not be null");
@@ -689,9 +711,9 @@ public enum WindowUnits implements WindowUnit {
 	 */
 	public static class SetWindowFieldsOperationBuilder {
 
-		private Object partitionBy;
-		private SortOperation sortOperation;
-		private WindowOutput output;
+		private @Nullable Object partitionBy;
+		private @Nullable SortOperation sortOperation;
+		private @Nullable WindowOutput output;
 
 		/**
 		 * Specify the field to group by.
@@ -699,6 +721,7 @@ public static class SetWindowFieldsOperationBuilder {
 		 * @param fieldName must not be {@literal null} or null.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder partitionByField(String fieldName) {
 
 			Assert.hasText(fieldName, "Field name must not be empty or null");
@@ -711,6 +734,7 @@ public SetWindowFieldsOperationBuilder partitionByField(String fieldName) {
 		 * @param expression must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder partitionByExpression(AggregationExpression expression) {
 			return partitionBy(expression);
 		}
@@ -721,6 +745,7 @@ public SetWindowFieldsOperationBuilder partitionByExpression(AggregationExpressi
 		 * @param fields must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder sortBy(String... fields) {
 			return sortBy(Sort.by(fields));
 		}
@@ -731,6 +756,7 @@ public SetWindowFieldsOperationBuilder sortBy(String... fields) {
 		 * @param sort must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder sortBy(Sort sort) {
 			return sortBy(new SortOperation(sort));
 		}
@@ -741,6 +767,7 @@ public SetWindowFieldsOperationBuilder sortBy(Sort sort) {
 		 * @param sort must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder sortBy(SortOperation sort) {
 
 			Assert.notNull(sort, "SortOperation must not be null");
@@ -755,6 +782,7 @@ public SetWindowFieldsOperationBuilder sortBy(SortOperation sort) {
 		 * @param output must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder output(WindowOutput output) {
 
 			Assert.notNull(output, "WindowOutput must not be null");
@@ -769,6 +797,7 @@ public SetWindowFieldsOperationBuilder output(WindowOutput output) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link WindowChoice}.
 		 */
+		@Contract("_ -> new")
 		public WindowChoice output(AggregationExpression expression) {
 
 			return new WindowChoice() {
@@ -837,6 +866,7 @@ public interface WindowChoice extends As {
 		 * @param value must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public SetWindowFieldsOperationBuilder partitionBy(Object value) {
 
 			Assert.notNull(value, "Partition By must not be null");
@@ -850,7 +880,10 @@ public SetWindowFieldsOperationBuilder partitionBy(Object value) {
 		 *
 		 * @return new instance of {@link SetWindowFieldsOperation}.
 		 */
+		@Contract("-> new")
 		public SetWindowFieldsOperation build() {
+
+			Assert.notNull(output, "Output must be set first");
 			return new SetWindowFieldsOperation(partitionBy, sortOperation, output);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java
index ffc0aa0654..e6a9a23d31 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -67,6 +67,7 @@ public SortByCountOperation(AggregationExpression groupByExpression) {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public Document toDocument(AggregationOperationContext context) {
 
 		return new Document(getOperator(), groupByExpression == null ? context.getReference(groupByField).toString()
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java
index 3119e2729c..ade4f5328e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java
@@ -20,6 +20,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.GenericTypeResolver;
 import org.springframework.data.mongodb.core.spel.ExpressionNode;
 import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport;
@@ -42,7 +43,6 @@
 import org.springframework.expression.spel.standard.SpelExpression;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
@@ -83,7 +83,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
 	 * @param params must not be {@literal null}
 	 * @return
 	 */
-	public Object transform(String expression, AggregationOperationContext context, Object... params) {
+	public @Nullable Object transform(String expression, AggregationOperationContext context, Object... params) {
 
 		Assert.notNull(expression, "Expression must not be null");
 		Assert.notNull(context, "AggregationOperationContext must not be null");
@@ -96,7 +96,7 @@ public Object transform(String expression, AggregationOperationContext context,
 		return transform(new AggregationExpressionTransformationContext<>(node, null, null, context));
 	}
 
-	public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
+	public @Nullable Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
 		return lookupConversionFor(context.getCurrentNode()).convert(context);
 	}
 
@@ -137,7 +137,7 @@ private static abstract class ExpressionNodeConversion<T extends ExpressionNode>
 		 *
 		 * @param transformer must not be {@literal null}.
 		 */
-		@SuppressWarnings("unchecked")
+		@SuppressWarnings({"unchecked", "NullAway"})
 		public ExpressionNodeConversion(AggregationExpressionTransformer transformer) {
 
 			Assert.notNull(transformer, "Transformer must not be null");
@@ -165,7 +165,7 @@ protected boolean supports(ExpressionNode node) {
 		 * @param context must not be {@literal null}.
 		 * @return
 		 */
-		protected Object transform(ExpressionNode node, AggregationExpressionTransformationContext<?> context) {
+		protected @Nullable Object transform(ExpressionNode node, AggregationExpressionTransformationContext<?> context) {
 
 			Assert.notNull(node, "ExpressionNode must not be null");
 			Assert.notNull(context, "AggregationExpressionTransformationContext must not be null");
@@ -183,7 +183,7 @@ protected Object transform(ExpressionNode node, AggregationExpressionTransformat
 		 * @param context must not be {@literal null}.
 		 * @return
 		 */
-		protected Object transform(ExpressionNode node, @Nullable ExpressionNode parent, @Nullable Document operation,
+		protected @Nullable Object transform(ExpressionNode node, @Nullable ExpressionNode parent, @Nullable Document operation,
 				AggregationExpressionTransformationContext<?> context) {
 
 			Assert.notNull(node, "ExpressionNode must not be null");
@@ -194,7 +194,7 @@ protected Object transform(ExpressionNode node, @Nullable ExpressionNode parent,
 		}
 
 		@Override
-		public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		public @Nullable Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) {
 			return transformer.transform(context);
 		}
 
@@ -204,7 +204,7 @@ public Object transform(AggregationExpressionTransformationContext<ExpressionNod
 		 * @param context
 		 * @return
 		 */
-		protected abstract Object convert(AggregationExpressionTransformationContext<T> context);
+		protected abstract @Nullable Object convert(AggregationExpressionTransformationContext<T> context);
 	}
 
 	/**
@@ -247,6 +247,7 @@ protected Object convert(AggregationExpressionTransformationContext<OperatorNode
 			return operationObject;
 		}
 
+		@SuppressWarnings("NullAway")
 		private Document createOperationObjectAndAddToPreviousArgumentsIfNecessary(
 				AggregationExpressionTransformationContext<OperatorNode> context, OperatorNode currentNode) {
 
@@ -301,7 +302,7 @@ private static class IndexerNodeConversion extends ExpressionNodeConversion<Expr
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
 			return context.addToPreviousOrReturn(context.getCurrentNode().getValue());
 		}
 
@@ -322,9 +323,8 @@ private static class InlineListNodeConversion extends ExpressionNodeConversion<E
 			super(transformer);
 		}
 
-		@Nullable
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
 
 			ExpressionNode currentNode = context.getCurrentNode();
 
@@ -355,7 +355,7 @@ private static class PropertyOrFieldReferenceNodeConversion extends ExpressionNo
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
 
 			String fieldReference = context.getFieldReference().toString();
 			return context.addToPreviousOrReturn(fieldReference);
@@ -381,14 +381,14 @@ private static class LiteralNodeConversion extends ExpressionNodeConversion<Lite
 
 		@Override
 		@SuppressWarnings("unchecked")
-		protected Object convert(AggregationExpressionTransformationContext<LiteralNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<LiteralNode> context) {
 
 			LiteralNode node = context.getCurrentNode();
 			Object value = node.getValue();
 
 			if (context.hasPreviousOperation()) {
 
-				if (node.isUnaryMinus(context.getParentNode())) {
+				if (node.isUnaryMinus(context.getParentNode()) && value != null) {
 					// unary minus operator
 					return NumberUtils.convertNumberToTargetClass(((Number) value).doubleValue() * -1,
 							(Class<Number>) value.getClass()); // retain type, e.g. int to -int
@@ -419,7 +419,7 @@ private static class MethodReferenceNodeConversion extends ExpressionNodeConvers
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) {
 
 			MethodReferenceNode node = context.getCurrentNode();
 			AggregationMethodReference methodReference = node.getMethodReference();
@@ -469,7 +469,7 @@ private static class CompoundExpressionNodeConversion extends ExpressionNodeConv
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
 
 			ExpressionNode currentNode = context.getCurrentNode();
 
@@ -503,7 +503,7 @@ static class NotOperatorNodeConversion extends ExpressionNodeConversion<NotOpera
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) {
 
 			NotOperatorNode node = context.getCurrentNode();
 			List<Object> args = new ArrayList<>();
@@ -537,7 +537,7 @@ static class ValueRetrievingNodeConversion extends ExpressionNodeConversion<Expr
 		}
 
 		@Override
-		protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
+		protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) {
 
 			Object value = context.getCurrentNode().getValue();
 			return ObjectUtils.isArray(value) ? Arrays.asList(ObjectUtils.toObjectArray(value)) : value;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
index 9788497601..0f3447a476 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
@@ -21,8 +21,10 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.mongodb.util.RegexFlags;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -60,8 +62,8 @@ public static StringOperatorFactory valueOf(AggregationExpression fieldReference
 	 */
 	public static class StringOperatorFactory {
 
-		private final String fieldReference;
-		private final AggregationExpression expression;
+		private final @Nullable String fieldReference;
+		private final @Nullable AggregationExpression expression;
 
 		/**
 		 * Creates new {@link StringOperatorFactory} for given {@literal fieldReference}.
@@ -126,6 +128,7 @@ public Concat concat(String value) {
 			return createConcat().concat(value);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Concat createConcat() {
 			return usesFieldRef() ? Concat.valueOf(fieldReference) : Concat.valueOf(expression);
 		}
@@ -153,6 +156,7 @@ public Substr substring(int start, int nrOfChars) {
 			return createSubstr().substring(start, nrOfChars);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Substr createSubstr() {
 			return usesFieldRef() ? Substr.valueOf(fieldReference) : Substr.valueOf(expression);
 		}
@@ -162,6 +166,7 @@ private Substr createSubstr() {
 		 *
 		 * @return new instance of {@link ToLower}.
 		 */
+		@SuppressWarnings("NullAway")
 		public ToLower toLower() {
 			return usesFieldRef() ? ToLower.lowerValueOf(fieldReference) : ToLower.lowerValueOf(expression);
 		}
@@ -171,6 +176,7 @@ public ToLower toLower() {
 		 *
 		 * @return new instance of {@link ToUpper}.
 		 */
+		@SuppressWarnings("NullAway")
 		public ToUpper toUpper() {
 			return usesFieldRef() ? ToUpper.upperValueOf(fieldReference) : ToUpper.upperValueOf(expression);
 		}
@@ -214,6 +220,7 @@ public StrCaseCmp strCaseCmpValueOf(AggregationExpression expression) {
 			return createStrCaseCmp().strcasecmpValueOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private StrCaseCmp createStrCaseCmp() {
 			return usesFieldRef() ? StrCaseCmp.valueOf(fieldReference) : StrCaseCmp.valueOf(expression);
 		}
@@ -260,6 +267,7 @@ public IndexOfBytes indexOf(AggregationExpression expression) {
 			return createIndexOfBytesSubstringBuilder().indexOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private IndexOfBytes.SubstringBuilder createIndexOfBytesSubstringBuilder() {
 			return usesFieldRef() ? IndexOfBytes.valueOf(fieldReference) : IndexOfBytes.valueOf(expression);
 		}
@@ -306,6 +314,7 @@ public IndexOfCP indexOfCP(AggregationExpression expression) {
 			return createIndexOfCPSubstringBuilder().indexOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private IndexOfCP.SubstringBuilder createIndexOfCPSubstringBuilder() {
 			return usesFieldRef() ? IndexOfCP.valueOf(fieldReference) : IndexOfCP.valueOf(expression);
 		}
@@ -343,6 +352,7 @@ public Split split(AggregationExpression expression) {
 			return createSplit().split(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Split createSplit() {
 			return usesFieldRef() ? Split.valueOf(fieldReference) : Split.valueOf(expression);
 		}
@@ -353,6 +363,7 @@ private Split createSplit() {
 		 *
 		 * @return new instance of {@link StrLenBytes}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StrLenBytes length() {
 			return usesFieldRef() ? StrLenBytes.stringLengthOf(fieldReference) : StrLenBytes.stringLengthOf(expression);
 		}
@@ -363,6 +374,7 @@ public StrLenBytes length() {
 		 *
 		 * @return new instance of {@link StrLenCP}.
 		 */
+		@SuppressWarnings("NullAway")
 		public StrLenCP lengthCP() {
 			return usesFieldRef() ? StrLenCP.stringLengthOfCP(fieldReference) : StrLenCP.stringLengthOfCP(expression);
 		}
@@ -390,6 +402,7 @@ public SubstrCP substringCP(int codePointStart, int nrOfCodePoints) {
 			return createSubstrCP().substringCP(codePointStart, nrOfCodePoints);
 		}
 
+		@SuppressWarnings("NullAway")
 		private SubstrCP createSubstrCP() {
 			return usesFieldRef() ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression);
 		}
@@ -432,6 +445,7 @@ public Trim trim(AggregationExpression expression) {
 			return trim().charsOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Trim createTrim() {
 			return usesFieldRef() ? Trim.valueOf(fieldReference) : Trim.valueOf(expression);
 		}
@@ -474,6 +488,7 @@ public LTrim ltrim(AggregationExpression expression) {
 			return ltrim().charsOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private LTrim createLTrim() {
 			return usesFieldRef() ? LTrim.valueOf(fieldReference) : LTrim.valueOf(expression);
 		}
@@ -516,6 +531,7 @@ public RTrim rtrim(AggregationExpression expression) {
 			return rtrim().charsOf(expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private RTrim createRTrim() {
 			return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression);
 		}
@@ -572,6 +588,7 @@ public RegexFind regexFind(String regex, String options) {
 			return createRegexFind().regex(regex).options(options);
 		}
 
+		@SuppressWarnings("NullAway")
 		private RegexFind createRegexFind() {
 			return usesFieldRef() ? RegexFind.valueOf(fieldReference) : RegexFind.valueOf(expression);
 		}
@@ -628,6 +645,7 @@ public RegexFindAll regexFindAll(String regex, String options) {
 			return createRegexFindAll().regex(regex).options(options);
 		}
 
+		@SuppressWarnings("NullAway")
 		private RegexFindAll createRegexFindAll() {
 			return usesFieldRef() ? RegexFindAll.valueOf(fieldReference) : RegexFindAll.valueOf(expression);
 		}
@@ -683,6 +701,7 @@ public RegexMatch regexMatch(String regex, String options) {
 			return createRegexMatch().regex(regex).options(options);
 		}
 
+		@SuppressWarnings("NullAway")
 		private RegexMatch createRegexMatch() {
 			return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression);
 		}
@@ -713,6 +732,7 @@ public ReplaceOne replaceOne(AggregationExpression search, String replacement) {
 			return createReplaceOne().findValueOf(search).replacement(replacement);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ReplaceOne createReplaceOne() {
 			return usesFieldRef() ? ReplaceOne.valueOf(fieldReference) : ReplaceOne.valueOf(expression);
 		}
@@ -743,6 +763,7 @@ public ReplaceAll replaceAll(AggregationExpression search, String replacement) {
 			return createReplaceAll().findValueOf(search).replacement(replacement);
 		}
 
+		@SuppressWarnings("NullAway")
 		private ReplaceAll createReplaceAll() {
 			return usesFieldRef() ? ReplaceAll.valueOf(fieldReference) : ReplaceAll.valueOf(expression);
 		}
@@ -810,6 +831,7 @@ public static Concat stringValue(String value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Concat}.
 		 */
+		@Contract("_ -> new")
 		public Concat concatValueOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -822,6 +844,7 @@ public Concat concatValueOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Concat}.
 		 */
+		@Contract("_ -> new")
 		public Concat concatValueOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -834,6 +857,7 @@ public Concat concatValueOf(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link Concat}.
 		 */
+		@Contract("_ -> new")
 		public Concat concat(String value) {
 			return new Concat(append(value));
 		}
@@ -883,6 +907,7 @@ public static Substr valueOf(AggregationExpression expression) {
 		 * @param start start index (including)
 		 * @return new instance of {@link Substr}.
 		 */
+		@Contract("_ -> new")
 		public Substr substring(int start) {
 			return substring(start, -1);
 		}
@@ -892,6 +917,7 @@ public Substr substring(int start) {
 		 * @param nrOfChars
 		 * @return new instance of {@link Substr}.
 		 */
+		@Contract("_, _ -> new")
 		public Substr substring(int start, int nrOfChars) {
 			return new Substr(append(Arrays.asList(start, nrOfChars)));
 		}
@@ -1055,16 +1081,19 @@ public static StrCaseCmp stringValue(String value) {
 			return new StrCaseCmp(Collections.singletonList(value));
 		}
 
+		@Contract("_ -> new")
 		public StrCaseCmp strcasecmp(String value) {
 			return new StrCaseCmp(append(value));
 		}
 
+		@Contract("_ -> new")
 		public StrCaseCmp strcasecmpValueOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
 			return new StrCaseCmp(append(Fields.field(fieldReference)));
 		}
 
+		@Contract("_ -> new")
 		public StrCaseCmp strcasecmpValueOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1118,6 +1147,7 @@ public static SubstringBuilder valueOf(AggregationExpression expression) {
 		 * @param range must not be {@literal null}.
 		 * @return new instance of {@link IndexOfBytes}.
 		 */
+		@Contract("_ -> new")
 		public IndexOfBytes within(Range<Long> range) {
 			return new IndexOfBytes(append(AggregationUtils.toRangeValues(range)));
 		}
@@ -1208,6 +1238,7 @@ public static SubstringBuilder valueOf(AggregationExpression expression) {
 		 * @param range must not be {@literal null}.
 		 * @return new instance of {@link IndexOfCP}.
 		 */
+		@Contract("_ -> new")
 		public IndexOfCP within(Range<Long> range) {
 			return new IndexOfCP(append(AggregationUtils.toRangeValues(range)));
 		}
@@ -1298,6 +1329,7 @@ public static Split valueOf(AggregationExpression expression) {
 		 * @param delimiter must not be {@literal null}.
 		 * @return new instance of {@link Split}.
 		 */
+		@Contract("_ -> new")
 		public Split split(String delimiter) {
 
 			Assert.notNull(delimiter, "Delimiter must not be null");
@@ -1310,6 +1342,7 @@ public Split split(String delimiter) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Split}.
 		 */
+		@Contract("_ -> new")
 		public Split split(Field fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1322,6 +1355,7 @@ public Split split(Field fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Split}.
 		 */
+		@Contract("_ -> new")
 		public Split split(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1447,10 +1481,12 @@ public static SubstrCP valueOf(AggregationExpression expression) {
 			return new SubstrCP(Collections.singletonList(expression));
 		}
 
+		@Contract("_ -> new")
 		public SubstrCP substringCP(int start) {
 			return substringCP(start, -1);
 		}
 
+		@Contract("_, _ -> new")
 		public SubstrCP substringCP(int start, int nrOfChars) {
 			return new SubstrCP(append(Arrays.asList(start, nrOfChars)));
 		}
@@ -1501,6 +1537,7 @@ public static Trim valueOf(AggregationExpression expression) {
 		 * @param chars must not be {@literal null}.
 		 * @return new instance of {@link Trim}.
 		 */
+		@Contract("_ -> new")
 		public Trim chars(String chars) {
 
 			Assert.notNull(chars, "Chars must not be null");
@@ -1514,6 +1551,7 @@ public Trim chars(String chars) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link Trim}.
 		 */
+		@Contract("_ -> new")
 		public Trim charsOf(String fieldReference) {
 			return new Trim(append("chars", Fields.field(fieldReference)));
 		}
@@ -1525,6 +1563,7 @@ public Trim charsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link Trim}.
 		 */
+		@Contract("_ -> new")
 		public Trim charsOf(AggregationExpression expression) {
 			return new Trim(append("chars", expression));
 		}
@@ -1598,6 +1637,7 @@ public static LTrim valueOf(AggregationExpression expression) {
 		 * @param chars must not be {@literal null}.
 		 * @return new instance of {@link LTrim}.
 		 */
+		@Contract("_ -> new")
 		public LTrim chars(String chars) {
 
 			Assert.notNull(chars, "Chars must not be null");
@@ -1611,6 +1651,7 @@ public LTrim chars(String chars) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link LTrim}.
 		 */
+		@Contract("_ -> new")
 		public LTrim charsOf(String fieldReference) {
 			return new LTrim(append("chars", Fields.field(fieldReference)));
 		}
@@ -1622,6 +1663,7 @@ public LTrim charsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link LTrim}.
 		 */
+		@Contract("_ -> new")
 		public LTrim charsOf(AggregationExpression expression) {
 			return new LTrim(append("chars", expression));
 		}
@@ -1677,6 +1719,7 @@ public static RTrim valueOf(AggregationExpression expression) {
 		 * @param chars must not be {@literal null}.
 		 * @return new instance of {@link RTrim}.
 		 */
+		@Contract("_ -> new")
 		public RTrim chars(String chars) {
 
 			Assert.notNull(chars, "Chars must not be null");
@@ -1689,6 +1732,7 @@ public RTrim chars(String chars) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RTrim}.
 		 */
+		@Contract("_ -> new")
 		public RTrim charsOf(String fieldReference) {
 			return new RTrim(append("chars", Fields.field(fieldReference)));
 		}
@@ -1699,6 +1743,7 @@ public RTrim charsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RTrim}.
 		 */
+		@Contract("_ -> new")
 		public RTrim charsOf(AggregationExpression expression) {
 			return new RTrim(append("chars", expression));
 		}
@@ -1757,6 +1802,7 @@ public static RegexFind valueOf(AggregationExpression expression) {
 		 * @param options must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind options(String options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -1771,6 +1817,7 @@ public RegexFind options(String options) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind optionsOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -1785,6 +1832,7 @@ public RegexFind optionsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind optionsOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1798,6 +1846,7 @@ public RegexFind optionsOf(AggregationExpression expression) {
 		 * @param regex must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind regex(String regex) {
 
 			Assert.notNull(regex, "Regex must not be null");
@@ -1811,6 +1860,7 @@ public RegexFind regex(String regex) {
 		 * @param pattern must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind pattern(Pattern pattern) {
 
 			Assert.notNull(pattern, "Pattern must not be null");
@@ -1827,6 +1877,7 @@ public RegexFind pattern(Pattern pattern) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind regexOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -1840,6 +1891,7 @@ public RegexFind regexOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexFind}.
 		 */
+		@Contract("_ -> new")
 		public RegexFind regexOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1899,6 +1951,7 @@ public static RegexFindAll valueOf(AggregationExpression expression) {
 		 * @param options must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll options(String options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -1913,6 +1966,7 @@ public RegexFindAll options(String options) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll optionsOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -1927,6 +1981,7 @@ public RegexFindAll optionsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll optionsOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -1940,6 +1995,7 @@ public RegexFindAll optionsOf(AggregationExpression expression) {
 		 * @param pattern must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll pattern(Pattern pattern) {
 
 			Assert.notNull(pattern, "Pattern must not be null");
@@ -1956,6 +2012,7 @@ public RegexFindAll pattern(Pattern pattern) {
 		 * @param regex must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll regex(String regex) {
 
 			Assert.notNull(regex, "Regex must not be null");
@@ -1969,6 +2026,7 @@ public RegexFindAll regex(String regex) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll regexOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -1982,6 +2040,7 @@ public RegexFindAll regexOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexFindAll}.
 		 */
+		@Contract("_ -> new")
 		public RegexFindAll regexOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2043,6 +2102,7 @@ public static RegexMatch valueOf(AggregationExpression expression) {
 		 * @param options must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch options(String options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -2057,6 +2117,7 @@ public RegexMatch options(String options) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch optionsOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -2071,6 +2132,7 @@ public RegexMatch optionsOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch optionsOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2084,6 +2146,7 @@ public RegexMatch optionsOf(AggregationExpression expression) {
 		 * @param pattern must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch pattern(Pattern pattern) {
 
 			Assert.notNull(pattern, "Pattern must not be null");
@@ -2100,6 +2163,7 @@ public RegexMatch pattern(Pattern pattern) {
 		 * @param regex must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch regex(String regex) {
 
 			Assert.notNull(regex, "Regex must not be null");
@@ -2113,6 +2177,7 @@ public RegexMatch regex(String regex) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch regexOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -2126,6 +2191,7 @@ public RegexMatch regexOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link RegexMatch}.
 		 */
+		@Contract("_ -> new")
 		public RegexMatch regexOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2201,6 +2267,7 @@ public static ReplaceOne valueOf(AggregationExpression expression) {
 		 * @param replacement must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne replacement(String replacement) {
 
 			Assert.notNull(replacement, "Replacement must not be null");
@@ -2215,6 +2282,7 @@ public ReplaceOne replacement(String replacement) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne replacementOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -2229,6 +2297,7 @@ public ReplaceOne replacementOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne replacementOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2242,6 +2311,7 @@ public ReplaceOne replacementOf(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne find(String value) {
 
 			Assert.notNull(value, "Search string must not be null");
@@ -2255,6 +2325,7 @@ public ReplaceOne find(String value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne findValueOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -2269,6 +2340,7 @@ public ReplaceOne findValueOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ReplaceOne}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceOne findValueOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2344,6 +2416,7 @@ public static ReplaceAll valueOf(AggregationExpression expression) {
 		 * @param replacement must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll replacement(String replacement) {
 
 			Assert.notNull(replacement, "Replacement must not be null");
@@ -2358,6 +2431,7 @@ public ReplaceAll replacement(String replacement) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll replacementValueOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "FieldReference must not be null");
@@ -2372,6 +2446,7 @@ public ReplaceAll replacementValueOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll replacementValueOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
@@ -2385,6 +2460,7 @@ public ReplaceAll replacementValueOf(AggregationExpression expression) {
 		 * @param value must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll find(String value) {
 
 			Assert.notNull(value, "Search string must not be null");
@@ -2398,6 +2474,7 @@ public ReplaceAll find(String value) {
 		 * @param fieldReference must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll findValueOf(String fieldReference) {
 
 			Assert.notNull(fieldReference, "fieldReference must not be null");
@@ -2411,6 +2488,7 @@ public ReplaceAll findValueOf(String fieldReference) {
 		 * @param expression must not be {@literal null}.
 		 * @return new instance of {@link ReplaceAll}.
 		 */
+		@Contract("_ -> new")
 		public ReplaceAll findValueOf(AggregationExpression expression) {
 
 			Assert.notNull(expression, "Expression must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java
index 1fcf87d2a0..cc0296c900 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.aggregation;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Describes the system variables available in MongoDB aggregation framework pipeline expressions.
@@ -116,8 +116,7 @@ public String getTarget() {
 		return toString();
 	}
 
-	@Nullable
-	static String variableNameFrom(@Nullable String fieldRef) {
+	static @Nullable String variableNameFrom(@Nullable String fieldRef) {
 
 		if (fieldRef == null || !fieldRef.startsWith(PREFIX) || fieldRef.length() <= 2) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
index f30ebf394b..d2d49abf78 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
@@ -22,7 +22,7 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.MappingException;
 import org.springframework.data.mapping.PersistentPropertyPath;
 import org.springframework.data.mapping.context.MappingContext;
@@ -33,7 +33,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java
index 057ada12d5..c93c1bad9e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java
@@ -19,7 +19,7 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java
index ff765c37f7..0bcc192ded 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java
@@ -23,6 +23,7 @@
 
 import org.bson.Document;
 import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -67,6 +68,7 @@ public static UnsetOperation unset(String... fields) {
 	 * @param fields must not be {@literal null}.
 	 * @return new instance of {@link UnsetOperation}.
 	 */
+	@Contract("_ -> new")
 	public UnsetOperation and(String... fields) {
 
 		List<Object> target = new ArrayList<>(this.fields);
@@ -80,6 +82,7 @@ public UnsetOperation and(String... fields) {
 	 * @param fields must not be {@literal null}.
 	 * @return new instance of {@link UnsetOperation}.
 	 */
+	@Contract("_ -> new")
 	public UnsetOperation and(Field... fields) {
 
 		List<Object> target = new ArrayList<>(this.fields);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java
index d59ae01b12..cc0552cd1c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java
@@ -16,8 +16,9 @@
 package org.springframework.data.mongodb.core.aggregation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -201,6 +202,8 @@ public static PathBuilder newBuilder() {
 		@Override
 		public UnwindOperation preserveNullAndEmptyArrays() {
 
+			Assert.notNull(field, "Path needs to be set first");
+
 			if (arrayIndex != null) {
 				return new UnwindOperation(field, arrayIndex, true);
 			}
@@ -211,6 +214,8 @@ public UnwindOperation preserveNullAndEmptyArrays() {
 		@Override
 		public UnwindOperation skipNullAndEmptyArrays() {
 
+			Assert.notNull(field, "Path needs to be set first");
+
 			if (arrayIndex != null) {
 				return new UnwindOperation(field, arrayIndex, false);
 			}
@@ -219,6 +224,7 @@ public UnwindOperation skipNullAndEmptyArrays() {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public EmptyArraysBuilder arrayIndex(String field) {
 
 			Assert.hasText(field, "'ArrayIndex' must not be null or empty");
@@ -227,6 +233,7 @@ public EmptyArraysBuilder arrayIndex(String field) {
 		}
 
 		@Override
+		@Contract("-> this")
 		public EmptyArraysBuilder noArrayIndex() {
 
 			arrayIndex = null;
@@ -234,6 +241,7 @@ public EmptyArraysBuilder noArrayIndex() {
 		}
 
 		@Override
+		@Contract("_ -> this")
 		public UnwindOperationBuilder path(String path) {
 
 			Assert.hasText(path, "'Path' must not be null or empty");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java
index 8e676c72bc..b5a9ca0f21 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java
@@ -22,8 +22,8 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -223,8 +223,7 @@ public static class Let implements AggregationExpression {
 
 		private final List<ExpressionVariable> vars;
 
-		@Nullable //
-		private final AggregationExpression expression;
+		private final @Nullable AggregationExpression expression;
 
 		private Let(List<ExpressionVariable> vars, @Nullable AggregationExpression expression) {
 
@@ -333,6 +332,7 @@ private Document getMappedVariable(ExpressionVariable var, AggregationOperationC
 			return new Document(var.variableName, var.expression);
 		}
 
+		@SuppressWarnings("NullAway")
 		private Object getMappedIn(AggregationOperationContext context) {
 			return expression.toDocument(new NestedDelegatingExpressionAggregationOperationContext(context,
 					this.vars.stream().map(var -> Fields.field(var.variableName)).collect(Collectors.toList())));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java
index 0e30b8b855..2769990ca6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java
@@ -3,6 +3,6 @@
  * 
  * @since 1.3
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.aggregation;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java
index 3e08dc1014..9ada2014a0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Core Spring Data MongoDB annotations not limited to a special use case (like Query,...).
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.annotation;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
index 7a01677939..9b1c744be2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java
@@ -20,6 +20,7 @@
 
 import org.bson.types.Code;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.core.convert.support.DefaultConversionService;
@@ -31,7 +32,6 @@
 import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter;
 import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter;
 import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java
index 40afbb8c10..3b4dd99d4e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.DBRef;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java
index 0235694030..ee1f568494 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java
@@ -18,10 +18,10 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.DBRef;
@@ -60,7 +60,7 @@ Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbR
 	 * @param id will never be {@literal null}.
 	 * @return new instance of {@link DBRef}.
 	 */
-	default DBRef createDbRef(@Nullable org.springframework.data.mongodb.core.mapping.DBRef annotation,
+	default DBRef createDbRef(org.springframework.data.mongodb.core.mapping.@Nullable DBRef annotation,
 			MongoPersistentEntity<?> entity, Object id) {
 
 		if (annotation != null && StringUtils.hasText(annotation.db())) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java
index bf6b882375..fd80029118 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 
 /**
@@ -31,5 +32,5 @@ public interface DbRefResolverCallback {
 	 * @param property will never be {@literal null}.
 	 * @return
 	 */
-	Object resolve(MongoPersistentProperty property);
+	@Nullable Object resolve(MongoPersistentProperty property);
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java
index 22b1ce7981..13c0198aa0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java
@@ -18,13 +18,12 @@
 import java.util.function.Function;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.DBRef;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java
index de66c3ea94..c72bc4b886 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java
@@ -25,6 +25,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.MongoDatabaseUtils;
@@ -32,8 +33,8 @@
 import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
 import com.mongodb.DBRef;
@@ -71,7 +72,7 @@ public DefaultDbRefResolver(MongoDatabaseFactory mongoDbFactory) {
 	}
 
 	@Override
-	public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback,
+	public @Nullable Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback,
 			DbRefProxyHandler handler) {
 
 		Assert.notNull(property, "Property must not be null");
@@ -86,7 +87,7 @@ public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbr
 	}
 
 	@Override
-	public Document fetch(DBRef dbRef) {
+	public @Nullable Document fetch(DBRef dbRef) {
 		return getReferenceLoader().fetchOne(
 				DocumentReferenceQuery.forSingleDocument(Filters.eq(FieldName.ID.name(), dbRef.getId())),
 				ReferenceCollection.fromDBRef(dbRef));
@@ -171,7 +172,7 @@ private boolean isLazyDbRef(MongoPersistentProperty property) {
 	private static Stream<Document> documentWithId(Object identifier, Collection<Document> documents) {
 
 		return documents.stream() //
-				.filter(it -> it.get(BasicMongoPersistentProperty.ID_FIELD_NAME).equals(identifier)) //
+				.filter(it -> ObjectUtils.nullSafeEquals(it.get(BasicMongoPersistentProperty.ID_FIELD_NAME), identifier)) //
 				.limit(1);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java
index 82e5c9d0eb..376e0dd8cd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java
@@ -17,6 +17,7 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 
@@ -53,7 +54,7 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback {
 	}
 
 	@Override
-	public Object resolve(MongoPersistentProperty property) {
+	public @Nullable Object resolve(MongoPersistentProperty property) {
 		return resolver.getValueInternal(property, surroundingObject, evaluator, path);
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java
index 2c2b52afd5..f5db41f006 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java
@@ -23,6 +23,7 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.convert.CustomConversions;
 import org.springframework.data.convert.DefaultTypeMapper;
 import org.springframework.data.convert.SimpleTypeInformationMapper;
@@ -32,7 +33,6 @@
 import org.springframework.data.mapping.PersistentEntity;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 import com.mongodb.BasicDBList;
@@ -114,7 +114,7 @@ public DefaultMongoTypeMapper(@Nullable String typeKey, List<? extends TypeInfor
 	}
 
 	private DefaultMongoTypeMapper(@Nullable String typeKey, TypeAliasAccessor<Bson> accessor,
-			MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
+			@Nullable MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext,
 			List<? extends TypeInformationMapper> mappers) {
 
 		super(accessor, mappingContext, mappers);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
index a7b3d6f21f..4df7c02f91 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
@@ -20,6 +20,7 @@
 import java.util.Collections;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.support.PersistenceExceptionTranslator;
 import org.springframework.data.mongodb.core.mapping.DBRef;
 import org.springframework.data.mongodb.core.mapping.DocumentReference;
@@ -63,7 +64,8 @@ public DefaultReferenceResolver(ReferenceLoader referenceLoader, PersistenceExce
 	}
 
 	@Override
-	public Object resolveReference(MongoPersistentProperty property, Object source,
+	@SuppressWarnings("NullAway")
+	public @Nullable Object resolveReference(MongoPersistentProperty property, Object source,
 			ReferenceLookupDelegate referenceLookupDelegate, MongoEntityReader entityReader) {
 
 		LookupFunction lookupFunction = (property.isCollectionLike() || property.isMap()) ? collectionLookupFunction
@@ -84,6 +86,7 @@ public Object resolveReference(MongoPersistentProperty property, Object source,
 	 * @see DBRef#lazy()
 	 * @see DocumentReference#lazy()
 	 */
+	@SuppressWarnings("NullAway")
 	protected boolean isLazyReference(MongoPersistentProperty property) {
 
 		if (property.isDocumentReference()) {
@@ -106,6 +109,7 @@ LazyLoadingProxyFactory getProxyFactory() {
 		return proxyFactory;
 	}
 
+	@SuppressWarnings("NullAway")
 	private Object createLazyLoadingProxy(MongoPersistentProperty property, Object source,
 			ReferenceLookupDelegate referenceLookupDelegate, LookupFunction lookupFunction, MongoEntityReader entityReader) {
 		return proxyFactory.createLazyLoadingProxy(property,
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
index c795add9c8..ff50dd5df3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
@@ -21,11 +21,11 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.DBObject;
@@ -119,8 +119,7 @@ public void put(MongoPersistentProperty prop, @Nullable Object value) {
 	 * @param property must not be {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public Object get(MongoPersistentProperty property) {
+	public @Nullable Object get(MongoPersistentProperty property) {
 		return BsonUtils.resolveValue(document, getFieldName(property));
 	}
 
@@ -131,8 +130,7 @@ public Object get(MongoPersistentProperty property) {
 	 * @param entity must not be {@literal null}.
 	 * @return
 	 */
-	@Nullable
-	public Object getRawId(MongoPersistentEntity<?> entity) {
+	public @Nullable Object getRawId(MongoPersistentEntity<?> entity) {
 		return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.get(document, FieldName.ID.name());
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
index 8429584a6f..e03d215088 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
@@ -70,6 +70,7 @@ class DocumentPointerFactory {
 		this.cache = new WeakHashMap<>();
 	}
 
+	@SuppressWarnings("NullAway")
 	DocumentPointer<?> computePointer(
 			MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
 			MongoPersistentProperty property, Object value, Class<?> typeHint) {
@@ -87,7 +88,7 @@ DocumentPointer<?> computePointer(
 
 		if (usesDefaultLookup(property)) {
 
-			MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
+			MongoPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
 			Object idValue = persistentEntity.getIdentifierAccessor(value).getIdentifier();
 
 			if (idProperty.hasExplicitWriteTarget()
@@ -114,6 +115,7 @@ DocumentPointer<?> computePointer(
 				.getDocumentPointer(mappingContext, persistentEntity, propertyAccessor);
 	}
 
+	@SuppressWarnings("NullAway")
 	private boolean usesDefaultLookup(MongoPersistentProperty property) {
 
 		if (property.isDocumentReference()) {
@@ -216,9 +218,16 @@ Object updatePlaceholders(org.bson.Document source, org.bson.Document target,
 					MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(entry.getKey());
 					if (persistentProperty != null && persistentProperty.isEntity()) {
 
-						MongoPersistentEntity<?> nestedEntity = mappingContext.getPersistentEntity(persistentProperty.getType());
-						target.put(entry.getKey(), updatePlaceholders(document, new Document(), mappingContext,
-								nestedEntity, nestedEntity.getPropertyAccessor(propertyAccessor.getProperty(persistentProperty))));
+						MongoPersistentEntity<?> nestedEntity = mappingContext.getRequiredPersistentEntity(persistentProperty.getType());
+						Object propertyValue = propertyAccessor.getProperty(persistentProperty);
+
+						if(propertyValue == null) {
+							target.put(entry.getKey(), propertyValue);
+						} else {
+							PersistentPropertyAccessor<?> nestedAccessor = nestedEntity.getPropertyAccessor(propertyValue);
+							target.put(entry.getKey(), updatePlaceholders(document, new Document(), mappingContext,
+								nestedEntity, nestedAccessor));
+						}
 					} else {
 						target.put(entry.getKey(), updatePlaceholders((Document) entry.getValue(), new Document(), mappingContext,
 								persistentEntity, propertyAccessor));
@@ -236,7 +245,7 @@ Object updatePlaceholders(org.bson.Document source, org.bson.Document target,
 					String fieldName = entry.getKey().equals(FieldName.ID.name()) ? "id" : entry.getKey();
 					if (!fieldName.contains(".")) {
 
-						Object targetValue = propertyAccessor.getProperty(persistentEntity.getPersistentProperty(fieldName));
+						Object targetValue = propertyAccessor.getProperty(persistentEntity.getRequiredPersistentProperty(fieldName));
 						target.put(attribute, targetValue);
 						continue;
 					}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java
index ea5ce01b44..a41e17c0ec 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java
@@ -18,11 +18,11 @@
 import java.util.Map;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.expression.MapAccessor;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.PropertyAccessor;
 import org.springframework.expression.TypedValue;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link PropertyAccessor} to allow entity based field access to {@link Document}s.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
index bf21781058..b1e894efe9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
@@ -15,7 +15,8 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * The source object to resolve document references upon. Encapsulates the actual source and the reference specific
@@ -56,8 +57,7 @@ public Object getSelf() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public Object getTargetSource() {
+	public @Nullable Object getTargetSource() {
 		return targetSource;
 	}
 
@@ -67,8 +67,7 @@ public Object getTargetSource() {
 	 * @param source
 	 * @return
 	 */
-	@Nullable
-	static Object getTargetSource(Object source) {
+	static @Nullable Object getTargetSource(@Nullable Object source) {
 		return source instanceof DocumentReferenceSource referenceSource ? referenceSource.getTargetSource() : source;
 	}
 
@@ -78,7 +77,8 @@ static Object getTargetSource(Object source) {
 	 * @param self
 	 * @return
 	 */
-	static Object getSelf(Object self) {
+	@Contract("null -> null")
+	static @Nullable Object getSelf(@Nullable Object self) {
 		return self instanceof DocumentReferenceSource referenceSource ? referenceSource.getSelf() : self;
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java
index 2bca260b79..ae73ab68bd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java
@@ -25,6 +25,7 @@
 
 import org.bson.Document;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.ReadingConverter;
 import org.springframework.data.convert.WritingConverter;
@@ -45,6 +46,7 @@
 import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
 import org.springframework.data.mongodb.core.geo.Sphere;
 import org.springframework.data.mongodb.core.query.GeoCommand;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
@@ -130,7 +132,8 @@ enum DocumentToPointConverter implements Converter<Document, Point> {
 		INSTANCE;
 
 		@Override
-		public Point convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Point convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -157,7 +160,8 @@ enum PointToDocumentConverter implements Converter<Point, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(Point source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable Point source) {
 			return source == null ? null : new Document("x", source.getX()).append("y", source.getY());
 		}
 	}
@@ -174,7 +178,8 @@ enum BoxToDocumentConverter implements Converter<Box, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(Box source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable Box source) {
 
 			if (source == null) {
 				return null;
@@ -199,7 +204,9 @@ enum DocumentToBoxConverter implements Converter<Document, Box> {
 		INSTANCE;
 
 		@Override
-		public Box convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		@SuppressWarnings("NullAway")
+		public @Nullable Box convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -223,7 +230,8 @@ enum CircleToDocumentConverter implements Converter<Circle, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(Circle source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable Circle source) {
 
 			if (source == null) {
 				return null;
@@ -249,7 +257,8 @@ enum DocumentToCircleConverter implements Converter<Document, Circle> {
 		INSTANCE;
 
 		@Override
-		public Circle convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Circle convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -286,7 +295,8 @@ enum SphereToDocumentConverter implements Converter<Sphere, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(Sphere source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable Sphere source) {
 
 			if (source == null) {
 				return null;
@@ -312,7 +322,8 @@ enum DocumentToSphereConverter implements Converter<Document, Sphere> {
 		INSTANCE;
 
 		@Override
-		public Sphere convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Sphere convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -349,7 +360,8 @@ enum PolygonToDocumentConverter implements Converter<Polygon, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(Polygon source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable Polygon source) {
 
 			if (source == null) {
 				return null;
@@ -381,18 +393,20 @@ enum DocumentToPolygonConverter implements Converter<Document, Polygon> {
 
 		@Override
 		@SuppressWarnings({ "unchecked" })
-		public Polygon convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Polygon convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
 			}
 
 			List<Document> points = (List<Document>) source.get("points");
-			List<Point> newPoints = new ArrayList<>(points.size());
+			Assert.notNull(points, "Points elements of polygon must not be null");
 
+			List<Point> newPoints = new ArrayList<>(points.size());
 			for (Document element : points) {
 
-				Assert.notNull(element, "Point elements of polygon must not be null");
+				Assert.notNull(element, "Point elements of polygon must not contain null");
 				newPoints.add(DocumentToPointConverter.INSTANCE.convert(element));
 			}
 
@@ -412,7 +426,8 @@ enum GeoCommandToDocumentConverter implements Converter<GeoCommand, Document> {
 
 		@Override
 		@SuppressWarnings("rawtypes")
-		public Document convert(GeoCommand source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable GeoCommand source) {
 
 			if (source == null) {
 				return null;
@@ -463,7 +478,8 @@ enum GeoJsonToDocumentConverter implements Converter<GeoJson<?>, Document> {
 		INSTANCE;
 
 		@Override
-		public Document convert(GeoJson<?> source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable Document convert(@Nullable GeoJson<?> source) {
 
 			if (source == null) {
 				return null;
@@ -490,7 +506,7 @@ public Document convert(GeoJson<?> source) {
 
 		private Object convertIfNecessary(Object candidate) {
 
-			if (candidate instanceof GeoJson geoJson) {
+			if (candidate instanceof GeoJson<?> geoJson) {
 				return convertIfNecessary(geoJson.getCoordinates());
 			}
 
@@ -551,7 +567,8 @@ enum DocumentToGeoJsonPointConverter implements Converter<Document, GeoJsonPoint
 
 		@Override
 		@SuppressWarnings("unchecked")
-		public GeoJsonPoint convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonPoint convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -560,7 +577,10 @@ public GeoJsonPoint convert(Document source) {
 			Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"),
 					String.format("Cannot convert type '%s' to Point", source.get("type")));
 
-			List<Number> dbl = (List<Number>) source.get("coordinates");
+			if(!(source.get("coordinates") instanceof List<?> sourceCoordinates)) {
+				throw new IllegalArgumentException("Coordinates need to be present");
+			}
+			List<Number> dbl = (List<Number>) sourceCoordinates;
 			return new GeoJsonPoint(toPrimitiveDoubleValue(dbl.get(0)), toPrimitiveDoubleValue(dbl.get(1)));
 		}
 	}
@@ -574,7 +594,8 @@ enum DocumentToGeoJsonPolygonConverter implements Converter<Document, GeoJsonPol
 		INSTANCE;
 
 		@Override
-		public GeoJsonPolygon convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonPolygon convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -596,7 +617,8 @@ enum DocumentToGeoJsonMultiPolygonConverter implements Converter<Document, GeoJs
 		INSTANCE;
 
 		@Override
-		public GeoJsonMultiPolygon convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonMultiPolygon convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -606,8 +628,9 @@ public GeoJsonMultiPolygon convert(Document source) {
 					String.format("Cannot convert type '%s' to MultiPolygon", source.get("type")));
 
 			List<?> dbl = (List<?>) source.get("coordinates");
-			List<GeoJsonPolygon> polygones = new ArrayList<>();
+			Assert.notNull(dbl, "Source needs to contain coordinates");
 
+			List<GeoJsonPolygon> polygones = new ArrayList<>(dbl.size());
 			for (Object polygon : dbl) {
 				polygones.add(toGeoJsonPolygon((List<?>) polygon));
 			}
@@ -625,7 +648,8 @@ enum DocumentToGeoJsonLineStringConverter implements Converter<Document, GeoJson
 		INSTANCE;
 
 		@Override
-		public GeoJsonLineString convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonLineString convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -649,7 +673,8 @@ enum DocumentToGeoJsonMultiPointConverter implements Converter<Document, GeoJson
 		INSTANCE;
 
 		@Override
-		public GeoJsonMultiPoint convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonMultiPoint convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -673,7 +698,8 @@ enum DocumentToGeoJsonMultiLineStringConverter implements Converter<Document, Ge
 		INSTANCE;
 
 		@Override
-		public GeoJsonMultiLineString convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonMultiLineString convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -682,10 +708,13 @@ public GeoJsonMultiLineString convert(Document source) {
 			Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiLineString"),
 					String.format("Cannot convert type '%s' to MultiLineString", source.get("type")));
 
-			List<GeoJsonLineString> lines = new ArrayList<>();
-			List<?> cords = (List<?>) source.get("coordinates");
+			if(!(source.get("coordinates") instanceof List<?> coordinates)) {
+				throw new IllegalArgumentException("coordinates need to be present");
+			}
+			
+			List<GeoJsonLineString> lines = new ArrayList<>(coordinates.size());
 
-			for (Object line : cords) {
+			for (Object line : coordinates) {
 				lines.add(new GeoJsonLineString(toListOfPoint((List<?>) line)));
 			}
 			return new GeoJsonMultiLineString(lines);
@@ -700,9 +729,9 @@ enum DocumentToGeoJsonGeometryCollectionConverter implements Converter<Document,
 
 		INSTANCE;
 
-		@SuppressWarnings("rawtypes")
 		@Override
-		public GeoJsonGeometryCollection convert(Document source) {
+		@Contract("null -> null; !null -> !null")
+		public @Nullable GeoJsonGeometryCollection convert(@Nullable Document source) {
 
 			if (source == null) {
 				return null;
@@ -711,8 +740,12 @@ public GeoJsonGeometryCollection convert(Document source) {
 			Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"),
 					String.format("Cannot convert type '%s' to GeometryCollection", source.get("type")));
 
-			List<GeoJson<?>> geometries = new ArrayList<>();
-			for (Object o : (List) source.get("geometries")) {
+			if(!(source.get("geometries") instanceof List<?> sourceGeometries)) {
+				throw new IllegalArgumentException("Geometries need to be present");
+			}
+			
+			List<GeoJson<?>> geometries = new ArrayList<>(sourceGeometries.size());
+			for (Object o : sourceGeometries) {
 				geometries.add(toGenericGeoJson((Document) o));
 			}
 
@@ -732,7 +765,10 @@ static List<Double> toList(Point point) {
 	 * @since 1.7
 	 */
 	@SuppressWarnings("unchecked")
-	static List<Point> toListOfPoint(List<?> listOfCoordinatePairs) {
+	@Contract("null -> fail")
+	static List<Point> toListOfPoint(@Nullable List<?> listOfCoordinatePairs) {
+
+		Assert.notNull(listOfCoordinatePairs, "ListOfCoordinatePairs must not be null");
 
 		List<Point> points = new ArrayList<>(listOfCoordinatePairs.size());
 
@@ -755,7 +791,10 @@ static List<Point> toListOfPoint(List<?> listOfCoordinatePairs) {
 	 * @return never {@literal null}.
 	 * @since 1.7
 	 */
-	static GeoJsonPolygon toGeoJsonPolygon(List<?> dbList) {
+	@Contract("null -> fail")
+	static GeoJsonPolygon toGeoJsonPolygon(@Nullable List<?> dbList) {
+
+		Assert.notNull(dbList, "DbList must not be null");
 
 		GeoJsonPolygon polygon = new GeoJsonPolygon(toListOfPoint((List<?>) dbList.get(0)));
 		return dbList.size() > 1 ? polygon.withInnerRing(toListOfPoint((List<?>) dbList.get(1))) : polygon;
@@ -794,7 +833,8 @@ private static GeoJson<?> toGenericGeoJson(Document source) {
 		throw new IllegalArgumentException(String.format("No converter found capable of converting GeoJson type %s", type));
 	}
 
-	private static double toPrimitiveDoubleValue(Object value) {
+	@Contract("null -> fail")
+	private static double toPrimitiveDoubleValue(@Nullable Object value) {
 
 		Assert.isInstanceOf(Number.class, value, "Argument must be a Number");
 		return NumberUtils.convertNumberToTargetClass((Number) value, Double.class);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java
index 77aac55813..6329d74d4f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.DBRef;
 
@@ -53,8 +53,7 @@ public interface LazyLoadingProxy {
 	 * @return can be {@literal null}.
 	 * @since 3.3
 	 */
-	@Nullable
-	default Object getSource() {
+	default @Nullable Object getSource() {
 		return toDBRef();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java
index 76539ea431..eff58e7bd4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java
@@ -30,6 +30,8 @@
 import org.aopalliance.intercept.MethodInvocation;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.aop.framework.ProxyFactory;
 import org.springframework.cglib.core.SpringNamingPolicy;
 import org.springframework.cglib.proxy.Callback;
@@ -43,7 +45,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.Lock;
 import org.springframework.data.util.Lock.AcquiredLock;
-import org.springframework.lang.Nullable;
 import org.springframework.objenesis.SpringObjenesis;
 import org.springframework.util.ReflectionUtils;
 
@@ -124,7 +125,7 @@ private ProxyFactory prepareProxyFactory(Class<?> propertyType, Supplier<LazyLoa
 	}
 
 	public Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefResolverCallback callback,
-			Object source) {
+			@Nullable Object source) {
 
 		Class<?> propertyType = property.getType();
 		LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, callback, source, exceptionTranslator);
@@ -160,6 +161,7 @@ private Class<?> getEnhancedTypeFor(Class<?> type) {
 		return enhancer.createClass();
 	}
 
+	@NullUnmarked
 	public static class LazyLoadingInterceptor
 			implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable {
 
@@ -180,10 +182,10 @@ public static class LazyLoadingInterceptor
 		private final Lock readLock = Lock.of(rwLock.readLock());
 		private final Lock writeLock = Lock.of(rwLock.writeLock());
 
-		private final MongoPersistentProperty property;
-		private final DbRefResolverCallback callback;
-		private final Object source;
-		private final PersistenceExceptionTranslator exceptionTranslator;
+		private final @Nullable MongoPersistentProperty property;
+		private final @Nullable DbRefResolverCallback callback;
+		private final @Nullable Object source;
+		private final @Nullable PersistenceExceptionTranslator exceptionTranslator;
 		private volatile boolean resolved;
 		private @Nullable Object result;
 
@@ -191,18 +193,17 @@ public static class LazyLoadingInterceptor
 		 * @return a {@link LazyLoadingInterceptor} that just continues with the invocation.
 		 * @since 4.0
 		 */
+		@SuppressWarnings("NullAway")
 		public static LazyLoadingInterceptor none() {
 
 			return new LazyLoadingInterceptor(null, null, null, null) {
-				@Nullable
 				@Override
-				public Object invoke(MethodInvocation invocation) throws Throwable {
+				public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {
 					return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
 				}
 
-				@Nullable
 				@Override
-				public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
+				public @Nullable Object intercept(Object o, Method method, @Nullable Object @Nullable[] args, @Nullable MethodProxy proxy) throws Throwable {
 
 					ReflectionUtils.makeAccessible(method);
 					return method.invoke(o, args);
@@ -210,8 +211,8 @@ public Object intercept(Object o, Method method, Object[] args, MethodProxy prox
 			};
 		}
 
-		public LazyLoadingInterceptor(MongoPersistentProperty property, DbRefResolverCallback callback, Object source,
-				PersistenceExceptionTranslator exceptionTranslator) {
+		public LazyLoadingInterceptor(@Nullable MongoPersistentProperty property, @Nullable DbRefResolverCallback callback, @Nullable Object source,
+			@Nullable PersistenceExceptionTranslator exceptionTranslator) {
 
 			this.property = property;
 			this.callback = callback;
@@ -219,15 +220,13 @@ public LazyLoadingInterceptor(MongoPersistentProperty property, DbRefResolverCal
 			this.exceptionTranslator = exceptionTranslator;
 		}
 
-		@Nullable
 		@Override
-		public Object invoke(MethodInvocation invocation) throws Throwable {
+		public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {
 			return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null);
 		}
 
-		@Nullable
 		@Override
-		public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
+		public @Nullable Object intercept(Object o, Method method, @Nullable Object @Nullable[] args, @Nullable MethodProxy proxy) throws Throwable {
 
 			if (INITIALIZE_METHOD.equals(method)) {
 				return ensureResolved();
@@ -247,7 +246,7 @@ public Object intercept(Object o, Method method, Object[] args, MethodProxy prox
 					return proxyToString(source);
 				}
 
-				if (ReflectionUtils.isEqualsMethod(method)) {
+				if (ReflectionUtils.isEqualsMethod(method) && args != null) {
 					return proxyEquals(o, args[0]);
 				}
 
@@ -347,8 +346,8 @@ private void readObject(ObjectInputStream in) throws IOException {
 			}
 		}
 
-		@Nullable
-		private Object resolve() {
+		@SuppressWarnings("NullAway")
+		private @Nullable Object resolve() {
 
 			try (AcquiredLock l = readLock.lock()) {
 				if (resolved) {
@@ -370,7 +369,7 @@ private Object resolve() {
 				return writeLock.execute(() -> callback.resolve(property));
 			} catch (RuntimeException ex) {
 
-				DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(ex);
+				DataAccessException translatedException =  exceptionTranslator != null ? exceptionTranslator.translateExceptionIfPossible(ex) : null;
 
 				if (translatedException instanceof ClientSessionException) {
 					throw new LazyLoadingException("Unable to lazily resolve DBRef; Invalid session state", ex);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 864cc1c3e3..2d073869ae 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -39,7 +39,7 @@
 import org.bson.conversions.Bson;
 import org.bson.json.JsonReader;
 import org.bson.types.ObjectId;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanClassLoaderAware;
 import org.springframework.context.ApplicationContext;
@@ -53,6 +53,7 @@
 import org.springframework.core.env.StandardEnvironment;
 import org.springframework.data.annotation.Reference;
 import org.springframework.data.convert.CustomConversions;
+import org.springframework.data.convert.PropertyValueConversions;
 import org.springframework.data.convert.PropertyValueConverter;
 import org.springframework.data.convert.TypeMapper;
 import org.springframework.data.convert.ValueConversionContext;
@@ -71,7 +72,6 @@
 import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
 import org.springframework.data.mapping.model.PropertyValueProvider;
 import org.springframework.data.mapping.model.SpELContext;
-import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider;
 import org.springframework.data.mongodb.CodecRegistryProvider;
@@ -95,7 +95,7 @@
 import org.springframework.data.util.Predicates;
 import org.springframework.data.util.TypeInformation;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.CollectionUtils;
@@ -158,7 +158,7 @@ public class MappingMongoConverter extends AbstractMongoConverter
 
 	protected @Nullable ApplicationContext applicationContext;
 	protected @Nullable Environment environment;
-	protected MongoTypeMapper typeMapper;
+	protected @Nullable MongoTypeMapper typeMapper;
 	protected @Nullable String mapKeyDotReplacement = null;
 	protected @Nullable CodecRegistryProvider codecRegistryProvider;
 
@@ -308,7 +308,9 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
 		this.environment = applicationContext.getEnvironment();
 		this.spELContext = new SpELContext(this.spELContext, applicationContext);
 		this.projectionFactory.setBeanFactory(applicationContext);
-		this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
+		if(applicationContext.getClassLoader() != null) {
+			this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
+		}
 
 		if (entityCallbacks == null) {
 			setEntityCallbacks(EntityCallbacks.create(applicationContext));
@@ -419,7 +421,7 @@ FieldName getFieldName(MongoPersistentProperty prop) {
 		return accessor.getBean();
 	}
 
-	private Object doReadOrProject(ConversionContext context, Bson source, TypeInformation<?> typeHint,
+	private Object doReadOrProject(ConversionContext context, @Nullable Bson source, TypeInformation<?> typeHint,
 			EntityProjection<?, ?> typeDescriptor) {
 
 		if (typeDescriptor.isProjection()) {
@@ -434,12 +436,12 @@ static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor
 		Map<String, Object> map = new LinkedHashMap<>();
 
 		@Override
-		public void setProperty(PersistentProperty<?> persistentProperty, Object o) {
+		public void setProperty(PersistentProperty<?> persistentProperty, @Nullable Object o) {
 			map.put(persistentProperty.getName(), o);
 		}
 
 		@Override
-		public Object getProperty(PersistentProperty<?> persistentProperty) {
+		public @Nullable Object getProperty(PersistentProperty<?> persistentProperty) {
 			return map.get(persistentProperty.getName());
 		}
 
@@ -467,10 +469,14 @@ protected <S extends Object> S read(TypeInformation<S> type, Bson bson) {
 	 * @return the converted object, will never be {@literal null}.
 	 * @since 3.2
 	 */
-	@SuppressWarnings("unchecked")
-	protected <S extends Object> S readDocument(ConversionContext context, Bson bson,
+	@SuppressWarnings({"unchecked","NullAway"})
+	protected <S extends Object> S readDocument(ConversionContext context, @Nullable Bson bson,
 			TypeInformation<? extends S> typeHint) {
 
+		if(bson == null) {
+			bson  = new Document();
+		}
+
 		Document document = bson instanceof BasicDBObject dbObject ? new Document(dbObject) : (Document) bson;
 		TypeInformation<? extends S> typeToRead = getTypeMapper().readType(document, typeHint);
 		Class<? extends S> rawType = typeToRead.getType();
@@ -546,7 +552,7 @@ public EvaluatingDocumentAccessor(Bson document) {
 		}
 
 		@Override
-		public <T> T evaluate(String expression) {
+		public <T> @Nullable T evaluate(String expression) {
 			return expressionEvaluatorFactory.create(getDocument()).evaluate(expression);
 		}
 	}
@@ -600,8 +606,7 @@ private <S> S populateProperties(ConversionContext context, MongoPersistentEntit
 	 * Reads the identifier from either the bean backing the {@link PersistentPropertyAccessor} or the source document in
 	 * case the identifier has not be populated yet. In this case the identifier is set on the bean for further reference.
 	 */
-	@Nullable
-	private Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor<?> accessor,
+	private @Nullable Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor<?> accessor,
 			DocumentAccessor document, MongoPersistentEntity<?> entity, ValueExpressionEvaluator evaluator) {
 
 		Object rawId = document.getRawId(entity);
@@ -685,8 +690,7 @@ private DbRefResolverCallback getDbRefResolverCallback(ConversionContext context
 				(prop, bson, e, path) -> MappingMongoConverter.this.getValueInternal(context, prop, bson, e));
 	}
 
-	@Nullable
-	private Object readAssociation(Association<MongoPersistentProperty> association, DocumentAccessor documentAccessor,
+	private @Nullable Object readAssociation(Association<MongoPersistentProperty> association, DocumentAccessor documentAccessor,
 			DbRefProxyHandler handler, DbRefResolverCallback callback, ConversionContext context) {
 
 		MongoPersistentProperty property = association.getInverse();
@@ -746,8 +750,8 @@ && peek(collection) instanceof Document) {
 		}
 	}
 
-	@Nullable
-	private Object readUnwrapped(ConversionContext context, DocumentAccessor documentAccessor,
+	@SuppressWarnings("NullAway")
+	private @Nullable Object readUnwrapped(ConversionContext context, DocumentAccessor documentAccessor,
 			MongoPersistentProperty prop, MongoPersistentEntity<?> unwrappedEntity) {
 
 		if (prop.findAnnotation(Unwrapped.class).onEmpty().equals(OnEmpty.USE_EMPTY)) {
@@ -763,13 +767,14 @@ private Object readUnwrapped(ConversionContext context, DocumentAccessor documen
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public DBRef toDBRef(Object object, @Nullable MongoPersistentProperty referringProperty) {
 
 		org.springframework.data.mongodb.core.mapping.DBRef annotation;
 
 		if (referringProperty != null) {
 			annotation = referringProperty.getDBRef();
-			Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef");
+			Assert.notNull(annotation, "The referenced property has to be mapped with @DBRef");
 		}
 
 		// DATAMONGO-913
@@ -781,6 +786,7 @@ public DBRef toDBRef(Object object, @Nullable MongoPersistentProperty referringP
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public DocumentPointer toDocumentPointer(Object source, @Nullable MongoPersistentProperty referringProperty) {
 
 		if (source instanceof LazyLoadingProxy proxy) {
@@ -800,6 +806,7 @@ public DocumentPointer toDocumentPointer(Object source, @Nullable MongoPersisten
 		throw new IllegalArgumentException("The referringProperty is neither a DBRef nor a document reference");
 	}
 
+	@SuppressWarnings("NullAway")
 	DocumentPointer<?> createDocumentPointer(Object source, @Nullable MongoPersistentProperty referringProperty) {
 
 		if (referringProperty == null) {
@@ -864,7 +871,7 @@ private boolean requiresTypeHint(Class<?> type) {
 	/**
 	 * Internal write conversion method which should be used for nested invocations.
 	 */
-	@SuppressWarnings("unchecked")
+	@SuppressWarnings({"unchecked","NullAway"})
 	protected void writeInternal(@Nullable Object obj, Bson bson, @Nullable TypeInformation<?> typeHint) {
 
 		if (null == obj) {
@@ -1268,6 +1275,7 @@ protected String potentiallyEscapeMapKey(String source) {
 	 *
 	 * @param key
 	 */
+	@SuppressWarnings("NullAway")
 	private String potentiallyConvertMapKey(Object key) {
 
 		if (key instanceof String stringValue) {
@@ -1333,20 +1341,23 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist
 				property.hasExplicitWriteTarget() ? property.getFieldType() : Object.class));
 	}
 
-	@Nullable
 	@SuppressWarnings("unchecked")
-	private Object applyPropertyConversion(@Nullable Object value, MongoPersistentProperty property,
+	private @Nullable Object applyPropertyConversion(@Nullable Object value, MongoPersistentProperty property,
 			PersistentPropertyAccessor<?> persistentPropertyAccessor) {
 		MongoConversionContext context = new MongoConversionContext(new PropertyValueProvider<>() {
 
-			@Nullable
 			@Override
-			public <T> T getPropertyValue(MongoPersistentProperty property) {
+			public <T> @Nullable T getPropertyValue(MongoPersistentProperty property) {
 				return (T) persistentPropertyAccessor.getProperty(property);
 			}
 		}, property, this, spELContext);
-		PropertyValueConverter<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter = conversions
-				.getPropertyValueConversions().getValueConverter(property);
+
+		PropertyValueConversions propertyValueConversions = conversions.getPropertyValueConversions();
+		if(propertyValueConversions == null) {
+			return value;
+		}
+
+		PropertyValueConverter<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter = propertyValueConversions.getValueConverter(property);
 		return value != null ? valueConverter.write(value, context) : valueConverter.writeNull(context);
 	}
 
@@ -1354,8 +1365,9 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
 	 * Checks whether we have a custom conversion registered for the given value into an arbitrary simple Mongo type.
 	 * Returns the converted value if so. If not, we perform special enum handling or simply return the value as is.
 	 */
-	@Nullable
-	private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nullable Class<?> typeHint) {
+	@Contract("null, _-> null")
+	@SuppressWarnings("NullAway")
+	private @Nullable Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nullable Class<?> typeHint) {
 
 		if (value == null) {
 			return null;
@@ -1391,7 +1403,7 @@ private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nulla
 	 *
 	 * @since 3.2
 	 */
-	protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation<?> target) {
+	protected @Nullable Object getPotentiallyConvertedSimpleRead(@Nullable Object value, TypeInformation<?> target) {
 		return getPotentiallyConvertedSimpleRead(value, target.getType());
 	}
 
@@ -1399,10 +1411,11 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation
 	 * Checks whether we have a custom conversion for the given simple object. Converts the given value if so, applies
 	 * {@link Enum} handling or returns the value as is.
 	 */
+	@Contract("null, _ -> null; _, null -> param1")
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	private Object getPotentiallyConvertedSimpleRead(Object value, @Nullable Class<?> target) {
+	private @Nullable Object getPotentiallyConvertedSimpleRead(@Nullable Object value, @Nullable Class<?> target) {
 
-		if (target == null) {
+		if (target == null || value == null) {
 			return value;
 		}
 
@@ -1421,6 +1434,7 @@ private Object getPotentiallyConvertedSimpleRead(Object value, @Nullable Class<?
 		return doConvert(value, target);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty property) {
 
 		Assert.notNull(target, "Target object must not be null");
@@ -1456,8 +1470,7 @@ protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty pro
 		throw new MappingException("No id property found on class " + entity.getType());
 	}
 
-	@Nullable
-	private Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson,
+	private @Nullable Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson,
 			ValueExpressionEvaluator evaluator) {
 		return new MongoDbPropertyValueProvider(context, bson, evaluator).getPropertyValue(prop);
 	}
@@ -1472,11 +1485,12 @@ private Object getValueInternal(ConversionContext context, MongoPersistentProper
 	 * @since 3.2
 	 * @return the converted {@link Collection} or array, will never be {@literal null}.
 	 */
-	@SuppressWarnings("unchecked")
-	protected Object readCollectionOrArray(ConversionContext context, Collection<?> source,
+	@SuppressWarnings({"unchecked","NullAway"})
+	protected @Nullable Object readCollectionOrArray(ConversionContext context, @Nullable Collection<?> source,
 			TypeInformation<?> targetType) {
 
 		Assert.notNull(targetType, "Target type must not be null");
+		Assert.notNull(source, "Source must not be null");
 
 		Class<?> collectionType = targetType.isSubTypeOf(Collection.class) //
 				? targetType.getType() //
@@ -1518,7 +1532,7 @@ protected Object readCollectionOrArray(ConversionContext context, Collection<?>
 	 * @return the converted {@link Map}, will never be {@literal null}.
 	 * @since 3.2
 	 */
-	protected Map<Object, Object> readMap(ConversionContext context, Bson bson, TypeInformation<?> targetType) {
+	protected @Nullable Map<Object, Object> readMap(ConversionContext context, @Nullable Bson bson, TypeInformation<?> targetType) {
 
 		Assert.notNull(bson, "Document must not be null");
 		Assert.notNull(targetType, "TypeInformation must not be null");
@@ -1715,9 +1729,8 @@ private Object removeTypeInfo(Object object, boolean recursively) {
 		return document;
 	}
 
-	@Nullable
 	@SuppressWarnings("unchecked")
-	<T> T readValue(ConversionContext context, @Nullable Object value, TypeInformation<?> type) {
+	<T> @Nullable T readValue(ConversionContext context, @Nullable Object value, TypeInformation<?> type) {
 
 		if (value == null) {
 			return null;
@@ -1736,8 +1749,7 @@ <T> T readValue(ConversionContext context, @Nullable Object value, TypeInformati
 		return (T) context.convert(value, type);
 	}
 
-	@Nullable
-	private Object readDBRef(ConversionContext context, @Nullable DBRef dbref, TypeInformation<?> type) {
+	private @Nullable Object readDBRef(ConversionContext context, @Nullable DBRef dbref, TypeInformation<?> type) {
 
 		if (type.getType().equals(DBRef.class)) {
 			return dbref;
@@ -1802,6 +1814,7 @@ private <T> List<T> bulkReadAndConvertDBRefs(ConversionContext context, List<DBR
 		return targetList;
 	}
 
+	@SuppressWarnings("NullAway")
 	private void maybeEmitEvent(MongoMappingEvent<?> event) {
 
 		if (canPublishEvent()) {
@@ -1881,12 +1894,12 @@ public MappingMongoConverter with(MongoDatabaseFactory dbFactory) {
 		return target;
 	}
 
-	private <T extends Object> T doConvert(Object value, Class<? extends T> target) {
+	private <T extends Object> @Nullable T doConvert(Object value, Class<? extends T> target) {
 		return doConvert(value, target, null);
 	}
 
 	@SuppressWarnings("ConstantConditions")
-	private <T extends Object> T doConvert(Object value, Class<? extends T> target,
+	private <T extends Object> @Nullable T doConvert(Object value, Class<? extends T> target,
 			@Nullable Class<? extends T> fallback) {
 
 		if (conversionService.canConvert(value.getClass(), target) || fallback == null) {
@@ -1940,7 +1953,7 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo
 		final ConversionContext context;
 		final DocumentAccessor accessor;
 		final ValueExpressionEvaluator evaluator;
-		final SpELContext spELContext;
+		final @Nullable SpELContext spELContext;
 
 		/**
 		 * Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link ValueExpressionEvaluator} and
@@ -1963,7 +1976,7 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo
 		 * @param evaluator must not be {@literal null}.
 		 */
 		MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor,
-				ValueExpressionEvaluator evaluator, SpELContext spELContext) {
+				ValueExpressionEvaluator evaluator, @Nullable SpELContext spELContext) {
 
 			this.context = context;
 			this.accessor = accessor;
@@ -1972,9 +1985,8 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo
 		}
 
 		@Override
-		@Nullable
-		@SuppressWarnings("unchecked")
-		public <T> T getPropertyValue(MongoPersistentProperty property) {
+		@SuppressWarnings({"unchecked", "NullAway"})
+		public <T> @Nullable T getPropertyValue(MongoPersistentProperty property) {
 
 			String expression = property.getSpelExpression();
 			Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property);
@@ -2059,7 +2071,7 @@ public <T> T getPropertyValue(MongoPersistentProperty property) {
 	}
 
 	/**
-	 * Extension of {@link SpELExpressionParameterValueProvider} to recursively trigger value conversion on the raw
+	 * Extension of {@link ValueExpressionParameterValueProvider} to recursively trigger value conversion on the raw
 	 * resolved SpEL value.
 	 *
 	 * @author Oliver Gierke
@@ -2110,7 +2122,7 @@ enum NoOpParameterValueProvider implements ParameterValueProvider<MongoPersisten
 		INSTANCE;
 
 		@Override
-		public <T> T getParameterValue(Parameter<T, MongoPersistentProperty> parameter) {
+		public <T> @Nullable T getParameterValue(Parameter<T, MongoPersistentProperty> parameter) {
 			return null;
 		}
 	}
@@ -2138,7 +2150,7 @@ public List<org.springframework.data.util.TypeInformation<?>> getParameterTypes(
 		}
 
 		@Override
-		public org.springframework.data.util.TypeInformation<?> getProperty(String property) {
+		public org.springframework.data.util.@Nullable TypeInformation<?> getProperty(String property) {
 			return delegate.getProperty(property);
 		}
 
@@ -2173,7 +2185,7 @@ public TypeInformation<?> getRawTypeInformation() {
 		}
 
 		@Override
-		public org.springframework.data.util.TypeInformation<?> getActualType() {
+		public org.springframework.data.util.@Nullable TypeInformation<?> getActualType() {
 			return delegate.getActualType();
 		}
 
@@ -2188,7 +2200,7 @@ public List<org.springframework.data.util.TypeInformation<?>> getParameterTypes(
 		}
 
 		@Override
-		public org.springframework.data.util.TypeInformation<?> getSuperTypeInformation(Class superType) {
+		public org.springframework.data.util.@Nullable TypeInformation<?> getSuperTypeInformation(Class superType) {
 			return delegate.getSuperTypeInformation(superType);
 		}
 
@@ -2280,8 +2292,7 @@ default ConversionContext forProperty(MongoPersistentProperty property) {
 		 * @return
 		 * @param <S>
 		 */
-		@Nullable
-		default <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
+		default <S> @Nullable S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
 			return null;
 		}
 
@@ -2315,7 +2326,7 @@ public ConversionContext withPath(ObjectPath currentPath) {
 		}
 
 		@Override
-		public <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
+		public <S> @Nullable S findContextualEntity(MongoPersistentEntity<S> entity, Document document) {
 
 			Object identifier = document.get(BasicMongoPersistentProperty.ID_FIELD_NAME);
 
@@ -2352,15 +2363,15 @@ protected static class DefaultConversionContext implements ConversionContext {
 		final ObjectPath path;
 		final ContainerValueConverter<Bson> documentConverter;
 		final ContainerValueConverter<Collection<?>> collectionConverter;
-		final ContainerValueConverter<Bson> mapConverter;
-		final ContainerValueConverter<DBRef> dbRefConverter;
-		final ValueConverter<Object> elementConverter;
+		final ContainerValueConverter<@Nullable Bson> mapConverter;
+		final ContainerValueConverter<@Nullable DBRef> dbRefConverter;
+		final ValueConverter<@Nullable Object> elementConverter;
 
 		DefaultConversionContext(MongoConverter sourceConverter,
 				org.springframework.data.convert.CustomConversions customConversions, ObjectPath path,
-				ContainerValueConverter<Bson> documentConverter, ContainerValueConverter<Collection<?>> collectionConverter,
-				ContainerValueConverter<Bson> mapConverter, ContainerValueConverter<DBRef> dbRefConverter,
-				ValueConverter<Object> elementConverter) {
+				ContainerValueConverter<@Nullable Bson> documentConverter, ContainerValueConverter<@Nullable Collection<?>> collectionConverter,
+				ContainerValueConverter<@Nullable Bson> mapConverter, ContainerValueConverter<@Nullable DBRef> dbRefConverter,
+				ValueConverter<@Nullable Object> elementConverter) {
 
 			this.sourceConverter = sourceConverter;
 			this.conversions = customConversions;
@@ -2372,8 +2383,8 @@ protected static class DefaultConversionContext implements ConversionContext {
 			this.elementConverter = elementConverter;
 		}
 
-		@SuppressWarnings("unchecked")
 		@Override
+		@SuppressWarnings({"unchecked", "NullAway"})
 		public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint,
 				ConversionContext context) {
 
@@ -2454,7 +2465,7 @@ public ObjectPath getPath() {
 		 */
 		interface ValueConverter<T> {
 
-			Object convert(T source, TypeInformation<?> typeHint);
+			@Nullable Object convert(@Nullable T source, TypeInformation<?> typeHint);
 
 		}
 
@@ -2466,7 +2477,7 @@ interface ValueConverter<T> {
 		 */
 		interface ContainerValueConverter<T> {
 
-			Object convert(ConversionContext context, T source, TypeInformation<?> typeHint);
+			@Nullable Object convert(ConversionContext context, @Nullable T source, TypeInformation<?> typeHint);
 
 		}
 
@@ -2481,7 +2492,7 @@ class ProjectingConversionContext extends DefaultConversionContext {
 
 		ProjectingConversionContext(MongoConverter sourceConverter, CustomConversions customConversions, ObjectPath path,
 				ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> mapConverter,
-				ContainerValueConverter<DBRef> dbRefConverter, ValueConverter<Object> elementConverter,
+				ContainerValueConverter<@Nullable DBRef> dbRefConverter, ValueConverter<@Nullable Object> elementConverter,
 				EntityProjection<?, ?> projection) {
 			super(sourceConverter, customConversions, path,
 					(context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection),
@@ -2533,7 +2544,7 @@ public void setProperty(PersistentProperty<?> property, @Nullable Object value)
 		}
 
 		@Override
-		public Object getProperty(PersistentProperty<?> property) {
+		public @Nullable Object getProperty(PersistentProperty<?> property) {
 			return delegate.getProperty(translate(property));
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java
index 5fde0acddd..74be2949d0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java
@@ -16,13 +16,12 @@
 package org.springframework.data.mongodb.core.convert;
 
 import org.bson.conversions.Bson;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.convert.ValueConversionContext;
 import org.springframework.data.mapping.model.PropertyValueProvider;
 import org.springframework.data.mapping.model.SpELContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link ValueConversionContext} that allows to delegate read/write to an underlying {@link MongoConverter}.
@@ -63,25 +62,23 @@ public MongoPersistentProperty getProperty() {
 		return persistentProperty;
 	}
 
-	@Nullable
-	public Object getValue(String propertyPath) {
+	public @Nullable Object getValue(String propertyPath) {
 		return accessor.getPropertyValue(getProperty().getOwner().getRequiredPersistentProperty(propertyPath));
 	}
 
 	@Override
 	@SuppressWarnings("unchecked")
-	public <T> T write(@Nullable Object value, TypeInformation<T> target) {
+	public <T> @Nullable T write(@Nullable Object value, TypeInformation<T> target) {
 		return (T) mongoConverter.convertToMongoType(value, target);
 	}
 
 	@Override
-	public <T> T read(@Nullable Object value, TypeInformation<T> target) {
+	public <T> @Nullable T read(@Nullable Object value, TypeInformation<T> target) {
 		return value instanceof Bson bson ? mongoConverter.read(target.getType(), bson)
 				: ValueConversionContext.super.read(value, target);
 	}
 
-	@Nullable
-	public SpELContext getSpELContext() {
+	public @Nullable SpELContext getSpELContext() {
 		return spELContext;
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
index 3676e74c8b..e147d64cc5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
@@ -21,6 +21,7 @@
 import org.bson.codecs.configuration.CodecRegistry;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.ConversionException;
 import org.springframework.data.convert.CustomConversions;
 import org.springframework.data.convert.EntityConverter;
@@ -33,7 +34,6 @@
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.projection.ProjectionFactory;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -101,9 +101,8 @@ public interface MongoConverter
 	 * @throws IllegalArgumentException if {@literal targetType} is {@literal null}.
 	 * @since 2.1
 	 */
-	@SuppressWarnings("unchecked")
-	@Nullable
-	default <S, T> T mapValueToTargetType(S source, Class<T> targetType, DbRefResolver dbRefResolver) {
+	@SuppressWarnings({"unchecked","NullAway"})
+	default <S, T> @Nullable T mapValueToTargetType(S source, Class<T> targetType, DbRefResolver dbRefResolver) {
 
 		Assert.notNull(targetType, "TargetType must not be null");
 		Assert.notNull(dbRefResolver, "DbRefResolver must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
index f9a67d73a0..1fd45e1960 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java
@@ -47,6 +47,7 @@
 import org.bson.types.Code;
 import org.bson.types.Decimal128;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.core.convert.ConversionFailedException;
 import org.springframework.core.convert.TypeDescriptor;
@@ -142,7 +143,7 @@ public String convert(ObjectId id) {
 	enum StringToObjectIdConverter implements Converter<String, ObjectId> {
 		INSTANCE;
 
-		public ObjectId convert(String source) {
+		public @Nullable ObjectId convert(String source) {
 			return StringUtils.hasText(source) ? new ObjectId(source) : null;
 		}
 	}
@@ -206,7 +207,7 @@ public Decimal128 convert(BigInteger source) {
 	enum StringToBigDecimalConverter implements Converter<String, BigDecimal> {
 		INSTANCE;
 
-		public BigDecimal convert(String source) {
+		public @Nullable BigDecimal convert(String source) {
 			return StringUtils.hasText(source) ? new BigDecimal(source) : null;
 		}
 	}
@@ -235,7 +236,7 @@ public String convert(BigInteger source) {
 	enum StringToBigIntegerConverter implements Converter<String, BigInteger> {
 		INSTANCE;
 
-		public BigInteger convert(String source) {
+		public @Nullable BigInteger convert(String source) {
 			return StringUtils.hasText(source) ? new BigInteger(source) : null;
 		}
 	}
@@ -312,20 +313,25 @@ public String convert(Term source) {
 	 * @author Christoph Strobl
 	 * @since 1.7
 	 */
+	@SuppressWarnings("NullAway")
 	enum DocumentToNamedMongoScriptConverter implements Converter<Document, NamedMongoScript> {
 
 		INSTANCE;
 
 		@Override
-		public NamedMongoScript convert(Document source) {
+		public @Nullable NamedMongoScript convert(Document source) {
 
 			if (source.isEmpty()) {
 				return null;
 			}
 
 			String id = source.get(FieldName.ID.name()).toString();
+			Assert.notNull(id, "Script id must not be null");
+
 			Object rawValue = source.get("value");
 
+			Assert.isInstanceOf(Code.class, rawValue);
+
 			return new NamedMongoScript(id, ((Code) rawValue).getCode());
 		}
 	}
@@ -379,7 +385,7 @@ enum StringToCurrencyConverter implements Converter<String, Currency> {
 		INSTANCE;
 
 		@Override
-		public Currency convert(String source) {
+		public @Nullable Currency convert(String source) {
 			return StringUtils.hasText(source) ? Currency.getInstance(source) : null;
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java
index 050c3bd27d..8dccced380 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java
@@ -32,6 +32,7 @@
 import java.util.Set;
 import java.util.function.Consumer;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.TypeDescriptor;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.core.convert.converter.ConverterFactory;
@@ -51,7 +52,7 @@
 import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -137,7 +138,7 @@ public Set<ConvertiblePair> getConvertibleTypes() {
 			return new HashSet<>(Arrays.asList(localeToString, booleanToString));
 		}
 
-		public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+		public @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
 			return source != null ? source.toString() : null;
 		}
 	}
@@ -188,6 +189,7 @@ public static MongoConverterConfigurationAdapter from(List<?> converters) {
 		 * @param converter must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter registerConverter(Converter<?, ?> converter) {
 
 			Assert.notNull(converter, "Converter must not be null");
@@ -202,6 +204,7 @@ public MongoConverterConfigurationAdapter registerConverter(Converter<?, ?> conv
 		 * @param converters must not be {@literal null} nor contain {@literal null} values.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter registerConverters(Collection<?> converters) {
 
 			Assert.notNull(converters, "Converters must not be null");
@@ -217,6 +220,7 @@ public MongoConverterConfigurationAdapter registerConverters(Collection<?> conve
 		 * @param converterFactory must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter registerConverterFactory(ConverterFactory<?, ?> converterFactory) {
 
 			Assert.notNull(converterFactory, "ConverterFactory must not be null");
@@ -232,6 +236,7 @@ public MongoConverterConfigurationAdapter registerConverterFactory(ConverterFact
 		 * @return this.
 		 * @since 3.4
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter registerPropertyValueConverterFactory(
 				PropertyValueConverterFactory converterFactory) {
 
@@ -249,6 +254,7 @@ public MongoConverterConfigurationAdapter registerPropertyValueConverterFactory(
 		 * @return this.
 		 * @since 3.4
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter configurePropertyConversions(
 				Consumer<PropertyValueConverterRegistrar<MongoPersistentProperty>> configurationAdapter) {
 
@@ -271,6 +277,7 @@ public MongoConverterConfigurationAdapter configurePropertyConversions(
 		 * @param useNativeDriverJavaTimeCodecs
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs(boolean useNativeDriverJavaTimeCodecs) {
 
 			this.useNativeDriverJavaTimeCodecs = useNativeDriverJavaTimeCodecs;
@@ -285,6 +292,7 @@ public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs(boolean
 		 * @return this.
 		 * @see #useNativeDriverJavaTimeCodecs(boolean)
 		 */
+		@Contract("-> this")
 		public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs() {
 			return useNativeDriverJavaTimeCodecs(true);
 		}
@@ -299,6 +307,7 @@ public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs() {
 		 * @return this.
 		 * @see #useNativeDriverJavaTimeCodecs(boolean)
 		 */
+		@Contract("-> this")
 		public MongoConverterConfigurationAdapter useSpringDataJavaTimeCodecs() {
 			return useNativeDriverJavaTimeCodecs(false);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java
index 0316251dc1..67f9d5ec46 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java
@@ -28,6 +28,7 @@
 import java.util.regex.Pattern;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Example;
 import org.springframework.data.domain.ExampleMatcher.NullHandler;
 import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer;
@@ -98,13 +99,17 @@ public Document getMappedExample(Example<?> example) {
 	 * @param entity must not be {@literal null}.
 	 * @return
 	 */
-	public Document getMappedExample(Example<?> example, MongoPersistentEntity<?> entity) {
+	@SuppressWarnings("NullAway")
+	public Document getMappedExample(Example<?> example, @Nullable MongoPersistentEntity<?> entity) {
 
 		Assert.notNull(example, "Example must not be null");
-		Assert.notNull(entity, "MongoPersistentEntity must not be null");
 
 		Document reference = (Document) converter.convertToMongoType(example.getProbe());
 
+		if(entity != null) {
+			entity = mappingContext.getRequiredPersistentEntity(example.getProbeType());
+		}
+
 		if (entity.getIdProperty() != null && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) {
 
 			Object identifier = entity.getIdentifierAccessor(example.getProbe()).getIdentifier();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java
index 8d199083e7..a5d329045e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java
@@ -21,12 +21,12 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentEntity;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java
index 867a6213d2..8aeb576c67 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java
@@ -16,12 +16,12 @@
 package org.springframework.data.mongodb.core.convert;
 
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.convert.EntityWriter;
 import org.springframework.data.mongodb.core.mapping.DocumentPointer;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.DBRef;
 
@@ -43,8 +43,7 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> {
 	 * @param obj can be {@literal null}.
 	 * @return
 	 */
-	@Nullable
-	default Object convertToMongoType(@Nullable Object obj) {
+	default @Nullable Object convertToMongoType(@Nullable Object obj) {
 		return convertToMongoType(obj, (TypeInformation<?>) null);
 	}
 
@@ -59,7 +58,7 @@ default Object convertToMongoType(@Nullable Object obj) {
 	@Nullable
 	Object convertToMongoType(@Nullable Object obj, @Nullable TypeInformation<?> typeInformation);
 
-	default Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity<?> entity) {
+	default @Nullable Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity<?> entity) {
 		return convertToMongoType(obj, entity.getTypeInformation());
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java
index 265257af5c..68578f32b9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java
@@ -18,9 +18,8 @@
 import java.util.List;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.DBRef;
 
@@ -37,16 +36,14 @@ public enum NoOpDbRefResolver implements DbRefResolver {
 	INSTANCE;
 
 	@Override
-	@Nullable
-	public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback,
+	public @Nullable Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback,
 			DbRefProxyHandler proxyHandler) {
 
 		return handle();
 	}
 
 	@Override
-	@Nullable
-	public Document fetch(DBRef dbRef) {
+	public @Nullable Document fetch(DBRef dbRef) {
 		return handle();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java
index 5fefd472c4..d5f034eb1d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java
@@ -18,9 +18,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -99,8 +99,7 @@ ObjectPath push(Object object, MongoPersistentEntity<?> entity, @Nullable Object
 	 * @return {@literal null} when no match found.
 	 * @since 2.0
 	 */
-	@Nullable
-	<T> T getPathItem(Object id, String collection, Class<T> type) {
+	<T> @Nullable T getPathItem(Object id, String collection, Class<T> type) {
 
 		Assert.notNull(id, "Id must not be null");
 		Assert.hasText(collection, "Collection name must not be null");
@@ -133,13 +132,11 @@ Object getCurrentObject() {
 		return getObject();
 	}
 
-	@Nullable
-	private Object getObject() {
+	private @Nullable Object getObject() {
 		return object;
 	}
 
-	@Nullable
-	private Object getIdValue() {
+	private @Nullable Object getIdValue() {
 		return idValue;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
index cce809adc6..7aa9019ffd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
@@ -37,7 +37,8 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
-
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.annotation.Reference;
@@ -66,7 +67,7 @@
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.mongodb.util.DotPath;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -135,6 +136,7 @@ public Document getMappedObject(Bson query, Optional<? extends MongoPersistentEn
 	 * @param entity can be {@literal null}.
 	 * @return
 	 */
+	@SuppressWarnings("NullAway")
 	public Document getMappedObject(Bson query, @Nullable MongoPersistentEntity<?> entity) {
 
 		if (isNestedKeyword(query)) {
@@ -275,6 +277,7 @@ public Document addMetaAttributes(Document source, @Nullable MongoPersistentEnti
 		return mapMetaAttributes(source, entity, MetaMapping.FORCE);
 	}
 
+	@SuppressWarnings("NullAway")
 	private Document mapMetaAttributes(Document source, @Nullable MongoPersistentEntity<?> entity,
 			MetaMapping metaMapping) {
 
@@ -347,7 +350,7 @@ private Document getMappedTextScoreField(MongoPersistentProperty property) {
 	 * @param rawValue
 	 * @return
 	 */
-	protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) {
+	protected Entry<String, @Nullable Object> getMappedObjectForField(Field field, @Nullable Object rawValue) {
 
 		String key = field.getMappedKey();
 		Object value;
@@ -411,7 +414,9 @@ protected Document getMappedKeyword(Keyword keyword, @Nullable MongoPersistentEn
 		}
 
 		if (keyword.isSample()) {
-			return exampleMapper.getMappedExample(keyword.getValue(), entity);
+
+			Example<?> example = keyword.getValue();
+			return exampleMapper.getMappedExample(example, entity != null ? entity : mappingContext.getRequiredPersistentEntity(example.getProbeType()));
 		}
 
 		if (keyword.isJsonSchema()) {
@@ -452,9 +457,8 @@ protected Document getMappedKeyword(Field property, Keyword keyword) {
 	 * @param sourceValue the source object to be mapped
 	 * @return
 	 */
-	@Nullable
-	@SuppressWarnings("unchecked")
-	protected Object getMappedValue(Field documentField, Object sourceValue) {
+	@SuppressWarnings("NullAway")
+	protected @Nullable Object getMappedValue(Field documentField, @Nullable Object sourceValue) {
 
 		Object value = applyFieldTargetTypeHintToValue(documentField, sourceValue);
 
@@ -491,6 +495,7 @@ private boolean isIdField(Field documentField) {
 				&& documentField.getProperty().getOwner().isIdProperty(documentField.getProperty());
 	}
 
+	@SuppressWarnings("NullAway")
 	private Class<?> getIdTypeForField(Field documentField) {
 		return isIdField(documentField) ? documentField.getProperty().getFieldType() : ObjectId.class;
 	}
@@ -529,7 +534,7 @@ protected boolean isAssociationConversionNecessary(Field documentField, @Nullabl
 		}
 
 		MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
-		return entity.hasIdProperty()
+		return entity != null && entity.hasIdProperty()
 				&& (type.equals(DBRef.class) || entity.getRequiredIdProperty().getActualType().isAssignableFrom(type));
 	}
 
@@ -540,8 +545,7 @@ protected boolean isAssociationConversionNecessary(Field documentField, @Nullabl
 	 * @param entity
 	 * @return
 	 */
-	@Nullable
-	protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersistentEntity<?> entity) {
+	protected @Nullable Object convertSimpleOrDocument(Object source, @Nullable MongoPersistentEntity<?> entity) {
 
 		if (source instanceof Example<?> example) {
 			return exampleMapper.getMappedExample(example, entity);
@@ -601,8 +605,7 @@ protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersisten
 	 * @param entity
 	 * @return the converted mongo type or null if source is null
 	 */
-	@Nullable
-	protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
+	protected @Nullable Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
 
 		if (entity != null && entity.isUnwrapped()) {
 			return converter.convertToMongoType(source, entity);
@@ -611,8 +614,7 @@ protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersis
 		return converter.convertToMongoType(source, entity == null ? null : entity.getTypeInformation());
 	}
 
-	@Nullable
-	protected Object convertAssociation(Object source, Field field) {
+	protected @Nullable Object convertAssociation(Object source, Field field) {
 		Object value = convertAssociation(source, field.getProperty());
 		if (value != null && field.isIdField() && field.getFieldType() != value.getClass()) {
 			return convertId(value, field.getFieldType());
@@ -665,8 +667,7 @@ protected Object convertAssociation(@Nullable Object source, @Nullable MongoPers
 		return createReferenceFor(source, property);
 	}
 
-	@Nullable
-	private Object convertValue(Field documentField, Object sourceValue, Object value,
+	private @Nullable Object convertValue(Field documentField, @Nullable Object sourceValue, @Nullable Object value,
 			PropertyValueConverter<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter) {
 
 		MongoPersistentProperty property = documentField.getProperty();
@@ -688,7 +689,7 @@ private Object convertValue(Field documentField, Object sourceValue, Object valu
 			return converted;
 		}
 
-		if (property != null && !documentField.getProperty().isMap() && sourceValue instanceof Document document) {
+		if (property != null && !property.isMap() && sourceValue instanceof Document document) {
 
 			return BsonUtils.mapValues(document, (key, val) -> {
 				if (isKeyword(key)) {
@@ -698,11 +699,11 @@ private Object convertValue(Field documentField, Object sourceValue, Object valu
 			});
 		}
 
-		return valueConverter.write(value, conversionContext);
+		return value != null ? valueConverter.write(value, conversionContext) : value;
 	}
 
 	@Nullable
-	@SuppressWarnings("unchecked")
+	@SuppressWarnings({"unchecked", "NullAway"})
 	private Object convertIdField(Field documentField, Object source) {
 
 		Object value = source;
@@ -807,8 +808,7 @@ private Object createReferenceFor(Object source, MongoPersistentProperty propert
 	 * @return
 	 * @since 2.2
 	 */
-	@Nullable
-	public Object convertId(@Nullable Object id) {
+	public @Nullable Object convertId(@Nullable Object id) {
 		return convertId(id, ObjectId.class);
 	}
 
@@ -820,8 +820,7 @@ public Object convertId(@Nullable Object id) {
 	 * @return the converted {@literal id} or {@literal null} if the source was already {@literal null}.
 	 * @since 2.2
 	 */
-	@Nullable
-	public Object convertId(@Nullable Object id, Class<?> targetType) {
+	public @Nullable Object convertId(@Nullable Object id, Class<?> targetType) {
 
 		if (Quirks.skipConversion(id)) {
 			return id;
@@ -836,6 +835,7 @@ public Object convertId(@Nullable Object id, Class<?> targetType) {
 	 * @param candidate
 	 * @return
 	 */
+	@Contract("null -> false")
 	protected boolean isNestedKeyword(@Nullable Object candidate) {
 
 		if (!(candidate instanceof Document)) {
@@ -884,8 +884,7 @@ protected boolean isKeyword(String candidate) {
 	 * @param value the actual value. Can be {@literal null}.
 	 * @return the potentially converted target value.
 	 */
-	@Nullable
-	private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) {
+	private @Nullable Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) {
 
 		if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()
 				|| value instanceof Document || value instanceof DBObject || Quirks.skipConversion(value)) {
@@ -916,6 +915,7 @@ private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Ob
 	 * @author Oliver Gierke
 	 * @author Christoph Strobl
 	 */
+	@SuppressWarnings("NullAway")
 	static class Keyword {
 
 		private static final Set<String> NON_DBREF_CONVERTING_KEYWORDS = Set.of("$", "$size", "$slice", "$gt", "$lt");
@@ -1053,8 +1053,7 @@ public boolean isIdField() {
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public MongoPersistentProperty getProperty() {
+		public @Nullable MongoPersistentProperty getProperty() {
 			return null;
 		}
 
@@ -1063,8 +1062,7 @@ public MongoPersistentProperty getProperty() {
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public MongoPersistentEntity<?> getPropertyEntity() {
+		public @Nullable MongoPersistentEntity<?> getPropertyEntity() {
 			return null;
 		}
 
@@ -1100,8 +1098,7 @@ public boolean containsAssociation() {
 			return false;
 		}
 
-		@Nullable
-		public Association<MongoPersistentProperty> getAssociation() {
+		public @Nullable Association<MongoPersistentProperty> getAssociation() {
 			return null;
 		}
 
@@ -1164,6 +1161,7 @@ public MetadataBackedField(String name, MongoPersistentEntity<?> entity,
 		 * @param context must not be {@literal null}.
 		 * @param property may be {@literal null}.
 		 */
+		@SuppressWarnings("NullAway")
 		public MetadataBackedField(String name, MongoPersistentEntity<?> entity,
 				MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context,
 				@Nullable MongoPersistentProperty property) {
@@ -1207,14 +1205,13 @@ public MongoPersistentProperty getProperty() {
 		}
 
 		@Override
-		public MongoPersistentEntity<?> getPropertyEntity() {
+		public @Nullable MongoPersistentEntity<?> getPropertyEntity() {
 			MongoPersistentProperty property = getProperty();
 			return property == null ? null : mappingContext.getPersistentEntity(property);
 		}
 
-		@Nullable
 		@Override
-		public MongoPersistentEntity<?> getEntity() {
+		public @Nullable MongoPersistentEntity<?> getEntity() {
 			return entity;
 		}
 
@@ -1224,7 +1221,7 @@ public boolean isAssociation() {
 		}
 
 		@Override
-		public Association<MongoPersistentProperty> getAssociation() {
+		public @Nullable Association<MongoPersistentProperty> getAssociation() {
 			return association;
 		}
 
@@ -1265,8 +1262,7 @@ public String getMappedKey() {
 			return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter());
 		}
 
-		@Nullable
-		protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
+		protected @Nullable PersistentPropertyPath<MongoPersistentProperty> getPath() {
 			return path;
 		}
 
@@ -1276,8 +1272,7 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
 		 * @param pathExpression
 		 * @return
 		 */
-		@Nullable
-		private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
+		private @Nullable PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
 				@Nullable MongoPersistentProperty sourceProperty) {
 
 			if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
@@ -1327,8 +1322,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
 			return propertyPath;
 		}
 
-		@Nullable
-		private PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPropertyPath(PropertyPath path) {
+		private @Nullable PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPropertyPath(PropertyPath path) {
 
 			try {
 				return mappingContext.getPersistentPropertyPath(path);
@@ -1427,6 +1421,7 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
 		 * @return
 		 * @since 1.7
 		 */
+		@SuppressWarnings("NullAway")
 		protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
 			return new AssociationConverter(name, getAssociation());
 		}
@@ -1578,7 +1573,7 @@ public AssociationConverter(String name, Association<MongoPersistentProperty> as
 		}
 
 		@Override
-		public String convert(MongoPersistentProperty source) {
+		public @Nullable String convert(MongoPersistentProperty source) {
 
 			if (associationFound) {
 				return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java
index 5a1adf9114..cd7d55311d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java
@@ -20,9 +20,9 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.ReferenceResolver.ReferenceCollection;
 import org.springframework.data.mongodb.core.mapping.FieldName;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.client.MongoCollection;
 
@@ -42,8 +42,7 @@ public interface ReferenceLoader {
 	 * @param context must not be {@literal null}.
 	 * @return the matching {@link Document} or {@literal null} if none found.
 	 */
-	@Nullable
-	default Document fetchOne(DocumentReferenceQuery referenceQuery, ReferenceCollection context) {
+	default @Nullable Document fetchOne(DocumentReferenceQuery referenceQuery, ReferenceCollection context) {
 
 		Iterator<Document> it = fetchMany(referenceQuery, context).iterator();
 		return it.hasNext() ? it.next() : null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index b912cfb540..a0e5a6f2bb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -31,6 +31,7 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mapping.model.SpELContext;
 import org.springframework.data.mongodb.core.convert.ReferenceLoader.DocumentReferenceQuery;
@@ -47,7 +48,6 @@
 import org.springframework.data.mongodb.util.spel.ExpressionUtils;
 import org.springframework.data.util.Streamable;
 import org.springframework.expression.EvaluationContext;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -98,8 +98,7 @@ public ReferenceLookupDelegate(
 	 *          {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
+	public @Nullable Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
 			MongoEntityReader entityReader) {
 
 		Object value = source instanceof DocumentReferenceSource documentReferenceSource
@@ -126,7 +125,7 @@ public Object readReference(MongoPersistentProperty property, Object source, Loo
 
 	@Nullable
 	private Iterable<Document> retrieveRawDocuments(MongoPersistentProperty property, Object source,
-			LookupFunction lookupFunction, Object value) {
+			LookupFunction lookupFunction, @Nullable Object value) {
 
 		DocumentReferenceQuery filter = computeFilter(property, source, spELContext);
 		if (filter instanceof NoResultsFilter) {
@@ -137,7 +136,8 @@ private Iterable<Document> retrieveRawDocuments(MongoPersistentProperty property
 		return lookupFunction.apply(filter, referenceCollection);
 	}
 
-	private ReferenceCollection computeReferenceContext(MongoPersistentProperty property, Object value,
+	@SuppressWarnings("NullAway")
+	private ReferenceCollection computeReferenceContext(MongoPersistentProperty property, @Nullable Object value,
 			SpELContext spELContext) {
 
 		// Use the first value as a reference for others in case of collection like
@@ -195,7 +195,7 @@ private ReferenceCollection computeReferenceContext(MongoPersistentProperty prop
 	 * @return can be {@literal null}.
 	 */
 	@SuppressWarnings("unchecked")
-	private <T> T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier<T> defaultValue) {
+	private <T> T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier<@Nullable T> defaultValue) {
 
 		if (!StringUtils.hasText(value)) {
 			return defaultValue.get();
@@ -220,7 +220,7 @@ private <T> T parseValueOrGet(String value, ParameterBindingContext bindingConte
 		return evaluated != null ? evaluated : defaultValue.get();
 	}
 
-	ParameterBindingContext bindingContext(MongoPersistentProperty property, Object source, SpELContext spELContext) {
+	ParameterBindingContext bindingContext(MongoPersistentProperty property, @Nullable Object source, SpELContext spELContext) {
 
 		ValueProvider valueProvider = valueProviderFor(DocumentReferenceSource.getTargetSource(source));
 
@@ -228,7 +228,7 @@ ParameterBindingContext bindingContext(MongoPersistentProperty property, Object
 				() -> evaluationContextFor(property, source, spELContext));
 	}
 
-	ValueProvider valueProviderFor(Object source) {
+	ValueProvider valueProviderFor(@Nullable Object source) {
 
 		return index -> {
 			if (source instanceof Document document) {
@@ -238,7 +238,7 @@ ValueProvider valueProviderFor(Object source) {
 		};
 	}
 
-	EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object source, SpELContext spELContext) {
+	EvaluationContext evaluationContextFor(MongoPersistentProperty property, @Nullable Object source, SpELContext spELContext) {
 
 		Object target = source instanceof DocumentReferenceSource documentReferenceSource
 				? documentReferenceSource.getTargetSource()
@@ -264,7 +264,7 @@ EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object
 	 * @param spELContext must not be {@literal null}.
 	 * @return never {@literal null}.
 	 */
-	@SuppressWarnings("unchecked")
+	@SuppressWarnings({"unchecked","NullAway"})
 	DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object source, SpELContext spELContext) {
 
 		DocumentReference documentReference = property.isDocumentReference() ? property.getDocumentReference()
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java
index 715327d18e..0698b08bf8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.DBRef;
@@ -54,8 +54,7 @@ Object resolveReference(MongoPersistentProperty property, Object source,
 	 */
 	class ReferenceCollection {
 
-		@Nullable //
-		private final String database;
+		private final @Nullable String database;
 		private final String collection;
 
 		/**
@@ -95,8 +94,7 @@ public String getCollection() {
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public String getDatabase() {
+		public @Nullable String getDatabase() {
 			return database;
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java
index 35cb578c23..ad3e4986fb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java
@@ -23,6 +23,7 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Order;
@@ -34,7 +35,6 @@
 import org.springframework.data.mongodb.core.query.Update.Modifier;
 import org.springframework.data.mongodb.core.query.Update.Modifiers;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -129,7 +129,7 @@ public static boolean isUpdateObject(@Nullable Document updateObj) {
 	 *      org.springframework.data.mongodb.core.mapping.MongoPersistentEntity)
 	 */
 	@Override
-	protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
+	protected @Nullable Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {
 
 		if (entity != null && entity.isUnwrapped()) {
 			return converter.convertToMongoType(source, entity);
@@ -140,7 +140,8 @@ protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersis
 	}
 
 	@Override
-	protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) {
+	@SuppressWarnings("NullAway")
+	protected Entry<String, @Nullable Object> getMappedObjectForField(Field field, @Nullable Object rawValue) {
 
 		if (isDocument(rawValue)) {
 
@@ -196,11 +197,12 @@ private boolean isQuery(@Nullable Object value) {
 		return value instanceof Query;
 	}
 
-	private Document getMappedValue(@Nullable Field field, Modifier modifier) {
+	private @Nullable Document getMappedValue(@Nullable Field field, Modifier modifier) {
 		return new Document(modifier.getKey(), getMappedModifier(field, modifier));
 	}
 
-	private Object getMappedModifier(@Nullable Field field, Modifier modifier) {
+	@SuppressWarnings("NullAway")
+	private @Nullable Object getMappedModifier(@Nullable Field field, Modifier modifier) {
 
 		Object value = modifier.getValue();
 
@@ -211,7 +213,7 @@ private Object getMappedModifier(@Nullable Field field, Modifier modifier) {
 					: getMappedSort(sortObject, field.getPropertyEntity());
 		}
 
-		if (isAssociationConversionNecessary(field, value)) {
+		if (field != null && isAssociationConversionNecessary(field, value)) {
 			if (ObjectUtils.isArray(value) || value instanceof Collection) {
 				List<Object> targetPointers = new ArrayList<>();
 				for (Object val : converter.getConversionService().convert(value, List.class)) {
@@ -229,7 +231,7 @@ private Object getMappedModifier(@Nullable Field field, Modifier modifier) {
 	private TypeInformation<?> getTypeHintForEntity(@Nullable Object source, MongoPersistentEntity<?> entity) {
 
 		TypeInformation<?> info = entity.getTypeInformation();
-		Class<?> type = info.getActualType().getType();
+		Class<?> type = info.getRequiredActualType().getType();
 
 		if (source == null || type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) {
 			return info;
@@ -247,7 +249,7 @@ private TypeInformation<?> getTypeHintForEntity(@Nullable Object source, MongoPe
 	}
 
 	@Override
-	protected Field createPropertyField(MongoPersistentEntity<?> entity, String key,
+	protected Field createPropertyField(@Nullable MongoPersistentEntity<?> entity, String key,
 			MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
 
 		return entity == null ? super.createPropertyField(entity, key, mappingContext)
@@ -306,6 +308,7 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
 			return new UpdateAssociationConverter(getMappingContext(), getAssociation(), key);
 		}
@@ -333,7 +336,7 @@ public UpdateAssociationConverter(
 			}
 
 			@Override
-			public String convert(MongoPersistentProperty source) {
+			public @Nullable String convert(MongoPersistentProperty source) {
 				return super.convert(source) == null ? null : mapper.mapPropertyName(source);
 			}
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java
index 0a96cc867a..8eed053a09 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java
@@ -17,10 +17,9 @@
 
 import org.bson.Document;
 import org.bson.conversions.Bson;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 
 /**
  * Internal API to trigger the resolution of properties.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java
index 4097be7704..b31d8a2b7c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.core.convert.encryption;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.MongoConversionContext;
 import org.springframework.data.mongodb.core.convert.MongoValueConverter;
 import org.springframework.data.mongodb.core.encryption.EncryptionContext;
@@ -28,7 +29,7 @@
 public interface EncryptingConverter<S, T> extends MongoValueConverter<S, T> {
 
 	@Override
-	default S read(Object value, MongoConversionContext context) {
+	default @Nullable S read(Object value, MongoConversionContext context) {
 		return decrypt(value, buildEncryptionContext(context));
 	}
 
@@ -39,10 +40,10 @@ default S read(Object value, MongoConversionContext context) {
 	 * @param context the context to operate in.
 	 * @return never {@literal null}.
 	 */
-	S decrypt(Object encryptedValue, EncryptionContext context);
+	@Nullable S decrypt(Object encryptedValue, EncryptionContext context);
 
 	@Override
-	default T write(Object value, MongoConversionContext context) {
+	default @Nullable T write(Object value, MongoConversionContext context) {
 		return encrypt(value, buildEncryptionContext(context));
 	}
 
@@ -53,7 +54,7 @@ default T write(Object value, MongoConversionContext context) {
 	 * @param context the context to operate in.
 	 * @return never {@literal null}.
 	 */
-	T encrypt(Object value, EncryptionContext context);
+	T encrypt(@Nullable Object value, EncryptionContext context);
 
 	/**
 	 * Obtain the {@link EncryptionContext} for a given {@link MongoConversionContext value conversion context}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java
index f8d814fee4..07ddf93d48 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java
@@ -15,12 +15,12 @@
  */
 package org.springframework.data.mongodb.core.convert.encryption;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.MongoConversionContext;
 import org.springframework.data.mongodb.core.encryption.EncryptionContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
 import org.springframework.expression.EvaluationContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Default {@link EncryptionContext} implementation.
@@ -41,29 +41,33 @@ public MongoPersistentProperty getProperty() {
 		return conversionContext.getProperty();
 	}
 
-	@Nullable
 	@Override
-	public Object lookupValue(String path) {
+	public @Nullable Object lookupValue(String path) {
 		return conversionContext.getValue(path);
 	}
 
 	@Override
-	public Object convertToMongoType(Object value) {
+	public @Nullable Object convertToMongoType(Object value) {
 		return conversionContext.write(value);
 	}
 
 	@Override
-	public EvaluationContext getEvaluationContext(Object source) {
-		return conversionContext.getSpELContext().getEvaluationContext(source);
+	public EvaluationContext getEvaluationContext(@Nullable Object source) {
+
+		if(conversionContext.getSpELContext() != null) {
+			return conversionContext.getSpELContext().getEvaluationContext(source);
+		}
+
+		throw new IllegalStateException("SpEL context not present");
 	}
 
 	@Override
-	public <T> T read(@Nullable Object value, TypeInformation<T> target) {
+	public <T> @Nullable T read(@Nullable Object value, TypeInformation<T> target) {
 		return conversionContext.read(value, target);
 	}
 
 	@Override
-	public <T> T write(@Nullable Object value, TypeInformation<T> target) {
+	public <T> @Nullable T write(@Nullable Object value, TypeInformation<T> target) {
 		return conversionContext.write(value, target);
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java
index 1ce24b25fe..9e4991dfb1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java
@@ -27,6 +27,7 @@
 import org.bson.BsonValue;
 import org.bson.Document;
 import org.bson.types.Binary;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.CollectionFactory;
 import org.springframework.data.mongodb.core.convert.MongoConversionContext;
 import org.springframework.data.mongodb.core.encryption.Encryption;
@@ -36,7 +37,6 @@
 import org.springframework.data.mongodb.core.mapping.Encrypted;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -59,16 +59,15 @@ public MongoEncryptionConverter(Encryption<BsonValue, BsonBinary> encryption, En
 		this.keyResolver = keyResolver;
 	}
 
-	@Nullable
 	@Override
-	public Object read(Object value, MongoConversionContext context) {
+	public @Nullable Object read(Object value, MongoConversionContext context) {
 
 		Object decrypted = EncryptingConverter.super.read(value, context);
 		return decrypted instanceof BsonValue bsonValue ? BsonUtils.toJavaType(bsonValue) : decrypted;
 	}
 
 	@Override
-	public Object decrypt(Object encryptedValue, EncryptionContext context) {
+	public @Nullable Object decrypt(Object encryptedValue, EncryptionContext context) {
 
 		Object decryptedValue = encryptedValue;
 		if (encryptedValue instanceof Binary || encryptedValue instanceof BsonBinary) {
@@ -142,7 +141,8 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) {
 	}
 
 	@Override
-	public Object encrypt(Object value, EncryptionContext context) {
+	@SuppressWarnings("NullAway")
+	public Object encrypt(@Nullable Object value, EncryptionContext context) {
 
 		if (LOGGER.isDebugEnabled()) {
 			LOGGER.debug(String.format("Encrypting %s.%s.", getProperty(context).getOwner().getName(),
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java
index 4a6f78357a..a0e8ea27f7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java
@@ -3,5 +3,5 @@
  * <a href="https://www.mongodb.com/docs/manual/core/csfle/fundamentals/manual-encryption/">explicit encryption
  * mechanism of Client-Side Field Level Encryption</a>.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.convert.encryption;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java
index cfa07fa8f9..dbef5cbb90 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Spring Data MongoDB specific converter infrastructure.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.convert;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java
index 89beaadedb..a2a2083255 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.mongodb.core.encryption;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
 import org.springframework.expression.EvaluationContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Context to encapsulate encryption for a specific {@link MongoPersistentProperty}.
@@ -43,7 +43,7 @@ public interface EncryptionContext {
 	 * @param value
 	 * @return
 	 */
-	Object convertToMongoType(Object value);
+	@Nullable Object convertToMongoType(Object value);
 
 	/**
 	 * Reads the value as an instance of the {@link PersistentProperty#getTypeInformation() property type}.
@@ -52,7 +52,7 @@ public interface EncryptionContext {
 	 * @return can be {@literal null}.
 	 * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}.
 	 */
-	default <T> T read(@Nullable Object value) {
+	default <T> @Nullable T read(@Nullable Object value) {
 		return (T) read(value, getProperty().getTypeInformation());
 	}
 
@@ -64,7 +64,7 @@ default <T> T read(@Nullable Object value) {
 	 * @return can be {@literal null}.
 	 * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}.
 	 */
-	default <T> T read(@Nullable Object value, Class<T> target) {
+	default <T> @Nullable T read(@Nullable Object value, Class<T> target) {
 		return read(value, TypeInformation.of(target));
 	}
 
@@ -76,7 +76,7 @@ default <T> T read(@Nullable Object value, Class<T> target) {
 	 * @return can be {@literal null}.
 	 * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}.
 	 */
-	<T> T read(@Nullable Object value, TypeInformation<T> target);
+	<T> @Nullable T read(@Nullable Object value, TypeInformation<T> target);
 
 	/**
 	 * Write the value as an instance of the {@link PersistentProperty#getTypeInformation() property type}.
@@ -88,8 +88,7 @@ default <T> T read(@Nullable Object value, Class<T> target) {
 	 * @see PersistentProperty#getTypeInformation()
 	 * @see #write(Object, TypeInformation)
 	 */
-	@Nullable
-	default <T> T write(@Nullable Object value) {
+	default <T> @Nullable T write(@Nullable Object value) {
 		return (T) write(value, getProperty().getTypeInformation());
 	}
 
@@ -101,8 +100,7 @@ default <T> T write(@Nullable Object value) {
 	 * @return can be {@literal null}.
 	 * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}.
 	 */
-	@Nullable
-	default <T> T write(@Nullable Object value, Class<T> target) {
+	default <T> @Nullable T write(@Nullable Object value, Class<T> target) {
 		return write(value, TypeInformation.of(target));
 	}
 
@@ -114,8 +112,7 @@ default <T> T write(@Nullable Object value, Class<T> target) {
 	 * @return can be {@literal null}.
 	 * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}.
 	 */
-	@Nullable
-	<T> T write(@Nullable Object value, TypeInformation<T> target);
+	<T> @Nullable T write(@Nullable Object value, TypeInformation<T> target);
 
 	/**
 	 * Lookup the value for a given path within the current context.
@@ -126,6 +123,6 @@ default <T> T write(@Nullable Object value, Class<T> target) {
 	@Nullable
 	Object lookupValue(String path);
 
-	EvaluationContext getEvaluationContext(Object source);
+	EvaluationContext getEvaluationContext(@Nullable Object source);
 
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java
index f3906d89dd..90a3ab8720 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java
@@ -2,5 +2,5 @@
  * Infrastructure for <a href="https://www.mongodb.com/docs/manual/core/csfle/fundamentals/manual-encryption/">explicit
  * encryption mechanism of Client-Side Field Level Encryption</a>.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.encryption;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java
index 2372700aec..74f36e3198 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java
@@ -19,7 +19,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java
index bc74a56df3..cfae6761a8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java
@@ -19,9 +19,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
-import org.springframework.lang.Nullable;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.Version;
@@ -139,9 +138,8 @@ private static void registerDeserializersIn(SimpleModule module) {
 	 */
 	private static abstract class GeoJsonDeserializer<T extends GeoJson<?>> extends JsonDeserializer<T> {
 
-		@Nullable
 		@Override
-		public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException {
+		public @Nullable T deserialize(JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException {
 
 			JsonNode node = jp.readValueAsTree();
 			JsonNode coordinates = node.get("coordinates");
@@ -158,8 +156,7 @@ public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext c
 		 * @param coordinates
 		 * @return
 		 */
-		@Nullable
-		protected abstract T doDeserialize(ArrayNode coordinates);
+		protected abstract @Nullable T doDeserialize(ArrayNode coordinates);
 
 		/**
 		 * Get the {@link GeoJsonPoint} representation of given {@link ArrayNode} assuming {@code node.[0]} represents
@@ -168,8 +165,7 @@ public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext c
 		 * @param node can be {@literal null}.
 		 * @return {@literal null} when given a {@code null} value.
 		 */
-		@Nullable
-		protected GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) {
+		protected @Nullable GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) {
 
 			if (node == null) {
 				return null;
@@ -185,8 +181,7 @@ protected GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) {
 		 * @param node can be {@literal null}.
 		 * @return {@literal null} when given a {@code null} value.
 		 */
-		@Nullable
-		protected Point toPoint(@Nullable ArrayNode node) {
+		protected @Nullable Point toPoint(@Nullable ArrayNode node) {
 
 			if (node == null) {
 				return null;
@@ -236,9 +231,8 @@ protected GeoJsonLineString toLineString(ArrayNode node) {
 	 */
 	private static class GeoJsonPointDeserializer extends GeoJsonDeserializer<GeoJsonPoint> {
 
-		@Nullable
 		@Override
-		protected GeoJsonPoint doDeserialize(ArrayNode coordinates) {
+		protected @Nullable GeoJsonPoint doDeserialize(ArrayNode coordinates) {
 			return toGeoJsonPoint(coordinates);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java
index 8dafe9ea00..833a1dd9f6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java
@@ -19,8 +19,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
index bcb4c3e79e..e30ed5d6ed 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
@@ -20,8 +20,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java
index 12b9de9da4..a7e6306b49 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java
@@ -19,7 +19,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java
index 166a10df08..990be290cd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java
@@ -21,9 +21,10 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
 import org.springframework.data.geo.Polygon;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -78,6 +79,7 @@ public GeoJsonPolygon(List<Point> points) {
 	 * @return new {@link GeoJsonPolygon}.
 	 * @since 1.10
 	 */
+	@Contract("_, _, _, _, _ -> new")
 	public GeoJsonPolygon withInnerRing(Point first, Point second, Point third, Point fourth, Point... others) {
 		return withInnerRing(asList(first, second, third, fourth, others));
 	}
@@ -88,6 +90,7 @@ public GeoJsonPolygon withInnerRing(Point first, Point second, Point third, Poin
 	 * @param points must not be {@literal null}.
 	 * @return new {@link GeoJsonPolygon}.
 	 */
+	@Contract("_ -> new")
 	public GeoJsonPolygon withInnerRing(List<Point> points) {
 		return withInnerRing(new GeoJsonLineString(points));
 	}
@@ -99,6 +102,7 @@ public GeoJsonPolygon withInnerRing(List<Point> points) {
 	 * @return new {@link GeoJsonPolygon}.
 	 * @since 1.10
 	 */
+	@Contract("_ -> new")
 	public GeoJsonPolygon withInnerRing(GeoJsonLineString lineString) {
 
 		Assert.notNull(lineString, "LineString must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java
index a482c136e7..47be645869 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java
@@ -18,12 +18,12 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.geo.Circle;
 import org.springframework.data.geo.Distance;
 import org.springframework.data.geo.Point;
 import org.springframework.data.geo.Shape;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java
index 6cc77f832b..e5adfb26f4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Support for MongoDB geo-spatial queries.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.geo;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java
index 0949506195..7188da6972 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java
@@ -18,9 +18,10 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.util.MongoClientVersion;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -41,7 +42,7 @@ public class GeospatialIndex implements IndexDefinition {
 	private @Nullable Integer max;
 	private @Nullable Integer bits;
 	private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
-	private Double bucketSize = MongoClientVersion.isVersion5orNewer() ? null : 1.0;
+	private @Nullable Double bucketSize = MongoClientVersion.isVersion5orNewer() ? null : 1.0;
 	private @Nullable String additionalField;
 	private Optional<IndexFilter> filter = Optional.empty();
 	private Optional<Collation> collation = Optional.empty();
@@ -62,6 +63,7 @@ public GeospatialIndex(String field) {
 	 * @param name must not be {@literal null} or empty.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex named(String name) {
 
 		this.name = name;
@@ -72,6 +74,7 @@ public GeospatialIndex named(String name) {
 	 * @param min
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex withMin(int min) {
 		this.min = min;
 		return this;
@@ -81,6 +84,7 @@ public GeospatialIndex withMin(int min) {
 	 * @param max
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex withMax(int max) {
 		this.max = max;
 		return this;
@@ -90,6 +94,7 @@ public GeospatialIndex withMax(int max) {
 	 * @param bits
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex withBits(int bits) {
 		this.bits = bits;
 		return this;
@@ -99,6 +104,7 @@ public GeospatialIndex withBits(int bits) {
 	 * @param type must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex typed(GeoSpatialIndexType type) {
 
 		Assert.notNull(type, "Type must not be null");
@@ -113,6 +119,7 @@ public GeospatialIndex typed(GeoSpatialIndexType type) {
 	 * @deprecated since MongoDB server version 4.4
 	 */
 	@Deprecated
+	@Contract("_ -> this")
 	public GeospatialIndex withBucketSize(double bucketSize) {
 		this.bucketSize = bucketSize;
 		return this;
@@ -122,6 +129,7 @@ public GeospatialIndex withBucketSize(double bucketSize) {
 	 * @param fieldName
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex withAdditionalField(String fieldName) {
 		this.additionalField = fieldName;
 		return this;
@@ -136,6 +144,7 @@ public GeospatialIndex withAdditionalField(String fieldName) {
 	 *      "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
 	 * @since 1.10
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex partial(@Nullable IndexFilter filter) {
 
 		this.filter = Optional.ofNullable(filter);
@@ -152,6 +161,7 @@ public GeospatialIndex partial(@Nullable IndexFilter filter) {
 	 * @return this.
 	 * @since 2.0
 	 */
+	@Contract("_ -> this")
 	public GeospatialIndex collation(@Nullable Collation collation) {
 
 		this.collation = Optional.ofNullable(collation);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java
index 95f4226e28..91195a40f4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java
@@ -23,10 +23,11 @@
 import java.util.concurrent.TimeUnit;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.mongodb.core.index.IndexOptions.Unique;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -52,11 +53,13 @@ public Index(String key, Direction direction) {
 		fieldSpec.put(key, direction);
 	}
 
+	@Contract("_, _ -> this")
 	public Index on(String key, Direction direction) {
 		fieldSpec.put(key, direction);
 		return this;
 	}
 
+	@Contract("_ -> this")
 	public Index named(String name) {
 		this.name = name;
 		return this;
@@ -69,6 +72,7 @@ public Index named(String name) {
 	 * @see <a href=
 	 *      "https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a>
 	 */
+	@Contract("-> this")
 	public Index unique() {
 
 		this.options.setUnique(Unique.YES);
@@ -82,6 +86,7 @@ public Index unique() {
 	 * @see <a href=
 	 *      "https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a>
 	 */
+	@Contract("-> this")
 	public Index sparse() {
 		this.sparse = true;
 		return this;
@@ -92,7 +97,7 @@ public Index sparse() {
 	 *
 	 * @return this.
 	 * @since 1.5
-	 */
+	 */@Contract("-> this")
 	public Index background() {
 
 		this.background = true;
@@ -107,6 +112,7 @@ public Index background() {
 	 *      "https://www.mongodb.com/docs/manual/core/index-hidden/">https://www.mongodb.com/docs/manual/core/index-hidden/</a>
 	 * @since 4.1
 	 */
+	@Contract("-> this")
 	public Index hidden() {
 
 		options.setHidden(true);
@@ -120,6 +126,7 @@ public Index hidden() {
 	 * @return this.
 	 * @since 1.5
 	 */
+	@Contract("_ -> this")
 	public Index expire(long value) {
 		return expire(value, TimeUnit.SECONDS);
 	}
@@ -132,6 +139,7 @@ public Index expire(long value) {
 	 * @throws IllegalArgumentException if given {@literal timeout} is {@literal null}.
 	 * @since 2.2
 	 */
+	@Contract("_ -> this")
 	public Index expire(Duration timeout) {
 
 		Assert.notNull(timeout, "Timeout must not be null");
@@ -146,6 +154,7 @@ public Index expire(Duration timeout) {
 	 * @return this.
 	 * @since 1.5
 	 */
+	@Contract("_, _ -> this")
 	public Index expire(long value, TimeUnit unit) {
 
 		Assert.notNull(unit, "TimeUnit for expiration must not be null");
@@ -162,6 +171,7 @@ public Index expire(long value, TimeUnit unit) {
 	 *      "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
 	 * @since 1.10
 	 */
+	@Contract("_ -> this")
 	public Index partial(@Nullable IndexFilter filter) {
 
 		this.filter = Optional.ofNullable(filter);
@@ -178,6 +188,7 @@ public Index partial(@Nullable IndexFilter filter) {
 	 * @return this.
 	 * @since 2.0
 	 */
+	@Contract("_ -> this")
 	public Index collation(@Nullable Collation collation) {
 
 		this.collation = Optional.ofNullable(collation);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java
index a5cbf6c896..2e7268699c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.core.index;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort.Direction;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -139,8 +139,7 @@ public String getKey() {
 	 *
 	 * @return the direction
 	 */
-	@Nullable
-	public Direction getDirection() {
+	public @Nullable Direction getDirection() {
 		return direction;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java
index de7153bfb5..e9817746c3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java
@@ -27,11 +27,12 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
 
 /**
  * Index information for a MongoDB index.
@@ -89,7 +90,7 @@ public IndexInfo(List<IndexField> indexFields, String name, boolean unique, bool
 	 */
 	public static IndexInfo indexInfoOf(Document sourceDocument) {
 
-		Document keyDbObject = (Document) sourceDocument.get("key");
+		Document keyDbObject = sourceDocument.get("key", new Document());
 		int numberOfElements = keyDbObject.keySet().size();
 
 		List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
@@ -105,9 +106,10 @@ public static IndexInfo indexInfoOf(Document sourceDocument) {
 			} else if ("text".equals(value)) {
 
 				Document weights = (Document) sourceDocument.get("weights");
-
-				for (String fieldName : weights.keySet()) {
-					indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
+				if(weights != null) {
+					for (String fieldName : weights.keySet()) {
+						indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
+					}
 				}
 
 			} else {
@@ -129,7 +131,7 @@ public static IndexInfo indexInfoOf(Document sourceDocument) {
 			}
 		}
 
-		String name = sourceDocument.get("name").toString();
+		String name = ObjectUtils.nullSafeToString(sourceDocument.get("name"));
 
 		boolean unique = sourceDocument.get("unique", false);
 		boolean sparse = sourceDocument.get("sparse", false);
@@ -161,8 +163,7 @@ public static IndexInfo indexInfoOf(Document sourceDocument) {
 	 * @return the {@link String} representation of the partial filter {@link Document}.
 	 * @since 2.1.11
 	 */
-	@Nullable
-	private static String extractPartialFilterString(Document sourceDocument) {
+	private static @Nullable String extractPartialFilterString(Document sourceDocument) {
 
 		if (!sourceDocument.containsKey("partialFilterExpression")) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java
index ca3d951c94..aec1ba817d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.index;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Provider interface to obtain {@link IndexOperations} by MongoDB collection name or entity type.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java
index 887542cb0c..a390d1eb3e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java
@@ -18,7 +18,7 @@
 import java.time.Duration;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Changeable properties of an index. Can be used for index creation and modification.
@@ -28,14 +28,11 @@
  */
 public class IndexOptions {
 
-	@Nullable
-	private Duration expire;
+	private @Nullable Duration expire;
 
-	@Nullable
-	private Boolean hidden;
+	private @Nullable Boolean hidden;
 
-	@Nullable
-	private Unique unique;
+	private @Nullable Unique unique;
 
 	public enum Unique {
 
@@ -108,8 +105,7 @@ public void setExpire(Duration expire) {
 	/**
 	 * @return {@literal true} if hidden, {@literal null} if not set.
 	 */
-	@Nullable
-	public Boolean isHidden() {
+	public @Nullable Boolean isHidden() {
 		return hidden;
 	}
 
@@ -123,8 +119,7 @@ public void setHidden(boolean hidden) {
 	/**
 	 * @return the unique property value, {@literal null} if not set.
 	 */
-	@Nullable
-	public Unique getUnique() {
+	public @Nullable Unique getUnique() {
 		return unique;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java
index 362247725f..3bb3fdbd0f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.index;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * @author Jon Brisbin <jbrisbin@vmware.com>
@@ -26,8 +26,7 @@ public abstract class IndexPredicate {
 	private IndexDirection direction = IndexDirection.ASCENDING;
 	private boolean unique = false;
 
-	@Nullable
-	public String getName() {
+	public @Nullable String getName() {
 		return name;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
index e20b0704cc..f1550d1501 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java
@@ -21,7 +21,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.ApplicationListener;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.data.mapping.PersistentEntity;
@@ -33,7 +33,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.util.MongoDbErrorCodes;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -183,8 +182,7 @@ public boolean isIndexCreatorFor(MappingContext<?, ?> context) {
 		return this.mappingContext.equals(context);
 	}
 
-	@Nullable
-	private IndexInfo fetchIndexInformation(@Nullable IndexDefinitionHolder indexDefinition) {
+	private @Nullable IndexInfo fetchIndexInformation(@Nullable IndexDefinitionHolder indexDefinition) {
 
 		if (indexDefinition == null) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
index a5988b8c1d..9daedff91c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java
@@ -33,6 +33,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.annotation.MergedAnnotation;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.domain.Sort;
@@ -61,7 +62,6 @@
 import org.springframework.data.util.TypeInformation;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -165,7 +165,7 @@ private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, Mongo
 			}
 
 			if (persistentProperty.isEntity()) {
-				indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
+				indexes.addAll(resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(persistentProperty),
 						persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty),
 						root.getCollection(), guard));
 			}
@@ -232,7 +232,7 @@ private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty pers
 
 		if (persistentProperty.isEntity()) {
 			try {
-				indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty),
+				indexes.addAll(resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(persistentProperty),
 						propertyDotPath.toString(), propertyPath, collection, guard));
 			} catch (CyclicPropertyReferenceException e) {
 				LOGGER.info(e.getMessage());
@@ -385,7 +385,7 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty)
 
 						try {
 							appendTextIndexInformation(propertyDotPath, propertyPath, indexDefinitionBuilder,
-									mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
+									mappingContext.getRequiredPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard);
 						} catch (CyclicPropertyReferenceException e) {
 							LOGGER.info(e.getMessage());
 						} catch (InvalidDataAccessApiUsageException e) {
@@ -520,8 +520,7 @@ private org.bson.Document resolveCompoundIndexKeyFromStringDefinition(String dot
 	 * @param persistentProperty
 	 * @return
 	 */
-	@Nullable
-	protected IndexDefinitionHolder createIndexDefinition(String dotPath, String collection,
+	protected @Nullable IndexDefinitionHolder createIndexDefinition(String dotPath, String collection,
 			MongoPersistentProperty persistentProperty) {
 
 		Indexed index = persistentProperty.findAnnotation(Indexed.class);
@@ -577,7 +576,7 @@ protected IndexDefinitionHolder createIndexDefinition(String dotPath, String col
 		return new IndexDefinitionHolder(dotPath, indexDefinition, collection);
 	}
 
-	private PartialIndexFilter evaluatePartialFilter(String filterExpression, PersistentEntity<?, ?> entity) {
+	private PartialIndexFilter evaluatePartialFilter(String filterExpression, @Nullable PersistentEntity<?, ?> entity) {
 
 		Object result = ExpressionUtils.evaluate(filterExpression, () -> getEvaluationContextForProperty(entity));
 
@@ -588,7 +587,7 @@ private PartialIndexFilter evaluatePartialFilter(String filterExpression, Persis
 		return PartialIndexFilter.of(BsonUtils.parse(filterExpression, null));
 	}
 
-	private org.bson.Document evaluateWildcardProjection(String projectionExpression, PersistentEntity<?, ?> entity) {
+	private org.bson.Document evaluateWildcardProjection(String projectionExpression, @Nullable PersistentEntity<?, ?> entity) {
 
 		Object result = ExpressionUtils.evaluate(projectionExpression, () -> getEvaluationContextForProperty(entity));
 
@@ -599,7 +598,7 @@ private org.bson.Document evaluateWildcardProjection(String projectionExpression
 		return BsonUtils.parse(projectionExpression, null);
 	}
 
-	private Collation evaluateCollation(String collationExpression, PersistentEntity<?, ?> entity) {
+	private Collation evaluateCollation(String collationExpression, @Nullable PersistentEntity<?, ?> entity) {
 
 		Object result = ExpressionUtils.evaluate(collationExpression, () -> getEvaluationContextForProperty(entity));
 		if (result instanceof org.bson.Document document) {
@@ -692,8 +691,7 @@ public void setEvaluationContextProvider(EvaluationContextProvider evaluationCon
 	 * @param persistentProperty
 	 * @return
 	 */
-	@Nullable
-	protected IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection,
+	protected @Nullable IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection,
 			MongoPersistentProperty persistentProperty) {
 
 		GeoSpatialIndexed index = persistentProperty.findAnnotation(GeoSpatialIndexed.class);
@@ -812,8 +810,7 @@ private static Duration computeIndexTimeout(String timeoutValue, Supplier<Evalua
 	 * @return the collation present on either the annotation or the entity as a fallback. Might be {@literal null}.
 	 * @since 4.0
 	 */
-	@Nullable
-	private Collation resolveCollation(Annotation annotation, @Nullable PersistentEntity<?, ?> entity) {
+	private @Nullable Collation resolveCollation(Annotation annotation, @Nullable PersistentEntity<?, ?> entity) {
 		return MergedAnnotation.from(annotation).getValue("collation", String.class).filter(StringUtils::hasText)
 				.map(it -> evaluateCollation(it, entity)).orElseGet(() -> {
 
@@ -1138,8 +1135,7 @@ public IncludeStrategy getStrategy() {
 			return strategy;
 		}
 
-		@Nullable
-		public TextIndexedFieldSpec getParentFieldSpec() {
+		public @Nullable TextIndexedFieldSpec getParentFieldSpec() {
 			return parentFieldSpec;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java
index a87b15de45..0b473388fb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java
@@ -20,9 +20,10 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -235,6 +236,7 @@ public TextIndexDefinitionBuilder() {
 		 * @param name
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder named(String name) {
 			this.instance.name = name;
 			return this;
@@ -246,6 +248,7 @@ public TextIndexDefinitionBuilder named(String name) {
 		 *
 		 * @return
 		 */
+		@Contract("-> this")
 		public TextIndexDefinitionBuilder onAllFields() {
 
 			if (!instance.fieldSpecs.isEmpty()) {
@@ -262,6 +265,7 @@ public TextIndexDefinitionBuilder onAllFields() {
 		 * @param fieldnames
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder onFields(String... fieldnames) {
 
 			for (String fieldname : fieldnames) {
@@ -276,6 +280,7 @@ public TextIndexDefinitionBuilder onFields(String... fieldnames) {
 		 * @param fieldname
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder onField(String fieldname) {
 			return onField(fieldname, 1F);
 		}
@@ -286,6 +291,7 @@ public TextIndexDefinitionBuilder onField(String fieldname) {
 		 * @param fieldname
 		 * @return
 		 */
+		@Contract("_, _ -> this")
 		public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
 
 			if (this.instance.fieldSpecs.contains(ALL_FIELDS)) {
@@ -305,6 +311,7 @@ public TextIndexDefinitionBuilder onField(String fieldname, Float weight) {
 		 * @see <a href=
 		 *      "https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index">https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index</a>
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder withDefaultLanguage(String language) {
 
 			this.instance.defaultLanguage = language;
@@ -317,6 +324,7 @@ public TextIndexDefinitionBuilder withDefaultLanguage(String language) {
 		 * @param fieldname
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
 
 			if (StringUtils.hasText(this.instance.languageOverride)) {
@@ -338,6 +346,7 @@ public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) {
 		 *      "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
 		 * @since 1.10
 		 */
+		@Contract("_ -> this")
 		public TextIndexDefinitionBuilder partial(@Nullable IndexFilter filter) {
 
 			this.instance.filter = filter;
@@ -349,12 +358,14 @@ public TextIndexDefinitionBuilder partial(@Nullable IndexFilter filter) {
 		 *
 		 * @since 2.2
 		 */
+		@Contract("-> this")
 		public TextIndexDefinitionBuilder withSimpleCollation() {
 
 			this.instance.collation = Collation.simple();
 			return this;
 		}
 
+		@Contract("-> new")
 		public TextIndexDefinition build() {
 			return this.instance;
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java
index dcd2b7c022..ff0a92ada1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java
@@ -21,8 +21,9 @@
 import java.util.concurrent.TimeUnit;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
 
@@ -76,6 +77,7 @@ public WildcardIndex(@Nullable String path) {
 	 *
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public WildcardIndex includeId() {
 
 		wildcardProjection.put(FieldName.ID.name(), 1);
@@ -89,6 +91,7 @@ public WildcardIndex includeId() {
 	 * @return this.
 	 */
 	@Override
+	@Contract("_ -> this")
 	public WildcardIndex named(String name) {
 
 		super.named(name);
@@ -101,6 +104,7 @@ public WildcardIndex named(String name) {
 	 * @throws UnsupportedOperationException not supported for wildcard indexes.
 	 */
 	@Override
+	@Contract("-> fail")
 	public Index unique() {
 		throw new UnsupportedOperationException("Wildcard Index does not support 'unique'");
 	}
@@ -111,6 +115,7 @@ public Index unique() {
 	 * @throws UnsupportedOperationException not supported for wildcard indexes.
 	 */
 	@Override
+	@Contract("-> fail")
 	public Index expire(long seconds) {
 		throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'");
 	}
@@ -121,6 +126,7 @@ public Index expire(long seconds) {
 	 * @throws UnsupportedOperationException not supported for wildcard indexes.
 	 */
 	@Override
+	@Contract("_, _ -> fail")
 	public Index expire(long value, TimeUnit timeUnit) {
 		throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'");
 	}
@@ -131,6 +137,7 @@ public Index expire(long value, TimeUnit timeUnit) {
 	 * @throws UnsupportedOperationException not supported for wildcard indexes.
 	 */
 	@Override
+	@Contract("_ -> fail")
 	public Index expire(Duration duration) {
 		throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'");
 	}
@@ -142,6 +149,7 @@ public Index expire(Duration duration) {
 	 * @param paths must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public WildcardIndex wildcardProjectionInclude(String... paths) {
 
 		for (String path : paths) {
@@ -157,6 +165,7 @@ public WildcardIndex wildcardProjectionInclude(String... paths) {
 	 * @param paths must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public WildcardIndex wildcardProjectionExclude(String... paths) {
 
 		for (String path : paths) {
@@ -172,6 +181,7 @@ public WildcardIndex wildcardProjectionExclude(String... paths) {
 	 * @param includeExclude must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public WildcardIndex wildcardProjection(Map<String, Object> includeExclude) {
 
 		wildcardProjection.putAll(includeExclude);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java
index c49f501d8d..8524ee62f7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Support for MongoDB document indexing.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.index;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
index 3d68dbaac2..e865009319 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
@@ -25,6 +25,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.expression.ValueEvaluationContext;
 import org.springframework.data.expression.ValueExpression;
@@ -35,6 +36,7 @@
 import org.springframework.data.mapping.PropertyHandler;
 import org.springframework.data.mapping.model.BasicPersistentEntity;
 import org.springframework.data.mongodb.MongoCollectionUtils;
+import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.util.encryption.EncryptionUtils;
 import org.springframework.data.spel.ExpressionDependencies;
 import org.springframework.data.util.Lazy;
@@ -42,7 +44,6 @@
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -139,9 +140,8 @@ public String getLanguage() {
 		return this.language;
 	}
 
-	@Nullable
 	@Override
-	public MongoPersistentProperty getTextScoreProperty() {
+	public @Nullable MongoPersistentProperty getTextScoreProperty() {
 		return getPersistentProperty(TextScore.class);
 	}
 
@@ -151,7 +151,7 @@ public boolean hasTextScoreProperty() {
 	}
 
 	@Override
-	public org.springframework.data.mongodb.core.query.Collation getCollation() {
+	public @Nullable Collation getCollation() {
 
 		Object collationValue = collationExpression != null
 				? collationExpression.evaluate(getValueEvaluationContext(null))
@@ -189,22 +189,22 @@ public void verify() {
 	}
 
 	@Override
-	public EvaluationContext getEvaluationContext(Object rootObject) {
+	public EvaluationContext getEvaluationContext(@Nullable Object rootObject) {
 		return super.getEvaluationContext(rootObject);
 	}
 
 	@Override
-	public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
+	public EvaluationContext getEvaluationContext(@Nullable Object rootObject, ExpressionDependencies dependencies) {
 		return super.getEvaluationContext(rootObject, dependencies);
 	}
 
 	@Override
-	public ValueEvaluationContext getValueEvaluationContext(Object rootObject) {
+	public ValueEvaluationContext getValueEvaluationContext(@Nullable Object rootObject) {
 		return super.getValueEvaluationContext(rootObject);
 	}
 
 	@Override
-	public ValueEvaluationContext getValueEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
+	public ValueEvaluationContext getValueEvaluationContext(@Nullable Object rootObject, ExpressionDependencies dependencies) {
 		return super.getValueEvaluationContext(rootObject, dependencies);
 	}
 
@@ -243,7 +243,11 @@ public int compare(@Nullable MongoPersistentProperty o1, @Nullable MongoPersiste
 				return -1;
 			}
 
-			return o1.getFieldOrder() - o2.getFieldOrder();
+			if(o1 != null && o2 != null) {
+				return o1.getFieldOrder() - o2.getFieldOrder();
+			}
+
+			return o1 != null ? o1.getFieldOrder() : -1;
 		}
 	}
 
@@ -257,7 +261,7 @@ public int compare(@Nullable MongoPersistentProperty o1, @Nullable MongoPersiste
 	 * @return can be {@literal null}.
 	 */
 	@Override
-	protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(MongoPersistentProperty property) {
+	protected @Nullable MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(MongoPersistentProperty property) {
 
 		Assert.notNull(property, "MongoPersistentProperty must not be null");
 
@@ -268,7 +272,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul
 		MongoPersistentProperty currentIdProperty = getIdProperty();
 
 		boolean currentIdPropertyIsSet = currentIdProperty != null;
-		@SuppressWarnings("null")
+		@SuppressWarnings("NullAway")
 		boolean currentIdPropertyIsExplicit = currentIdPropertyIsSet && currentIdProperty.isExplicitIdProperty();
 		boolean newIdPropertyIsExplicit = property.isExplicitIdProperty();
 
@@ -277,7 +281,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul
 
 		}
 
-		@SuppressWarnings("null")
+		@SuppressWarnings("NullAway")
 		Field currentIdPropertyField = currentIdProperty.getField();
 
 		if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) {
@@ -308,8 +312,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul
 	 * @param potentialExpression can be {@literal null}
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	private static ValueExpression detectExpression(@Nullable String potentialExpression) {
+	private static @Nullable ValueExpression detectExpression(@Nullable String potentialExpression) {
 
 		if (!StringUtils.hasText(potentialExpression)) {
 			return null;
@@ -352,7 +355,7 @@ private void assertUniqueness(MongoPersistentProperty property) {
 	}
 
 	@Override
-	public Collection<Object> getEncryptionKeyIds() {
+	public @Nullable Collection<Object> getEncryptionKeyIds() {
 
 		Encrypted encrypted = findAnnotation(Encrypted.class);
 		if (encrypted == null) {
@@ -405,6 +408,7 @@ private static void potentiallyAssertTextScoreType(MongoPersistentProperty persi
 			}
 		}
 
+		@SuppressWarnings("NullAway")
 		private static void potentiallyAssertDBRefTargetType(MongoPersistentProperty persistentProperty) {
 
 			if (persistentProperty.isDbReference() && persistentProperty.getDBRef().lazy()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
index 5c3b4e6532..027a570fa3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
@@ -23,7 +23,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.env.StandardEnvironment;
 import org.springframework.data.expression.ValueEvaluationContext;
 import org.springframework.data.mapping.Association;
@@ -39,7 +39,6 @@
 import org.springframework.data.util.Lazy;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
@@ -153,8 +152,7 @@ public boolean hasExplicitFieldName() {
 		return StringUtils.hasText(getAnnotatedFieldName());
 	}
 
-	@Nullable
-	private String getAnnotatedFieldName() {
+	private @Nullable String getAnnotatedFieldName() {
 
 		org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation(
 				org.springframework.data.mongodb.core.mapping.Field.class);
@@ -197,9 +195,8 @@ public DBRef getDBRef() {
 		return findAnnotation(DBRef.class);
 	}
 
-	@Nullable
 	@Override
-	public DocumentReference getDocumentReference() {
+	public @Nullable DocumentReference getDocumentReference() {
 		return findAnnotation(DocumentReference.class);
 	}
 
@@ -258,6 +255,7 @@ public MongoField getMongoField() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public Collection<Object> getEncryptionKeyIds() {
 
 		Encrypted encrypted = findAnnotation(Encrypted.class);
@@ -282,6 +280,7 @@ public Collection<Object> getEncryptionKeyIds() {
 		return target;
 	}
 
+	@SuppressWarnings("NullAway")
 	protected MongoField doGetMongoField() {
 
 		MongoFieldBuilder builder = MongoField.builder();
@@ -295,6 +294,7 @@ protected MongoField doGetMongoField() {
 		return builder.build();
 	}
 
+	@SuppressWarnings("NullAway")
 	private String doGetFieldName() {
 
 		if (isIdProperty()) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java
index 105c38b288..eb8d08db64 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.mongodb.core.mapping;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.FieldNamingStrategy;
 import org.springframework.data.mapping.model.Property;
 import org.springframework.data.mapping.model.SimpleTypeHolder;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link MongoPersistentProperty} caching access to {@link #isIdProperty()} and {@link #getFieldName()}.
@@ -121,12 +121,12 @@ public boolean isDbReference() {
 	}
 
 	@Override
-	public DBRef getDBRef() {
+	public @Nullable DBRef getDBRef() {
 		return dbref.getNullable();
 	}
 
 	@Override
-	public DocumentReference getDocumentReference() {
+	public @Nullable DocumentReference getDocumentReference() {
 		return documentReference.getNullable();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java
index 6f0e1ae4c3..881d741ee4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java
@@ -15,7 +15,9 @@
  */
 package org.springframework.data.mongodb.core.mapping;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.FieldName.Type;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -139,7 +141,7 @@ public String toString() {
 	 */
 	public static class MongoFieldBuilder {
 
-		private String name;
+		private @Nullable String name;
 		private Type nameType = Type.PATH;
 		private FieldType type = FieldType.IMPLICIT;
 		private int order = Integer.MAX_VALUE;
@@ -150,6 +152,7 @@ public static class MongoFieldBuilder {
 		 * @param fieldType
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public MongoFieldBuilder fieldType(FieldType fieldType) {
 
 			this.type = fieldType;
@@ -163,6 +166,7 @@ public MongoFieldBuilder fieldType(FieldType fieldType) {
 		 * @param fieldName
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public MongoFieldBuilder name(String fieldName) {
 
 			Assert.hasText(fieldName, "Field name must not be empty");
@@ -178,6 +182,7 @@ public MongoFieldBuilder name(String fieldName) {
 		 * @param path
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public MongoFieldBuilder path(String path) {
 
 			Assert.hasText(path, "Field path (name) must not be empty");
@@ -193,6 +198,7 @@ public MongoFieldBuilder path(String path) {
 		 * @param order
 		 * @return
 		 */
+		@Contract("_ -> this")
 		public MongoFieldBuilder order(int order) {
 
 			this.order = order;
@@ -204,7 +210,10 @@ public MongoFieldBuilder order(int order) {
 		 *
 		 * @return a new {@link MongoField}.
 		 */
+		@Contract("-> new")
 		public MongoField build() {
+
+			Assert.notNull(name, "Name of Field must not be null");
 			return new MongoField(new FieldName(name, nameType), type, order);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java
index 76c0269861..4540493124 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java
@@ -17,6 +17,7 @@
 
 import java.util.AbstractMap;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -28,7 +29,6 @@
 import org.springframework.data.mapping.model.SimpleTypeHolder;
 import org.springframework.data.util.NullableWrapperConverters;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * Default implementation of a {@link MappingContext} for MongoDB using {@link BasicMongoPersistentEntity} and
@@ -46,8 +46,7 @@ public class MongoMappingContext extends AbstractMappingContext<MongoPersistentE
 	private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY;
 	private boolean autoIndexCreation = false;
 
-	@Nullable
-	private ApplicationContext applicationContext;
+	private @Nullable ApplicationContext applicationContext;
 
 	/**
 	 * Creates a new {@link MongoMappingContext}.
@@ -125,9 +124,8 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) {
 		this.autoIndexCreation = autoCreateIndexes;
 	}
 
-	@Nullable
 	@Override
-	public MongoPersistentEntity<?> getPersistentEntity(MongoPersistentProperty persistentProperty) {
+	public @Nullable MongoPersistentEntity<?> getPersistentEntity(MongoPersistentProperty persistentProperty) {
 
 		MongoPersistentEntity<?> entity = super.getPersistentEntity(persistentProperty);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java
index e02bd00c8d..f1d67e4ae8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java
@@ -17,9 +17,10 @@
 
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentEntity;
 import org.springframework.data.mapping.model.MutablePersistentEntity;
-import org.springframework.lang.Nullable;
+import org.springframework.data.mongodb.core.query.Collation;
 
 /**
  * MongoDB specific {@link PersistentEntity} abstraction.
@@ -68,8 +69,7 @@ public interface MongoPersistentEntity<T> extends MutablePersistentEntity<T, Mon
 	 * @return {@literal null} if not set.
 	 * @since 2.2
 	 */
-	@Nullable
-	org.springframework.data.mongodb.core.query.Collation getCollation();
+	@Nullable Collation getCollation();
 
 	/**
 	 * @return {@literal true} if the entity is annotated with
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java
index e75ac015aa..cdbf940720 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java
@@ -17,12 +17,12 @@
 
 import java.util.Collection;
 
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.mapping.PersistentEntity;
 import org.springframework.data.mapping.PersistentProperty;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
 
 /**
  * MongoDB specific {@link org.springframework.data.mapping.PersistentProperty} extension.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java
index d78494d23b..01bb7792fa 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java
@@ -17,8 +17,8 @@
 
 import java.util.function.Predicate;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.util.Predicates;
-import org.springframework.lang.Nullable;
 
 /**
  * Utility to translate a {@link MongoPersistentProperty} into a corresponding property from a different
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java
index 28a114a918..1976cc35f3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java
@@ -21,7 +21,7 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java
index b3b73397ff..9b42a0d37e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.mapping;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java
index fed08815b8..5c404a9d12 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java
@@ -24,6 +24,7 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.env.Environment;
 import org.springframework.data.mapping.*;
 import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
@@ -31,7 +32,7 @@
 import org.springframework.data.spel.EvaluationContextProvider;
 import org.springframework.data.util.Streamable;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * Unwrapped variant of {@link MongoPersistentEntity}.
@@ -62,8 +63,7 @@ public String getLanguage() {
 	}
 
 	@Override
-	@Nullable
-	public MongoPersistentProperty getTextScoreProperty() {
+	public @Nullable MongoPersistentProperty getTextScoreProperty() {
 		return delegate.getTextScoreProperty();
 	}
 
@@ -73,8 +73,7 @@ public boolean hasTextScoreProperty() {
 	}
 
 	@Override
-	@Nullable
-	public Collation getCollation() {
+	public @Nullable Collation getCollation() {
 		return delegate.getCollation();
 	}
 
@@ -98,13 +97,7 @@ public String getName() {
 		return delegate.getName();
 	}
 
-	@Override
 	@Nullable
-	@Deprecated
-	public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() {
-		return delegate.getPersistenceConstructor();
-	}
-
 	@Override
 	public InstanceCreatorMetadata<MongoPersistentProperty> getInstanceCreatorMetadata() {
 		return delegate.getInstanceCreatorMetadata();
@@ -126,8 +119,7 @@ public boolean isVersionProperty(PersistentProperty<?> property) {
 	}
 
 	@Override
-	@Nullable
-	public MongoPersistentProperty getIdProperty() {
+	public @Nullable MongoPersistentProperty getIdProperty() {
 		return delegate.getIdProperty();
 	}
 
@@ -137,8 +129,7 @@ public MongoPersistentProperty getRequiredIdProperty() {
 	}
 
 	@Override
-	@Nullable
-	public MongoPersistentProperty getVersionProperty() {
+	public @Nullable MongoPersistentProperty getVersionProperty() {
 		return delegate.getVersionProperty();
 	}
 
@@ -148,8 +139,7 @@ public MongoPersistentProperty getRequiredVersionProperty() {
 	}
 
 	@Override
-	@Nullable
-	public MongoPersistentProperty getPersistentProperty(String name) {
+	public @Nullable MongoPersistentProperty getPersistentProperty(String name) {
 		return wrap(delegate.getPersistentProperty(name));
 	}
 
@@ -165,8 +155,7 @@ public MongoPersistentProperty getRequiredPersistentProperty(String name) {
 	}
 
 	@Override
-	@Nullable
-	public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
+	public @Nullable MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) {
 		return wrap(delegate.getPersistentProperty(annotationType));
 	}
 
@@ -232,8 +221,7 @@ public void doWithAssociations(SimpleAssociationHandler handler) {
 	}
 
 	@Override
-	@Nullable
-	public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
+	public <A extends Annotation> @Nullable A findAnnotation(Class<A> annotationType) {
 		return delegate.findAnnotation(annotationType);
 	}
 
@@ -295,7 +283,9 @@ public Spliterator<MongoPersistentProperty> spliterator() {
 		return delegate.spliterator();
 	}
 
-	private MongoPersistentProperty wrap(MongoPersistentProperty source) {
+	@Contract("null -> null; !null -> !null")
+	private @Nullable MongoPersistentProperty wrap(@Nullable MongoPersistentProperty source) {
+
 		if (source == null) {
 			return source;
 		}
@@ -338,7 +328,7 @@ public boolean isUnwrapped() {
 	}
 
 	@Override
-	public Collection<Object> getEncryptionKeyIds() {
+	public @Nullable Collection<Object> getEncryptionKeyIds() {
 		return delegate.getEncryptionKeyIds();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java
index 1d4877478f..ac7f24a555 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java
@@ -20,11 +20,11 @@
 import java.lang.reflect.Method;
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.Association;
 import org.springframework.data.mapping.PersistentEntity;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -47,6 +47,7 @@ public UnwrappedMongoPersistentProperty(MongoPersistentProperty delegate, Unwrap
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public String getFieldName() {
 
 		if (!context.getProperty().isUnwrapped()) {
@@ -57,6 +58,7 @@ public String getFieldName() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public boolean hasExplicitFieldName() {
 		return delegate.hasExplicitFieldName()
 				|| !ObjectUtils.isEmpty(context.getProperty().findAnnotation(Unwrapped.class).prefix());
@@ -108,14 +110,12 @@ public boolean isTextScoreProperty() {
 	}
 
 	@Override
-	@Nullable
-	public DBRef getDBRef() {
+	public @Nullable DBRef getDBRef() {
 		return delegate.getDBRef();
 	}
 
 	@Override
-	@Nullable
-	public DocumentReference getDocumentReference() {
+	public @Nullable DocumentReference getDocumentReference() {
 		return delegate.getDocumentReference();
 	}
 
@@ -145,6 +145,7 @@ public Class<?> getType() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public MongoField getMongoField() {
 
 		if (!context.getProperty().isUnwrapped()) {
@@ -165,8 +166,7 @@ public Iterable<? extends TypeInformation<?>> getPersistentEntityTypeInformation
 	}
 
 	@Override
-	@Nullable
-	public Method getGetter() {
+	public @Nullable Method getGetter() {
 		return delegate.getGetter();
 	}
 
@@ -176,8 +176,7 @@ public Method getRequiredGetter() {
 	}
 
 	@Override
-	@Nullable
-	public Method getSetter() {
+	public @Nullable Method getSetter() {
 		return delegate.getSetter();
 	}
 
@@ -187,8 +186,7 @@ public Method getRequiredSetter() {
 	}
 
 	@Override
-	@Nullable
-	public Method getWither() {
+	public @Nullable Method getWither() {
 		return delegate.getWither();
 	}
 
@@ -198,8 +196,7 @@ public Method getRequiredWither() {
 	}
 
 	@Override
-	@Nullable
-	public Field getField() {
+	public @Nullable Field getField() {
 		return delegate.getField();
 	}
 
@@ -209,14 +206,12 @@ public Field getRequiredField() {
 	}
 
 	@Override
-	@Nullable
-	public String getSpelExpression() {
+	public @Nullable String getSpelExpression() {
 		return delegate.getSpelExpression();
 	}
 
 	@Override
-	@Nullable
-	public Association<MongoPersistentProperty> getAssociation() {
+	public @Nullable Association<MongoPersistentProperty> getAssociation() {
 		return delegate.getAssociation();
 	}
 
@@ -291,8 +286,7 @@ public Collection<Object> getEncryptionKeyIds() {
 	}
 
 	@Override
-	@Nullable
-	public Class<?> getComponentType() {
+	public @Nullable Class<?> getComponentType() {
 		return delegate.getComponentType();
 	}
 
@@ -302,8 +296,7 @@ public Class<?> getRawType() {
 	}
 
 	@Override
-	@Nullable
-	public Class<?> getMapValueType() {
+	public @Nullable Class<?> getMapValueType() {
 		return delegate.getMapValueType();
 	}
 
@@ -313,8 +306,7 @@ public Class<?> getActualType() {
 	}
 
 	@Override
-	@Nullable
-	public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
+	public <A extends Annotation> @Nullable A findAnnotation(Class<A> annotationType) {
 		return delegate.findAnnotation(annotationType);
 	}
 
@@ -324,8 +316,7 @@ public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) t
 	}
 
 	@Override
-	@Nullable
-	public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
+	public <A extends Annotation> @Nullable A findPropertyOrOwnerAnnotation(Class<A> annotationType) {
 		return delegate.findPropertyOrOwnerAnnotation(annotationType);
 	}
 
@@ -340,13 +331,12 @@ public boolean hasActualTypeAnnotation(Class<? extends Annotation> annotationTyp
 	}
 
 	@Override
-	@Nullable
-	public Class<?> getAssociationTargetType() {
+	public @Nullable Class<?> getAssociationTargetType() {
 		return delegate.getAssociationTargetType();
 	}
 
 	@Override
-	public TypeInformation<?> getAssociationTargetTypeInformation() {
+	public @Nullable TypeInformation<?> getAssociationTargetTypeInformation() {
 		return delegate.getAssociationTargetTypeInformation();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java
index 73f4890dec..a8e2c93773 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.mapping.event;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Base class for delete events.
@@ -49,8 +49,7 @@ public AbstractDeleteEvent(Document document, @Nullable Class<T> type, String co
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public Class<T> getType() {
+	public @Nullable Class<T> getType() {
 		return type;
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java
index 55ccaa5f3f..10f4cdbbb7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.mapping.event;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Event being thrown after a single or a set of documents has/have been deleted. The {@link Document} held in the event
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java
index 49d509fb43..c826cadb4e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.mapping.event;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Event being thrown before a document is deleted. The {@link Document} held in the event will represent the query
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java
index eec9a3edf1..bec1986720 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java
@@ -18,8 +18,8 @@
 import java.util.function.Function;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.context.ApplicationEvent;
-import org.springframework.lang.Nullable;
 
 /**
  * Base {@link ApplicationEvent} triggered by Spring Data MongoDB.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java
index 0cc9d071a3..71ed503b20 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Mapping event callback infrastructure for the MongoDB document-to-object mapping subsystem.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.mapping.event;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java
index 0a513f1a18..f5c917d7d7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Infrastructure for the MongoDB document-to-object mapping subsystem.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.mapping;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java
index 32a9ed5118..ed9c148a1c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.mapreduce;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Value object to encapsulate results of a map-reduce count.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java
index 9f34ec44e4..2e5f543534 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java
@@ -20,10 +20,11 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.client.model.MapReduceAction;
+import org.springframework.lang.Contract;
 
 /**
  * @author Mark Pollack
@@ -65,6 +66,7 @@ public static MapReduceOptions options() {
 	 * @param limit Limit the number of objects to process
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions limit(int limit) {
 
 		this.limit = limit;
@@ -78,6 +80,7 @@ public MapReduceOptions limit(int limit) {
 	 * @param collectionName The name of the collection where the results of the map-reduce operation will be stored.
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions outputCollection(String collectionName) {
 
 		this.outputCollection = collectionName;
@@ -91,6 +94,7 @@ public MapReduceOptions outputCollection(String collectionName) {
 	 * @param outputDatabase The name of the database where the results of the map-reduce operation will be stored.
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions outputDatabase(@Nullable String outputDatabase) {
 
 		this.outputDatabase = Optional.ofNullable(outputDatabase);
@@ -105,6 +109,7 @@ public MapReduceOptions outputDatabase(@Nullable String outputDatabase) {
 	 * @return this.
 	 * @since 3.0
 	 */
+	@Contract("-> this")
 	public MapReduceOptions actionInline() {
 
 		this.mapReduceAction = null;
@@ -119,6 +124,7 @@ public MapReduceOptions actionInline() {
 	 * @return this.
 	 * @since 3.0
 	 */
+	@Contract("-> this")
 	public MapReduceOptions actionMerge() {
 
 		this.mapReduceAction = MapReduceAction.MERGE;
@@ -133,6 +139,7 @@ public MapReduceOptions actionMerge() {
 	 * @return this.
 	 * @since 3.0
 	 */
+	@Contract("-> this")
 	public MapReduceOptions actionReduce() {
 
 		this.mapReduceAction = MapReduceAction.REDUCE;
@@ -146,6 +153,7 @@ public MapReduceOptions actionReduce() {
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 * @since 3.0
 	 */
+	@Contract("-> this")
 	public MapReduceOptions actionReplace() {
 
 		this.mapReduceAction = MapReduceAction.REPLACE;
@@ -159,6 +167,7 @@ public MapReduceOptions actionReplace() {
 	 * @param outputShared if true, output will be sharded based on _id key.
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions outputSharded(boolean outputShared) {
 
 		this.outputSharded = Optional.of(outputShared);
@@ -171,6 +180,7 @@ public MapReduceOptions outputSharded(boolean outputShared) {
 	 * @param finalizeFunction The finalize function. Can be a JSON string or a Spring Resource URL
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions finalizeFunction(@Nullable String finalizeFunction) {
 
 		this.finalizeFunction = Optional.ofNullable(finalizeFunction);
@@ -184,6 +194,7 @@ public MapReduceOptions finalizeFunction(@Nullable String finalizeFunction) {
 	 * @param scopeVariables variables that can be accessed from map, reduce, and finalize scripts
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions scopeVariables(Map<String, Object> scopeVariables) {
 
 		this.scopeVariables = scopeVariables;
@@ -197,6 +208,7 @@ public MapReduceOptions scopeVariables(Map<String, Object> scopeVariables) {
 	 * @param javaScriptMode if true, have the execution of map-reduce stay in JavaScript
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions javaScriptMode(boolean javaScriptMode) {
 
 		this.jsMode = javaScriptMode;
@@ -208,6 +220,7 @@ public MapReduceOptions javaScriptMode(boolean javaScriptMode) {
 	 *
 	 * @return MapReduceOptions so that methods can be chained in a fluent API style
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions verbose(boolean verbose) {
 
 		this.verbose = verbose;
@@ -221,6 +234,7 @@ public MapReduceOptions verbose(boolean verbose) {
 	 * @return
 	 * @since 2.0
 	 */
+	@Contract("_ -> this")
 	public MapReduceOptions collation(@Nullable Collation collation) {
 
 		this.collation = Optional.ofNullable(collation);
@@ -231,13 +245,11 @@ public Optional<String> getFinalizeFunction() {
 		return this.finalizeFunction;
 	}
 
-	@Nullable
-	public Boolean getJavaScriptMode() {
+	public @Nullable Boolean getJavaScriptMode() {
 		return this.jsMode;
 	}
 
-	@Nullable
-	public String getOutputCollection() {
+	public @Nullable String getOutputCollection() {
 		return this.outputCollection;
 	}
 
@@ -279,8 +291,7 @@ public Optional<Collation> getCollation() {
 	 * @return the mapped action or {@literal null} if the action maps to inline output.
 	 * @since 2.0.10
 	 */
-	@Nullable
-	public MapReduceAction getMapReduceAction() {
+	public @Nullable MapReduceAction getMapReduceAction() {
 		return mapReduceAction;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
index 865a4e9438..1d4f644bd1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java
@@ -19,7 +19,7 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -71,13 +71,11 @@ public MapReduceCounts getCounts() {
 		return mapReduceCounts;
 	}
 
-	@Nullable
-	public String getOutputCollection() {
+	public @Nullable String getOutputCollection() {
 		return outputCollection;
 	}
 
-	@Nullable
-	public Document getRawResults() {
+	public @Nullable Document getRawResults() {
 		return rawResults;
 	}
 
@@ -147,7 +145,8 @@ private static String parseOutputCollection(Document rawResults) {
 			return null;
 		}
 
-		return resultField instanceof Document document ? document.get("collection").toString()
+		return resultField instanceof Document document && document.containsKey("collection")
+				? document.get("collection").toString()
 				: resultField.toString();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java
index 28de7fe850..d99f6d9237 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.mapreduce;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * @deprecated since 3.4 in favor of {@link org.springframework.data.mongodb.core.aggregation}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java
index 65522d8613..c5f5840e6b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java
@@ -3,6 +3,6 @@
  * @deprecated since MongoDB server version 5.0
  */
 @Deprecated
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.mapreduce;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java
index fec7fa60ef..e1da0b33ce 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java
@@ -20,12 +20,13 @@
 
 import org.bson.BsonValue;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ChangeStreamOptions;
 import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest.ChangeStreamRequestOptions;
 import org.springframework.data.mongodb.core.query.Collation;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.client.model.changestream.ChangeStreamDocument;
@@ -215,12 +216,12 @@ public ChangeStreamOptions getChangeStreamOptions() {
 		}
 
 		@Override
-		public String getCollectionName() {
+		public @Nullable String getCollectionName() {
 			return collectionName;
 		}
 
 		@Override
-		public String getDatabaseName() {
+		public @Nullable String getDatabaseName() {
 			return databaseName;
 		}
 
@@ -253,6 +254,7 @@ private ChangeStreamRequestBuilder() {}
 		 * @param databaseName must not be {@literal null} nor empty.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> database(String databaseName) {
 
 			Assert.hasText(databaseName, "DatabaseName must not be null");
@@ -267,6 +269,7 @@ public ChangeStreamRequestBuilder<T> database(String databaseName) {
 		 * @param collectionName must not be {@literal null} nor empty.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> collection(String collectionName) {
 
 			Assert.hasText(collectionName, "CollectionName must not be null");
@@ -281,6 +284,7 @@ public ChangeStreamRequestBuilder<T> collection(String collectionName) {
 		 * @param messageListener must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> publishTo(
 				MessageListener<ChangeStreamDocument<Document>, ? super T> messageListener) {
 
@@ -308,6 +312,7 @@ public ChangeStreamRequestBuilder<T> publishTo(
 		 * @see ChangeStreamOptions#getFilter()
 		 * @see ChangeStreamOptionsBuilder#filter(Aggregation)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> filter(Aggregation aggregation) {
 
 			Assert.notNull(aggregation, "Aggregation must not be null");
@@ -323,6 +328,7 @@ public ChangeStreamRequestBuilder<T> filter(Aggregation aggregation) {
 		 * @return this.
 		 * @see ChangeStreamOptions#getFilter()
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> filter(Document... pipeline) {
 
 			Assert.notNull(pipeline, "Aggregation pipeline must not be null");
@@ -340,6 +346,7 @@ public ChangeStreamRequestBuilder<T> filter(Document... pipeline) {
 		 * @see ChangeStreamOptions#getCollation()
 		 * @see ChangeStreamOptionsBuilder#collation(Collation)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> collation(Collation collation) {
 
 			Assert.notNull(collation, "Collation must not be null");
@@ -357,6 +364,7 @@ public ChangeStreamRequestBuilder<T> collation(Collation collation) {
 		 * @see ChangeStreamOptions#getResumeToken()
 		 * @see ChangeStreamOptionsBuilder#resumeToken(org.bson.BsonValue)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> resumeToken(BsonValue resumeToken) {
 
 			Assert.notNull(resumeToken, "Resume token not be null");
@@ -373,6 +381,7 @@ public ChangeStreamRequestBuilder<T> resumeToken(BsonValue resumeToken) {
 		 * @see ChangeStreamOptions#getResumeTimestamp()
 		 * @see ChangeStreamOptionsBuilder#resumeAt(java.time.Instant)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> resumeAt(Instant clusterTime) {
 
 			Assert.notNull(clusterTime, "ClusterTime must not be null");
@@ -388,6 +397,7 @@ public ChangeStreamRequestBuilder<T> resumeAt(Instant clusterTime) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> resumeAfter(BsonValue resumeToken) {
 
 			Assert.notNull(resumeToken, "ResumeToken must not be null");
@@ -403,6 +413,7 @@ public ChangeStreamRequestBuilder<T> resumeAfter(BsonValue resumeToken) {
 		 * @return this.
 		 * @since 2.2
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> startAfter(BsonValue resumeToken) {
 
 			Assert.notNull(resumeToken, "ResumeToken must not be null");
@@ -418,6 +429,7 @@ public ChangeStreamRequestBuilder<T> startAfter(BsonValue resumeToken) {
 		 * @see ChangeStreamOptions#getFullDocumentLookup()
 		 * @see ChangeStreamOptionsBuilder#fullDocumentLookup(FullDocument)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> fullDocumentLookup(FullDocument lookup) {
 
 			Assert.notNull(lookup, "FullDocument not be null");
@@ -434,6 +446,7 @@ public ChangeStreamRequestBuilder<T> fullDocumentLookup(FullDocument lookup) {
 		 * @see ChangeStreamOptions#getFullDocumentBeforeChangeLookup()
 		 * @see ChangeStreamOptionsBuilder#fullDocumentBeforeChangeLookup(FullDocumentBeforeChange)
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) {
 
 			Assert.notNull(lookup, "FullDocumentBeforeChange not be null");
@@ -448,6 +461,7 @@ public ChangeStreamRequestBuilder<T> fullDocumentBeforeChangeLookup(FullDocument
 		 * @param timeout must not be {@literal null}.
 		 * @since 3.0
 		 */
+		@Contract("_ -> this")
 		public ChangeStreamRequestBuilder<T> maxAwaitTime(Duration timeout) {
 
 			Assert.notNull(timeout, "timeout not be null");
@@ -459,6 +473,7 @@ public ChangeStreamRequestBuilder<T> maxAwaitTime(Duration timeout) {
 		/**
 		 * @return the build {@link ChangeStreamRequest}.
 		 */
+		@Contract("-> new")
 		public ChangeStreamRequest<T> build() {
 
 			Assert.notNull(listener, "MessageListener must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java
index fc8372613b..cc4d3f0bdb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java
@@ -27,6 +27,7 @@
 import org.bson.BsonTimestamp;
 import org.bson.BsonValue;
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ChangeStreamEvent;
 import org.springframework.data.mongodb.core.ChangeStreamOptions;
 import org.springframework.data.mongodb.core.MongoTemplate;
@@ -39,7 +40,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.messaging.Message.MessageProperties;
 import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ErrorHandler;
 import org.springframework.util.StringUtils;
@@ -224,21 +224,18 @@ static class ChangeStreamEventMessage<T> implements Message<ChangeStreamDocument
 			this.messageProperties = messageProperties;
 		}
 
-		@Nullable
 		@Override
-		public ChangeStreamDocument<Document> getRaw() {
+		public @Nullable ChangeStreamDocument<Document> getRaw() {
 			return delegate.getRaw();
 		}
 
-		@Nullable
 		@Override
-		public T getBody() {
+		public @Nullable T getBody() {
 			return delegate.getBody();
 		}
 
-		@Nullable
 		@Override
-		public T getBodyBeforeChange() {
+		public @Nullable T getBodyBeforeChange() {
 			return delegate.getBodyBeforeChange();
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java
index 41b5fed4f5..662960284d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java
@@ -21,12 +21,12 @@
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessResourceFailureException;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.messaging.Message.MessageProperties;
 import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
 import org.springframework.data.util.Lock;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ErrorHandler;
 
@@ -51,7 +51,7 @@ abstract class CursorReadingTask<T, R> implements Task {
 
 	private State state = State.CREATED;
 
-	private MongoCursor<T> cursor;
+	private @Nullable MongoCursor<T> cursor;
 
 	/**
 	 * @param template must not be {@literal null}.
@@ -109,6 +109,7 @@ public void run() {
 	 * is immediately {@link MongoCursor#close() closed} and a new {@link MongoCursor} is requested until a valid one is
 	 * retrieved or the {@link #state} changes.
 	 */
+	@SuppressWarnings("NullAway")
 	private void start() {
 
 		lock.executeWithoutResult(() -> {
@@ -188,6 +189,7 @@ public boolean awaitStart(Duration timeout) throws InterruptedException {
 		return awaitStart.await(timeout.toNanos(), TimeUnit.NANOSECONDS);
 	}
 
+	@SuppressWarnings("NullAway")
 	protected Message<T, R> createMessage(T source, Class<R> targetType, RequestOptions options) {
 
 		SimpleMessage<T, T> message = new SimpleMessage<>(source, source, MessageProperties.builder()
@@ -209,11 +211,10 @@ private void emitMessage(Message<T, R> message) {
 		}
 	}
 
-	@Nullable
-	private T getNext() {
+	private @Nullable T getNext() {
 
 		return lock.execute(() -> {
-			if (State.RUNNING.equals(state)) {
+			if (cursor != null && State.RUNNING.equals(state)) {
 				return cursor.tryNext();
 			}
 			throw new IllegalStateException(String.format("Cursor %s is not longer open", cursor));
@@ -239,8 +240,7 @@ private static boolean isValidCursor(@Nullable MongoCursor<?> cursor) {
 	 * @return can be {@literal null}.
 	 * @throws RuntimeException The potentially translated exception.
 	 */
-	@Nullable
-	private <V> V execute(Supplier<V> callback) {
+	private <V> @Nullable V execute(Supplier<V> callback) {
 
 		try {
 			return callback.get();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java
index 546f3fdd33..1b24e67e07 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java
@@ -25,12 +25,12 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.task.SimpleAsyncTaskExecutor;
 import org.springframework.dao.DataAccessResourceFailureException;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
 import org.springframework.data.util.Lock;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ErrorHandler;
 import org.springframework.util.ObjectUtils;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java
index 1c934e8302..f9a9c4131d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java
@@ -16,6 +16,7 @@
 package org.springframework.data.mongodb.core.messaging;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.util.ClassUtils;
 
@@ -38,12 +39,12 @@ class LazyMappingDelegatingMessage<S, T> implements Message<S, T> {
 	}
 
 	@Override
-	public S getRaw() {
+	public @Nullable S getRaw() {
 		return delegate.getRaw();
 	}
 
 	@Override
-	public T getBody() {
+	public @Nullable T getBody() {
 
 		if (delegate.getBody() == null || targetType.equals(delegate.getBody().getClass())) {
 			return targetType.cast(delegate.getBody());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java
index 46db068096..e7aa5b036d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java
@@ -15,7 +15,8 @@
  */
 package org.springframework.data.mongodb.core.messaging;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -59,8 +60,7 @@ public interface Message<S, T> {
 	 * @return can be {@literal null}.
 	 * @since 4.0
 	 */
-	@Nullable
-	default T getBodyBeforeChange() {
+	default @Nullable T getBodyBeforeChange() {
 		return null;
 	}
 
@@ -87,8 +87,7 @@ class MessageProperties {
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public String getDatabaseName() {
+		public @Nullable String getDatabaseName() {
 			return databaseName;
 		}
 
@@ -97,8 +96,7 @@ public String getDatabaseName() {
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public String getCollectionName() {
+		public @Nullable String getCollectionName() {
 			return collectionName;
 		}
 
@@ -162,6 +160,7 @@ public static class MessagePropertiesBuilder {
 			 * @param dbName must not be {@literal null}.
 			 * @return this.
 			 */
+			@Contract("_ -> this")
 			public MessagePropertiesBuilder databaseName(String dbName) {
 
 				Assert.notNull(dbName, "Database name must not be null");
@@ -174,6 +173,7 @@ public MessagePropertiesBuilder databaseName(String dbName) {
 			 * @param collectionName must not be {@literal null}.
 			 * @return this
 			 */
+			@Contract("_ -> this")
 			public MessagePropertiesBuilder collectionName(String collectionName) {
 
 				Assert.notNull(collectionName, "Collection name must not be null");
@@ -185,6 +185,7 @@ public MessagePropertiesBuilder collectionName(String collectionName) {
 			/**
 			 * @return the built {@link MessageProperties}.
 			 */
+			@Contract("-> new")
 			public MessageProperties build() {
 
 				MessageProperties properties = new MessageProperties();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java
index be5308e3cf..acb7bfd8a2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.messaging;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -46,12 +46,12 @@ class SimpleMessage<S, T> implements Message<S, T> {
 	}
 
 	@Override
-	public S getRaw() {
+	public @Nullable S getRaw() {
 		return raw;
 	}
 
 	@Override
-	public T getBody() {
+	public @Nullable T getBody() {
 		return body;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java
index 287ba293b6..7b914f16f5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java
@@ -17,9 +17,9 @@
 
 import java.time.Duration;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -61,8 +61,7 @@ interface RequestOptions {
 		 * @return the name of the database to subscribe to. Can be {@literal null} in which case the default
 		 *         {@link MongoDatabaseFactory#getMongoDatabase() database} is used.
 		 */
-		@Nullable
-		default String getDatabaseName() {
+		default @Nullable String getDatabaseName() {
 			return null;
 		}
 
@@ -106,7 +105,7 @@ static RequestOptions justDatabase(String database) {
 			return new RequestOptions() {
 
 				@Override
-				public String getCollectionName() {
+				public @Nullable String getCollectionName() {
 					return null;
 				}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java
index c6caef12fb..92e23ff847 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java
@@ -18,10 +18,11 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions;
 import org.springframework.data.mongodb.core.messaging.TailableCursorRequest.TailableCursorRequestOptions.TailableCursorRequestOptionsBuilder;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -121,6 +122,7 @@ public static class TailableCursorRequestOptions implements SubscriptionRequest.
 
 		TailableCursorRequestOptions() {}
 
+		@SuppressWarnings("NullAway")
 		public static TailableCursorRequestOptions of(RequestOptions options) {
 			return builder().collection(options.getCollectionName()).build();
 		}
@@ -136,7 +138,7 @@ public static TailableCursorRequestOptionsBuilder builder() {
 		}
 
 		@Override
-		public String getCollectionName() {
+		public @Nullable String getCollectionName() {
 			return collectionName;
 		}
 
@@ -163,6 +165,7 @@ private TailableCursorRequestOptionsBuilder() {}
 			 * @param collection must not be {@literal null} nor {@literal empty}.
 			 * @return this.
 			 */
+			@Contract("_ -> this")
 			public TailableCursorRequestOptionsBuilder collection(String collection) {
 
 				Assert.hasText(collection, "Collection must not be null nor empty");
@@ -177,6 +180,7 @@ public TailableCursorRequestOptionsBuilder collection(String collection) {
 			 * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}.
 			 * @return this.
 			 */
+			@Contract("_ -> this")
 			public TailableCursorRequestOptionsBuilder filter(Query filter) {
 
 				Assert.notNull(filter, "Filter must not be null");
@@ -188,6 +192,7 @@ public TailableCursorRequestOptionsBuilder filter(Query filter) {
 			/**
 			 * @return the built {@link TailableCursorRequestOptions}.
 			 */
+			@Contract("-> new")
 			public TailableCursorRequestOptions build() {
 
 				TailableCursorRequestOptions options = new TailableCursorRequestOptions();
@@ -220,6 +225,7 @@ private TailableCursorRequestBuilder() {}
 		 * @param collectionName must not be {@literal null} nor empty.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public TailableCursorRequestBuilder<T> collection(String collectionName) {
 
 			Assert.hasText(collectionName, "CollectionName must not be null");
@@ -234,6 +240,7 @@ public TailableCursorRequestBuilder<T> collection(String collectionName) {
 		 * @param messageListener must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public TailableCursorRequestBuilder<T> publishTo(MessageListener<Document, ? super T> messageListener) {
 
 			Assert.notNull(messageListener, "MessageListener must not be null");
@@ -248,6 +255,7 @@ public TailableCursorRequestBuilder<T> publishTo(MessageListener<Document, ? sup
 		 * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public TailableCursorRequestBuilder<T> filter(Query filter) {
 
 			Assert.notNull(filter, "Filter must not be null");
@@ -259,6 +267,7 @@ public TailableCursorRequestBuilder<T> filter(Query filter) {
 		/**
 		 * @return the build {@link ChangeStreamRequest}.
 		 */
+		@Contract("_ -> new")
 		public TailableCursorRequest<T> build() {
 
 			Assert.notNull(listener, "MessageListener must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java
index 35be8f2ef8..aa879cc3c3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java
@@ -2,5 +2,5 @@
  * MongoDB specific messaging support for listening to eg.
  * <a href="https://docs.mongodb.com/manual/changeStreams/">Change Streams</a>.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.messaging;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java
index e2f9169d0d..cae1d3df48 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB core support.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java
index 8b1620b320..fd81030275 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java
@@ -18,7 +18,8 @@
 import static org.springframework.util.ObjectUtils.*;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -91,6 +92,7 @@ public BasicQuery(Document queryObject, Document fieldsObject) {
 	 * @param query the query to copy.
 	 * @since 4.4
 	 */
+	@SuppressWarnings("NullAway")
 	public BasicQuery(Query query) {
 
 		super(query);
@@ -101,6 +103,7 @@ public BasicQuery(Query query) {
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public Query addCriteria(CriteriaDefinition criteria) {
 
 		this.queryObject.putAll(criteria.getCriteriaObject());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java
index 12843ce622..9ca1e1f1b8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java
@@ -15,21 +15,14 @@
  */
 package org.springframework.data.mongodb.core.query;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiFunction;
 
 import org.bson.Document;
-
-import org.springframework.lang.Nullable;
-import org.springframework.util.ClassUtils;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 
 /**
- * {@link Document}-based {@link Update} variant.
- *
  * @author Thomas Risberg
  * @author John Brisbin
  * @author Oliver Gierke
@@ -41,114 +34,83 @@ public class BasicUpdate extends Update {
 	private final Document updateObject;
 
 	public BasicUpdate(String updateString) {
-		this(Document.parse(updateString));
+		super();
+		this.updateObject = Document.parse(updateString);
 	}
 
 	public BasicUpdate(Document updateObject) {
+		super();
 		this.updateObject = updateObject;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update set(String key, @Nullable Object value) {
-		setOperationValue("$set", key, value);
+		updateObject.put("$set", Collections.singletonMap(key, value));
 		return this;
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public Update unset(String key) {
-		setOperationValue("$unset", key, 1);
+		updateObject.put("$unset", Collections.singletonMap(key, 1));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update inc(String key, Number inc) {
-		setOperationValue("$inc", key, inc);
+		updateObject.put("$inc", Collections.singletonMap(key, inc));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update push(String key, @Nullable Object value) {
-		setOperationValue("$push", key, value);
+		updateObject.put("$push", Collections.singletonMap(key, value));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update addToSet(String key, @Nullable Object value) {
-		setOperationValue("$addToSet", key, value);
+		updateObject.put("$addToSet", Collections.singletonMap(key, value));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update pop(String key, Position pos) {
-		setOperationValue("$pop", key, (pos == Position.FIRST ? -1 : 1));
+		updateObject.put("$pop", Collections.singletonMap(key, (pos == Position.FIRST ? -1 : 1)));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update pull(String key, @Nullable Object value) {
-		setOperationValue("$pull", key, value);
+		updateObject.put("$pull", Collections.singletonMap(key, value));
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update pullAll(String key, Object[] values) {
-		setOperationValue("$pullAll", key, List.of(values), (o, o2) -> {
-
-			if (o instanceof List<?> prev && o2 instanceof List<?> currentValue) {
-				List<Object> merged = new ArrayList<>(prev.size() + currentValue.size());
-				merged.addAll(prev);
-				merged.addAll(currentValue);
-				return merged;
-			}
-
-			return o2;
-		});
+		Document keyValue = new Document();
+		keyValue.put(key, Arrays.copyOf(values, values.length));
+		updateObject.put("$pullAll", keyValue);
 		return this;
 	}
 
 	@Override
+	@Contract("_, _ -> this")
 	public Update rename(String oldName, String newName) {
-		setOperationValue("$rename", oldName, newName);
+		updateObject.put("$rename", Collections.singletonMap(oldName, newName));
 		return this;
 	}
 
-	@Override
-	public boolean modifies(String key) {
-		return super.modifies(key) || Update.fromDocument(getUpdateObject()).modifies(key);
-	}
-
 	@Override
 	public Document getUpdateObject() {
 		return updateObject;
 	}
 
-	void setOperationValue(String operator, String key, @Nullable Object value) {
-		setOperationValue(operator, key, value, (o, o2) -> o2);
-	}
-
-	void setOperationValue(String operator, String key, @Nullable Object value,
-			BiFunction<Object, Object, Object> mergeFunction) {
-
-		if (!updateObject.containsKey(operator)) {
-			updateObject.put(operator, Collections.singletonMap(key, value));
-		} else {
-			Object o = updateObject.get(operator);
-			if (o instanceof Map<?, ?> existing) {
-				Map<Object, Object> target = new LinkedHashMap<>(existing);
-
-				if (target.containsKey(key)) {
-					target.put(key, mergeFunction.apply(target.get(key), value));
-				} else {
-					target.put(key, value);
-				}
-				updateObject.put(operator, target);
-			} else {
-				throw new IllegalStateException(
-						"Cannot add ['%s' : { '%s' : ... }]. Operator already exists with value of type [%s] which is not suitable for appending"
-								.formatted(operator, key,
-										o != null ? ClassUtils.getShortName(o.getClass()) : "null"));
-			}
-		}
-	}
-
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java
index de24c0511d..217e669883 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java
@@ -19,8 +19,9 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -180,6 +181,7 @@ public static Collation from(Document source) {
 	 * @param strength comparison level.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation strength(int strength) {
 
 		ComparisonLevel current = this.strength.orElseGet(() -> new ICUComparisonLevel(strength));
@@ -192,6 +194,7 @@ public Collation strength(int strength) {
 	 * @param comparisonLevel must not be {@literal null}.
 	 * @return new {@link Collation}
 	 */
+	@Contract("_ -> new")
 	public Collation strength(ComparisonLevel comparisonLevel) {
 
 		Collation newInstance = copy();
@@ -205,6 +208,7 @@ public Collation strength(ComparisonLevel comparisonLevel) {
 	 * @param caseLevel use {@literal true} to enable {@code caseLevel} comparison.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation caseLevel(boolean caseLevel) {
 
 		ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::primary);
@@ -218,6 +222,7 @@ public Collation caseLevel(boolean caseLevel) {
 	 * @param caseFirst must not be {@literal null}.
 	 * @return new instance of {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation caseFirst(String caseFirst) {
 		return caseFirst(new CaseFirst(caseFirst));
 	}
@@ -228,6 +233,7 @@ public Collation caseFirst(String caseFirst) {
 	 * @param sort must not be {@literal null}.
 	 * @return new instance of {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation caseFirst(CaseFirst sort) {
 
 		ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::tertiary);
@@ -239,6 +245,7 @@ public Collation caseFirst(CaseFirst sort) {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("-> new")
 	public Collation numericOrderingEnabled() {
 		return numericOrdering(true);
 	}
@@ -248,6 +255,7 @@ public Collation numericOrderingEnabled() {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("-> new")
 	public Collation numericOrderingDisabled() {
 		return numericOrdering(false);
 	}
@@ -257,6 +265,7 @@ public Collation numericOrderingDisabled() {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation numericOrdering(boolean flag) {
 
 		Collation newInstance = copy();
@@ -271,6 +280,7 @@ public Collation numericOrdering(boolean flag) {
 	 * @param alternate must not be {@literal null}.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation alternate(String alternate) {
 
 		Alternate instance = this.alternate.orElseGet(() -> new Alternate(alternate, Optional.empty()));
@@ -284,6 +294,7 @@ public Collation alternate(String alternate) {
 	 * @param alternate must not be {@literal null}.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation alternate(Alternate alternate) {
 
 		Collation newInstance = copy();
@@ -296,6 +307,7 @@ public Collation alternate(Alternate alternate) {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation backwardDiacriticSort() {
 		return backwards(true);
 	}
@@ -305,6 +317,7 @@ public Collation backwardDiacriticSort() {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("-> new")
 	public Collation forwardDiacriticSort() {
 		return backwards(false);
 	}
@@ -315,6 +328,7 @@ public Collation forwardDiacriticSort() {
 	 * @param backwards must not be {@literal null}.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation backwards(boolean backwards) {
 
 		Collation newInstance = copy();
@@ -327,6 +341,7 @@ public Collation backwards(boolean backwards) {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("-> new")
 	public Collation normalizationEnabled() {
 		return normalization(true);
 	}
@@ -336,6 +351,7 @@ public Collation normalizationEnabled() {
 	 *
 	 * @return new {@link Collation}.
 	 */
+	@Contract("-> new")
 	public Collation normalizationDisabled() {
 		return normalization(false);
 	}
@@ -346,6 +362,7 @@ public Collation normalizationDisabled() {
 	 * @param normalization must not be {@literal null}.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation normalization(boolean normalization) {
 
 		Collation newInstance = copy();
@@ -359,6 +376,7 @@ public Collation normalization(boolean normalization) {
 	 * @param maxVariable must not be {@literal null}.
 	 * @return new {@link Collation}.
 	 */
+	@Contract("_ -> new")
 	public Collation maxVariable(String maxVariable) {
 
 		Alternate alternateValue = alternate.orElseGet(Alternate::shifted);
@@ -370,6 +388,7 @@ public Collation maxVariable(String maxVariable) {
 	 *
 	 * @return the native MongoDB {@link Document} representation of the {@link Collation}.
 	 */
+	@SuppressWarnings("NullAway")
 	public Document toDocument() {
 		return map(toMongoDocumentConverter());
 	}
@@ -379,7 +398,7 @@ public Document toDocument() {
 	 *
 	 * @return he native MongoDB representation of the {@link Collation}.
 	 */
-	public com.mongodb.client.model.Collation toMongoCollation() {
+	public com.mongodb.client.model.@Nullable Collation toMongoCollation() {
 		return map(toMongoCollationConverter());
 	}
 
@@ -390,7 +409,7 @@ public com.mongodb.client.model.Collation toMongoCollation() {
 	 * @param <R>
 	 * @return the converted result.
 	 */
-	public <R> R map(Converter<? super Collation, ? extends R> mapper) {
+	public <R> @Nullable R map(Converter<? super Collation, ? extends R> mapper) {
 		return mapper.convert(this);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
index 8d4cb703bb..547c6965ce 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
@@ -33,6 +33,7 @@
 import org.bson.BsonType;
 import org.bson.Document;
 import org.bson.types.Binary;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Example;
 import org.springframework.data.geo.Circle;
 import org.springframework.data.geo.Point;
@@ -45,7 +46,7 @@
 import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
 import org.springframework.data.mongodb.util.RegexFlags;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ObjectUtils;
@@ -184,6 +185,7 @@ public static Criteria expr(MongoExpression expression) {
 	 *
 	 * @return new instance of {@link Criteria}.
 	 */
+	@Contract("_ -> new")
 	public Criteria and(String key) {
 		return new Criteria(this.criteriaChain, key);
 	}
@@ -194,6 +196,7 @@ public Criteria and(String key) {
 	 * @param value can be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria is(@Nullable Object value) {
 
 		if (!NOT_SET.equals(isValue)) {
@@ -221,6 +224,7 @@ public Criteria is(@Nullable Object value) {
 	 *      Missing Fields: Equality Filter</a>
 	 * @since 3.3
 	 */
+	@Contract("_ -> this")
 	public Criteria isNull() {
 		return is(null);
 	}
@@ -237,6 +241,7 @@ public Criteria isNull() {
 	 *      Fields: Type Check</a>
 	 * @since 3.3
 	 */
+	@Contract("_ -> this")
 	public Criteria isNullValue() {
 
 		criteria.put("$type", BsonType.NULL.getValue());
@@ -254,6 +259,7 @@ private boolean lastOperatorWasNot() {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/ne/">MongoDB Query operator: $ne</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria ne(@Nullable Object value) {
 		criteria.put("$ne", value);
 		return this;
@@ -266,6 +272,7 @@ public Criteria ne(@Nullable Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/lt/">MongoDB Query operator: $lt</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria lt(Object value) {
 		criteria.put("$lt", value);
 		return this;
@@ -278,6 +285,7 @@ public Criteria lt(Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/lte/">MongoDB Query operator: $lte</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria lte(Object value) {
 		criteria.put("$lte", value);
 		return this;
@@ -290,6 +298,7 @@ public Criteria lte(Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/gt/">MongoDB Query operator: $gt</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria gt(Object value) {
 		criteria.put("$gt", value);
 		return this;
@@ -302,6 +311,7 @@ public Criteria gt(Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/gte/">MongoDB Query operator: $gte</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria gte(Object value) {
 		criteria.put("$gte", value);
 		return this;
@@ -314,7 +324,8 @@ public Criteria gte(Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/in/">MongoDB Query operator: $in</a>
 	 */
-	public Criteria in(Object... values) {
+	@Contract("_ -> this")
+	public Criteria in(@Nullable Object ... values) {
 		if (values.length > 1 && values[1] instanceof Collection) {
 			throw new InvalidMongoDbApiUsageException(
 					"You can only pass in one argument of type " + values[1].getClass().getName());
@@ -330,6 +341,7 @@ public Criteria in(Object... values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/in/">MongoDB Query operator: $in</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria in(Collection<?> values) {
 		criteria.put("$in", values);
 		return this;
@@ -342,6 +354,7 @@ public Criteria in(Collection<?> values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nin/">MongoDB Query operator: $nin</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria nin(Object... values) {
 		return nin(Arrays.asList(values));
 	}
@@ -353,6 +366,7 @@ public Criteria nin(Object... values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nin/">MongoDB Query operator: $nin</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria nin(Collection<?> values) {
 		criteria.put("$nin", values);
 		return this;
@@ -366,6 +380,7 @@ public Criteria nin(Collection<?> values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/mod/">MongoDB Query operator: $mod</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria mod(Number value, Number remainder) {
 		List<Object> l = new ArrayList<>(2);
 		l.add(value);
@@ -381,6 +396,7 @@ public Criteria mod(Number value, Number remainder) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/all/">MongoDB Query operator: $all</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria all(Object... values) {
 		return all(Arrays.asList(values));
 	}
@@ -392,6 +408,7 @@ public Criteria all(Object... values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/all/">MongoDB Query operator: $all</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria all(Collection<?> values) {
 		criteria.put("$all", values);
 		return this;
@@ -404,6 +421,7 @@ public Criteria all(Collection<?> values) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/size/">MongoDB Query operator: $size</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria size(int size) {
 		criteria.put("$size", size);
 		return this;
@@ -416,6 +434,7 @@ public Criteria size(int size) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/exists/">MongoDB Query operator: $exists</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria exists(boolean value) {
 		criteria.put("$exists", value);
 		return this;
@@ -431,6 +450,7 @@ public Criteria exists(boolean value) {
 	 *      $sampleRate</a>
 	 * @since 3.3
 	 */
+	@Contract("_ -> this")
 	public Criteria sampleRate(double sampleRate) {
 
 		Assert.isTrue(sampleRate >= 0, "The sample rate must be greater than zero");
@@ -447,6 +467,7 @@ public Criteria sampleRate(double sampleRate) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria type(int typeNumber) {
 		criteria.put("$type", typeNumber);
 		return this;
@@ -460,6 +481,7 @@ public Criteria type(int typeNumber) {
 	 * @since 2.1
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria type(Type... types) {
 
 		Assert.notNull(types, "Types must not be null");
@@ -476,6 +498,7 @@ public Criteria type(Type... types) {
 	 * @since 3.2
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria type(Collection<Type> types) {
 
 		Assert.notNull(types, "Types must not be null");
@@ -490,6 +513,7 @@ public Criteria type(Collection<Type> types) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/not/">MongoDB Query operator: $not</a>
 	 */
+	@Contract("-> this")
 	public Criteria not() {
 		return not(null);
 	}
@@ -501,6 +525,7 @@ public Criteria not() {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/not/">MongoDB Query operator: $not</a>
 	 */
+	@Contract("_ -> this")
 	private Criteria not(@Nullable Object value) {
 		criteria.put("$not", value);
 		return this;
@@ -513,6 +538,7 @@ private Criteria not(@Nullable Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/regex/">MongoDB Query operator: $regex</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria regex(String regex) {
 		return regex(regex, null);
 	}
@@ -525,6 +551,7 @@ public Criteria regex(String regex) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/regex/">MongoDB Query operator: $regex</a>
 	 */
+	@Contract("_, _ -> this")
 	public Criteria regex(String regex, @Nullable String options) {
 		return regex(toPattern(regex, options));
 	}
@@ -535,6 +562,7 @@ public Criteria regex(String regex, @Nullable String options) {
 	 * @param pattern must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria regex(Pattern pattern) {
 
 		Assert.notNull(pattern, "Pattern must not be null");
@@ -553,6 +581,7 @@ public Criteria regex(Pattern pattern) {
 	 * @param regex must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria regex(BsonRegularExpression regex) {
 
 		if (lastOperatorWasNot()) {
@@ -581,6 +610,7 @@ private Pattern toPattern(String regex, @Nullable String options) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/centerSphere/">MongoDB Query operator:
 	 *      $centerSphere</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria withinSphere(Circle circle) {
 
 		Assert.notNull(circle, "Circle must not be null");
@@ -597,6 +627,7 @@ public Criteria withinSphere(Circle circle) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/geoWithin/">MongoDB Query operator:
 	 *      $geoWithin</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria within(Shape shape) {
 
 		Assert.notNull(shape, "Shape must not be null");
@@ -612,6 +643,7 @@ public Criteria within(Shape shape) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/near/">MongoDB Query operator: $near</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria near(Point point) {
 
 		Assert.notNull(point, "Point must not be null");
@@ -629,6 +661,7 @@ public Criteria near(Point point) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nearSphere/">MongoDB Query operator:
 	 *      $nearSphere</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria nearSphere(Point point) {
 
 		Assert.notNull(point, "Point must not be null");
@@ -646,6 +679,7 @@ public Criteria nearSphere(Point point) {
 	 * @since 1.8
 	 */
 	@SuppressWarnings("rawtypes")
+	@Contract("_ -> this")
 	public Criteria intersects(GeoJson geoJson) {
 
 		Assert.notNull(geoJson, "GeoJson must not be null");
@@ -665,6 +699,7 @@ public Criteria intersects(GeoJson geoJson) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/maxDistance/">MongoDB Query operator:
 	 *      $maxDistance</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria maxDistance(double maxDistance) {
 
 		if (createNearCriteriaForCommand("$near", "$maxDistance", maxDistance)
@@ -687,6 +722,7 @@ public Criteria maxDistance(double maxDistance) {
 	 * @return this.
 	 * @since 1.7
 	 */
+	@Contract("_ -> this")
 	public Criteria minDistance(double minDistance) {
 
 		if (createNearCriteriaForCommand("$near", "$minDistance", minDistance)
@@ -706,6 +742,7 @@ public Criteria minDistance(double minDistance) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/elemMatch/">MongoDB Query operator:
 	 *      $elemMatch</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria elemMatch(Criteria criteria) {
 		this.criteria.put("$elemMatch", criteria.getCriteriaObject());
 		return this;
@@ -718,6 +755,7 @@ public Criteria elemMatch(Criteria criteria) {
 	 * @return this.
 	 * @since 1.8
 	 */
+	@Contract("_ -> this")
 	public Criteria alike(Example<?> sample) {
 
 		if (StringUtils.hasText(this.getKey())) {
@@ -745,6 +783,7 @@ public Criteria alike(Example<?> sample) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/">MongoDB Query operator:
 	 *      $jsonSchema</a>
 	 */
+	@Contract("_ -> this")
 	public Criteria andDocumentStructureMatches(MongoJsonSchema schema) {
 
 		Assert.notNull(schema, "Schema must not be null");
@@ -776,6 +815,7 @@ public BitwiseCriteriaOperators bits() {
 	 * @param criteria must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria orOperator(Criteria... criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -793,6 +833,7 @@ public Criteria orOperator(Criteria... criteria) {
 	 * @return this.
 	 * @since 3.2
 	 */
+	@Contract("_ -> this")
 	public Criteria orOperator(Collection<Criteria> criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -810,6 +851,7 @@ public Criteria orOperator(Collection<Criteria> criteria) {
 	 * @param criteria must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria norOperator(Criteria... criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -827,6 +869,7 @@ public Criteria norOperator(Criteria... criteria) {
 	 * @return this.
 	 * @since 3.2
 	 */
+	@Contract("_ -> this")
 	public Criteria norOperator(Collection<Criteria> criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -844,6 +887,7 @@ public Criteria norOperator(Collection<Criteria> criteria) {
 	 * @param criteria must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Criteria andOperator(Criteria... criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -861,6 +905,7 @@ public Criteria andOperator(Criteria... criteria) {
 	 * @return this.
 	 * @since 3.2
 	 */
+	@Contract("_ -> this")
 	public Criteria andOperator(Collection<Criteria> criteria) {
 
 		Assert.notNull(criteria, "Criteria must not be null");
@@ -884,8 +929,7 @@ private Criteria registerCriteriaChainElement(Criteria criteria) {
 	 * @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getKey()
 	 */
 	@Override
-	@Nullable
-	public String getKey() {
+	public @Nullable String getKey() {
 		return this.key;
 	}
 
@@ -1095,7 +1139,7 @@ private boolean isEqual(@Nullable Object left, @Nullable Object right) {
 
 		if (Collection.class.isAssignableFrom(left.getClass())) {
 
-			if (!Collection.class.isAssignableFrom(right.getClass())) {
+			if (right == null || !Collection.class.isAssignableFrom(right.getClass())) {
 				return false;
 			}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java
index c00b1d4b82..c75f709ab9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.query;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * @author Oliver Gierke
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
index 3540a5a836..9775fefdb0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
@@ -22,8 +22,9 @@
 import java.util.Map.Entry;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.MongoExpression;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -52,6 +53,7 @@ public class Field {
 	 * @param field the document field name to be included.
 	 * @return {@code this} field projection instance.
 	 */
+	@Contract("_ -> this")
 	public Field include(String field) {
 
 		Assert.notNull(field, "Key must not be null");
@@ -111,6 +113,7 @@ public FieldProjectionExpression project(MongoExpression expression) {
 	 * @return new instance of {@link FieldProjectionExpression}.
 	 * @since 3.2
 	 */
+	@Contract("_, _ -> this")
 	public Field projectAs(MongoExpression expression, String field) {
 
 		criteria.put(field, expression);
@@ -124,6 +127,7 @@ public Field projectAs(MongoExpression expression, String field) {
 	 * @return {@code this} field projection instance.
 	 * @since 3.1
 	 */
+	@Contract("_ -> this")
 	public Field include(String... fields) {
 		return include(Arrays.asList(fields));
 	}
@@ -135,6 +139,7 @@ public Field include(String... fields) {
 	 * @return {@code this} field projection instance.
 	 * @since 4.4
 	 */
+	@Contract("_ -> this")
 	public Field include(Collection<String> fields) {
 
 		Assert.notNull(fields, "Keys must not be null");
@@ -149,6 +154,7 @@ public Field include(Collection<String> fields) {
 	 * @param field the document field name to be excluded.
 	 * @return {@code this} field projection instance.
 	 */
+	@Contract("_ -> this")
 	public Field exclude(String field) {
 
 		Assert.notNull(field, "Key must not be null");
@@ -165,6 +171,7 @@ public Field exclude(String field) {
 	 * @return {@code this} field projection instance.
 	 * @since 3.1
 	 */
+	@Contract("_ -> this")
 	public Field exclude(String... fields) {
 		return exclude(Arrays.asList(fields));
 	}
@@ -176,6 +183,7 @@ public Field exclude(String... fields) {
 	 * @return {@code this} field projection instance.
 	 * @since 4.4
 	 */
+	@Contract("_ -> this")
 	public Field exclude(Collection<String> fields) {
 
 		Assert.notNull(fields, "Keys must not be null");
@@ -191,6 +199,7 @@ public Field exclude(Collection<String> fields) {
 	 * @param size the number of elements to include.
 	 * @return {@code this} field projection instance.
 	 */
+	@Contract("_, _ -> this")
 	public Field slice(String field, int size) {
 
 		Assert.notNull(field, "Key must not be null");
@@ -209,12 +218,14 @@ public Field slice(String field, int size) {
 	 * @param size the number of elements to include.
 	 * @return {@code this} field projection instance.
 	 */
+	@Contract("_, _, _ -> this")
 	public Field slice(String field, int offset, int size) {
 
 		slices.put(field, Arrays.asList(offset, size));
 		return this;
 	}
 
+	@Contract("_, _ -> this")
 	public Field elemMatch(String field, Criteria elemMatchCriteria) {
 
 		elemMatches.put(field, elemMatchCriteria);
@@ -229,6 +240,7 @@ public Field elemMatch(String field, Criteria elemMatchCriteria) {
 	 * @param value
 	 * @return {@code this} field projection instance.
 	 */
+	@Contract("_, _ -> this")
 	public Field position(String field, int value) {
 
 		Assert.hasText(field, "DocumentField must not be null or empty");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java
index 83417c7200..19ecd94e23 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java
@@ -17,12 +17,12 @@
 
 import static org.springframework.util.ObjectUtils.*;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Box;
 import org.springframework.data.geo.Circle;
 import org.springframework.data.geo.Polygon;
 import org.springframework.data.geo.Shape;
 import org.springframework.data.mongodb.core.geo.Sphere;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
index 5757aa94a2..5ec4af3989 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
@@ -23,7 +23,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -50,8 +50,8 @@ private enum MetaKey {
 
 	private Map<String, Object> values = Collections.emptyMap();
 	private Set<CursorOption> flags = Collections.emptySet();
-	private Integer cursorBatchSize;
-	private Boolean allowDiskUse;
+	private @Nullable Integer cursorBatchSize;
+	private @Nullable Boolean allowDiskUse;
 
 	public Meta() {}
 
@@ -85,8 +85,7 @@ public boolean hasMaxTime() {
 	/**
 	 * @return {@literal null} if not set.
 	 */
-	@Nullable
-	public Long getMaxTimeMsec() {
+	public @Nullable Long getMaxTimeMsec() {
 		return getValue(MetaKey.MAX_TIME_MS.key);
 	}
 
@@ -181,8 +180,7 @@ public void setComment(String comment) {
 	 * @return {@literal null} if not set.
 	 * @since 2.1
 	 */
-	@Nullable
-	public Integer getCursorBatchSize() {
+	public @Nullable Integer getCursorBatchSize() {
 		return cursorBatchSize;
 	}
 
@@ -285,9 +283,8 @@ void setValue(String key, @Nullable Object value) {
 		this.values.put(key, value);
 	}
 
-	@Nullable
 	@SuppressWarnings("unchecked")
-	private <T> T getValue(String key) {
+	private <T> @Nullable T getValue(String key) {
 		return (T) this.values.get(key);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java
index 571bbd275c..5625de5e93 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java
@@ -20,9 +20,11 @@
 import java.math.MathContext;
 import java.math.RoundingMode;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Distance;
 import org.springframework.data.geo.Metric;
 import org.springframework.data.geo.Metrics;
+import org.springframework.util.Assert;
 
 /**
  * {@link Metric} and {@link Distance} conversions using the metric system.
@@ -151,8 +153,8 @@ static ConversionMultiplierBuilder builder() {
 	 */
 	private static class ConversionMultiplierBuilder {
 
-		private Number from;
-		private Number to;
+		private @Nullable Number from;
+		private @Nullable Number to;
 
 		ConversionMultiplierBuilder() {}
 
@@ -177,6 +179,9 @@ ConversionMultiplierBuilder to(Metric to) {
 		}
 
 		ConversionMultiplier build() {
+
+			Assert.notNull(from, "[From] must be set first");
+			Assert.notNull(to, "[To] must be set first");
 			return new ConversionMultiplier(this.from, this.to);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java
index e26a61c61e..b37c088981 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java
@@ -18,7 +18,7 @@
 import java.util.regex.Pattern;
 
 import org.bson.BsonRegularExpression;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * @author Christoph Strobl
@@ -80,8 +80,7 @@ public enum MatchMode {
 	 * @param matcherType the type of matching to perform
 	 * @return {@literal source} when {@literal source} or {@literal matcherType} is {@literal null}.
 	 */
-	@Nullable
-	public String toRegularExpression(@Nullable String source, @Nullable MatchMode matcherType) {
+	public @Nullable String toRegularExpression(@Nullable String source, @Nullable MatchMode matcherType) {
 
 		if (matcherType == null || source == null) {
 			return source;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java
index f0f3b0a4dc..6dad07b8cb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java
@@ -18,6 +18,7 @@
 import java.util.Arrays;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.geo.CustomMetric;
 import org.springframework.data.geo.Distance;
@@ -27,7 +28,7 @@
 import org.springframework.data.mongodb.core.ReadConcernAware;
 import org.springframework.data.mongodb.core.ReadPreferenceAware;
 import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -278,6 +279,7 @@ public Metric getMetric() {
 	 * @return
 	 * @since 2.2
 	 */
+	@Contract("_ -> this")
 	public NearQuery limit(long limit) {
 		this.limit = limit;
 		return this;
@@ -289,6 +291,7 @@ public NearQuery limit(long limit) {
 	 * @param skip
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery skip(long skip) {
 		this.skip = skip;
 		return this;
@@ -300,6 +303,7 @@ public NearQuery skip(long skip) {
 	 * @param pageable must not be {@literal null}
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery with(Pageable pageable) {
 
 		Assert.notNull(pageable, "Pageable must not be 'null'");
@@ -323,6 +327,7 @@ public NearQuery with(Pageable pageable) {
 	 * @param maxDistance
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery maxDistance(double maxDistance) {
 		return maxDistance(new Distance(maxDistance, getMetric()));
 	}
@@ -335,6 +340,7 @@ public NearQuery maxDistance(double maxDistance) {
 	 * @param metric must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_, _ -> this")
 	public NearQuery maxDistance(double maxDistance, Metric metric) {
 
 		Assert.notNull(metric, "Metric must not be null");
@@ -349,6 +355,7 @@ public NearQuery maxDistance(double maxDistance, Metric metric) {
 	 * @param distance must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery maxDistance(Distance distance) {
 
 		Assert.notNull(distance, "Distance must not be null");
@@ -379,6 +386,7 @@ public NearQuery maxDistance(Distance distance) {
 	 * @return
 	 * @since 1.7
 	 */
+	@Contract("_ -> this")
 	public NearQuery minDistance(double minDistance) {
 		return minDistance(new Distance(minDistance, getMetric()));
 	}
@@ -392,6 +400,7 @@ public NearQuery minDistance(double minDistance) {
 	 * @return
 	 * @since 1.7
 	 */
+	@Contract("_, _ -> this")
 	public NearQuery minDistance(double minDistance, Metric metric) {
 
 		Assert.notNull(metric, "Metric must not be null");
@@ -407,6 +416,7 @@ public NearQuery minDistance(double minDistance, Metric metric) {
 	 * @return
 	 * @since 1.7
 	 */
+	@Contract("_ -> this")
 	public NearQuery minDistance(Distance distance) {
 
 		Assert.notNull(distance, "Distance must not be null");
@@ -428,8 +438,7 @@ public NearQuery minDistance(Distance distance) {
 	 *
 	 * @return
 	 */
-	@Nullable
-	public Distance getMaxDistance() {
+	public @Nullable Distance getMaxDistance() {
 		return this.maxDistance;
 	}
 
@@ -439,8 +448,7 @@ public Distance getMaxDistance() {
 	 * @return
 	 * @since 1.7
 	 */
-	@Nullable
-	public Distance getMinDistance() {
+	public @Nullable Distance getMinDistance() {
 		return this.minDistance;
 	}
 
@@ -450,6 +458,7 @@ public Distance getMinDistance() {
 	 * @param distanceMultiplier
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery distanceMultiplier(double distanceMultiplier) {
 
 		this.metric = new CustomMetric(distanceMultiplier);
@@ -462,6 +471,7 @@ public NearQuery distanceMultiplier(double distanceMultiplier) {
 	 * @param spherical
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery spherical(boolean spherical) {
 		this.spherical = spherical;
 		return this;
@@ -482,6 +492,7 @@ public boolean isSpherical() {
 	 *
 	 * @return
 	 */
+	@Contract("-> this")
 	public NearQuery inKilometers() {
 		return adaptMetric(Metrics.KILOMETERS);
 	}
@@ -492,6 +503,7 @@ public NearQuery inKilometers() {
 	 *
 	 * @return
 	 */
+	@Contract("-> this")
 	public NearQuery inMiles() {
 		return adaptMetric(Metrics.MILES);
 	}
@@ -504,6 +516,7 @@ public NearQuery inMiles() {
 	 *          passed.
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery in(@Nullable Metric metric) {
 		return adaptMetric(metric == null ? Metrics.NEUTRAL : metric);
 	}
@@ -514,6 +527,7 @@ public NearQuery in(@Nullable Metric metric) {
 	 *
 	 * @param metric
 	 */
+	@Contract("_ -> this")
 	private NearQuery adaptMetric(Metric metric) {
 
 		if (metric != Metrics.NEUTRAL) {
@@ -530,6 +544,7 @@ private NearQuery adaptMetric(Metric metric) {
 	 * @param query must not be {@literal null}.
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public NearQuery query(Query query) {
 
 		Assert.notNull(query, "Cannot apply 'null' query on NearQuery");
@@ -546,8 +561,7 @@ public NearQuery query(Query query) {
 	/**
 	 * @return the number of elements to skip.
 	 */
-	@Nullable
-	public Long getSkip() {
+	public @Nullable Long getSkip() {
 		return skip;
 	}
 
@@ -557,8 +571,7 @@ public Long getSkip() {
 	 * @return the {@link Collation} if set. {@literal null} otherwise.
 	 * @since 2.2
 	 */
-	@Nullable
-	public Collation getCollation() {
+	public @Nullable Collation getCollation() {
 		return query != null ? query.getCollation().orElse(null) : null;
 	}
 
@@ -570,6 +583,7 @@ public Collation getCollation() {
 	 * @return this.
 	 * @since 4.1
 	 */
+	@Contract("_ -> this")
 	public NearQuery withReadConcern(ReadConcern readConcern) {
 
 		Assert.notNull(readConcern, "ReadConcern must not be null");
@@ -585,6 +599,7 @@ public NearQuery withReadConcern(ReadConcern readConcern) {
 	 * @return this.
 	 * @since 4.1
 	 */
+	@Contract("_ -> this")
 	public NearQuery withReadPreference(ReadPreference readPreference) {
 
 		Assert.notNull(readPreference, "ReadPreference must not be null");
@@ -601,9 +616,8 @@ public NearQuery withReadPreference(ReadPreference readPreference) {
 	 * @since 4.1
 	 * @see ReadConcernAware
 	 */
-	@Nullable
 	@Override
-	public ReadConcern getReadConcern() {
+	public @Nullable ReadConcern getReadConcern() {
 
 		if (query != null && query.hasReadConcern()) {
 			return query.getReadConcern();
@@ -620,9 +634,8 @@ public ReadConcern getReadConcern() {
 	 * @since 4.1
 	 * @see ReadPreferenceAware
 	 */
-	@Nullable
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 
 		if (query != null && query.hasReadPreference()) {
 			return query.getReadPreference();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
index 31c6b9069f..47ce615fe3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
@@ -15,8 +15,9 @@
  */
 package org.springframework.data.mongodb.core.query;
 
-import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
-import static org.springframework.util.ObjectUtils.*;
+import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely;
+import static org.springframework.util.ObjectUtils.nullSafeEquals;
+import static org.springframework.util.ObjectUtils.nullSafeHashCode;
 
 import java.time.Duration;
 import java.util.ArrayList;
@@ -30,6 +31,7 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.KeysetScrollPosition;
 import org.springframework.data.domain.Limit;
 import org.springframework.data.domain.OffsetScrollPosition;
@@ -42,7 +44,7 @@
 import org.springframework.data.mongodb.core.ReadPreferenceAware;
 import org.springframework.data.mongodb.core.query.Meta.CursorOption;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadConcern;
@@ -69,7 +71,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware {
 	private long skip;
 	private Limit limit = Limit.unlimited();
 
-	private KeysetScrollPosition keysetScrollPosition;
+	private @Nullable KeysetScrollPosition keysetScrollPosition;
 	private @Nullable ReadConcern readConcern;
 	private @Nullable ReadPreference readPreference;
 
@@ -123,6 +125,7 @@ public Query(CriteriaDefinition criteriaDefinition) {
 	 * @return this.
 	 * @since 1.6
 	 */
+	@Contract("_ -> this")
 	public Query addCriteria(CriteriaDefinition criteriaDefinition) {
 
 		Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null");
@@ -157,6 +160,7 @@ public Field fields() {
 	 * @param skip number of documents to skip. Use {@literal zero} or a {@literal negative} value to avoid skipping.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query skip(long skip) {
 		this.skip = skip;
 		return this;
@@ -169,6 +173,7 @@ public Query skip(long skip) {
 	 * @param limit number of documents to return. Use {@literal zero} or {@literal negative} for unlimited.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query limit(int limit) {
 		this.limit = limit > 0 ? Limit.of(limit) : Limit.unlimited();
 		return this;
@@ -181,6 +186,7 @@ public Query limit(int limit) {
 	 * @return this.
 	 * @since 4.2
 	 */
+	@Contract("_ -> this")
 	public Query limit(Limit limit) {
 
 		Assert.notNull(limit, "Limit must not be null");
@@ -202,6 +208,7 @@ public Query limit(Limit limit) {
 	 * @return this.
 	 * @see Document#parse(String)
 	 */
+	@Contract("_ -> this")
 	public Query withHint(String hint) {
 
 		Assert.hasText(hint, "Hint must not be empty or null");
@@ -216,6 +223,7 @@ public Query withHint(String hint) {
 	 * @return this.
 	 * @since 3.1
 	 */
+	@Contract("_ -> this")
 	public Query withReadConcern(ReadConcern readConcern) {
 
 		Assert.notNull(readConcern, "ReadConcern must not be null");
@@ -230,6 +238,7 @@ public Query withReadConcern(ReadConcern readConcern) {
 	 * @return this.
 	 * @since 4.1
 	 */
+	@Contract("_ -> this")
 	public Query withReadPreference(ReadPreference readPreference) {
 
 		Assert.notNull(readPreference, "ReadPreference must not be null");
@@ -243,7 +252,7 @@ public boolean hasReadConcern() {
 	}
 
 	@Override
-	public ReadConcern getReadConcern() {
+	public @Nullable ReadConcern getReadConcern() {
 		return this.readConcern;
 	}
 
@@ -253,7 +262,7 @@ public boolean hasReadPreference() {
 	}
 
 	@Override
-	public ReadPreference getReadPreference() {
+	public @Nullable ReadPreference getReadPreference() {
 
 		if (readPreference == null) {
 			return getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred() : null;
@@ -269,6 +278,7 @@ public ReadPreference getReadPreference() {
 	 * @return this.
 	 * @since 2.2
 	 */
+	@Contract("_ -> this")
 	public Query withHint(Document hint) {
 
 		Assert.notNull(hint, "Hint must not be null");
@@ -283,6 +293,7 @@ public Query withHint(Document hint) {
 	 * @param pageable must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query with(Pageable pageable) {
 
 		if (pageable.isPaged()) {
@@ -299,6 +310,7 @@ public Query with(Pageable pageable) {
 	 * @param position must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query with(ScrollPosition position) {
 
 		Assert.notNull(position, "ScrollPosition must not be null");
@@ -320,6 +332,7 @@ public Query with(ScrollPosition position) {
 	 * @param position must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query with(OffsetScrollPosition position) {
 
 		Assert.notNull(position, "ScrollPosition must not be null");
@@ -335,6 +348,7 @@ public Query with(OffsetScrollPosition position) {
 	 * @param position must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query with(KeysetScrollPosition position) {
 
 		Assert.notNull(position, "ScrollPosition must not be null");
@@ -349,8 +363,7 @@ public boolean hasKeyset() {
 		return keysetScrollPosition != null;
 	}
 
-	@Nullable
-	public KeysetScrollPosition getKeyset() {
+	public @Nullable KeysetScrollPosition getKeyset() {
 		return keysetScrollPosition;
 	}
 
@@ -360,6 +373,7 @@ public KeysetScrollPosition getKeyset() {
 	 * @param sort must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public Query with(Sort sort) {
 
 		Assert.notNull(sort, "Sort must not be null");
@@ -393,6 +407,7 @@ public Set<Class<?>> getRestrictedTypes() {
 	 * @param additionalTypes may not be {@literal null}
 	 * @return this.
 	 */
+	@Contract("_, _ -> this")
 	public Query restrict(Class<?> type, Class<?>... additionalTypes) {
 
 		Assert.notNull(type, "Type must not be null");
@@ -518,6 +533,7 @@ public String getHint() {
 	 * @see Meta#setMaxTimeMsec(long)
 	 * @since 1.6
 	 */
+	@Contract("_ -> this")
 	public Query maxTimeMsec(long maxTimeMsec) {
 
 		meta.setMaxTimeMsec(maxTimeMsec);
@@ -530,6 +546,7 @@ public Query maxTimeMsec(long maxTimeMsec) {
 	 * @see Meta#setMaxTime(Duration)
 	 * @since 2.1
 	 */
+	@Contract("_ -> this")
 	public Query maxTime(Duration timeout) {
 
 		meta.setMaxTime(timeout);
@@ -544,6 +561,7 @@ public Query maxTime(Duration timeout) {
 	 * @see Meta#setComment(String)
 	 * @since 1.6
 	 */
+	@Contract("_ -> this")
 	public Query comment(String comment) {
 
 		meta.setComment(comment);
@@ -562,6 +580,7 @@ public Query comment(String comment) {
 	 * @see Meta#setAllowDiskUse(Boolean)
 	 * @since 3.2
 	 */
+	@Contract("_ -> this")
 	public Query allowDiskUse(boolean allowDiskUse) {
 
 		meta.setAllowDiskUse(allowDiskUse);
@@ -578,6 +597,7 @@ public Query allowDiskUse(boolean allowDiskUse) {
 	 * @see Meta#setCursorBatchSize(int)
 	 * @since 2.1
 	 */
+	@Contract("_ -> this")
 	public Query cursorBatchSize(int batchSize) {
 
 		meta.setCursorBatchSize(batchSize);
@@ -589,6 +609,7 @@ public Query cursorBatchSize(int batchSize) {
 	 * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#NO_TIMEOUT
 	 * @since 1.10
 	 */
+	@Contract("-> this")
 	public Query noCursorTimeout() {
 
 		meta.addFlag(Meta.CursorOption.NO_TIMEOUT);
@@ -600,6 +621,7 @@ public Query noCursorTimeout() {
 	 * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#EXHAUST
 	 * @since 1.10
 	 */
+	@Contract("-> this")
 	public Query exhaust() {
 
 		meta.addFlag(Meta.CursorOption.EXHAUST);
@@ -613,6 +635,7 @@ public Query exhaust() {
 	 * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#SECONDARY_READS
 	 * @since 3.0.2
 	 */
+	@Contract("-> this")
 	public Query allowSecondaryReads() {
 
 		meta.addFlag(Meta.CursorOption.SECONDARY_READS);
@@ -624,6 +647,7 @@ public Query allowSecondaryReads() {
 	 * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#PARTIAL
 	 * @since 1.10
 	 */
+	@Contract("-> this")
 	public Query partialResults() {
 
 		meta.addFlag(Meta.CursorOption.PARTIAL);
@@ -655,6 +679,7 @@ public void setMeta(Meta meta) {
 	 * @return this.
 	 * @since 2.0
 	 */
+	@Contract("_ -> this")
 	public Query collation(@Nullable Collation collation) {
 
 		this.collation = Optional.ofNullable(collation);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
index 11e0f7fb24..29f8adb2c6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java
@@ -23,9 +23,9 @@
 import java.util.Map;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -110,8 +110,8 @@ private static void toFlatMap(String currentPath, Object source, Map<String, Obj
 	 * @param value
 	 * @return the serialized value or {@literal null}.
 	 */
-	@Nullable
-	public static String serializeToJsonSafely(@Nullable Object value) {
+	@Contract("null -> null; !null -> !null")
+	public static @Nullable String serializeToJsonSafely(@Nullable Object value) {
 
 		if (value == null) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java
index bd6d8c3469..cc87434178 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java
@@ -15,7 +15,8 @@
  */
 package org.springframework.data.mongodb.core.query;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.ObjectUtils;
 
 /**
@@ -61,6 +62,7 @@ public Term(String raw, @Nullable Type type) {
 	 *
 	 * @return
 	 */
+	@Contract("-> this")
 	public Term negate() {
 		this.negated = true;
 		return this;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java
index e1a7d0c4d0..5cedc2e476 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java
@@ -19,7 +19,8 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -71,7 +72,8 @@ public static TextCriteria forDefaultLanguage() {
 	 * @param language
 	 * @return
 	 */
-	public static TextCriteria forLanguage(String language) {
+	@Contract("null -> fail")
+	public static TextCriteria forLanguage(@Nullable String language) {
 
 		Assert.hasText(language, "Language must not be null or empty");
 		return new TextCriteria(language);
@@ -83,6 +85,7 @@ public static TextCriteria forLanguage(String language) {
 	 * @param words the words to match.
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria matchingAny(String... words) {
 
 		for (String word : words) {
@@ -97,6 +100,7 @@ public TextCriteria matchingAny(String... words) {
 	 *
 	 * @param term must not be {@literal null}.
 	 */
+	@Contract("_ -> this")
 	public TextCriteria matching(Term term) {
 
 		Assert.notNull(term, "Term to add must not be null");
@@ -109,6 +113,7 @@ public TextCriteria matching(Term term) {
 	 * @param term
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria matching(String term) {
 
 		if (StringUtils.hasText(term)) {
@@ -121,6 +126,7 @@ public TextCriteria matching(String term) {
 	 * @param term
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria notMatching(String term) {
 
 		if (StringUtils.hasText(term)) {
@@ -133,6 +139,7 @@ public TextCriteria notMatching(String term) {
 	 * @param words
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria notMatchingAny(String... words) {
 
 		for (String word : words) {
@@ -147,6 +154,7 @@ public TextCriteria notMatchingAny(String... words) {
 	 * @param phrase
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria notMatchingPhrase(String phrase) {
 
 		if (StringUtils.hasText(phrase)) {
@@ -161,6 +169,7 @@ public TextCriteria notMatchingPhrase(String phrase) {
 	 * @param phrase
 	 * @return
 	 */
+	@Contract("_ -> this")
 	public TextCriteria matchingPhrase(String phrase) {
 
 		if (StringUtils.hasText(phrase)) {
@@ -176,6 +185,7 @@ public TextCriteria matchingPhrase(String phrase) {
 	 * @return never {@literal null}.
 	 * @since 1.10
 	 */
+	@Contract("_ -> this")
 	public TextCriteria caseSensitive(boolean caseSensitive) {
 
 		this.caseSensitive = caseSensitive;
@@ -189,6 +199,7 @@ public TextCriteria caseSensitive(boolean caseSensitive) {
 	 * @return never {@literal null}.
 	 * @since 1.10
 	 */
+	@Contract("_ -> this")
 	public TextCriteria diacriticSensitive(boolean diacriticSensitive) {
 
 		this.diacriticSensitive = diacriticSensitive;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
index a6583299d6..a9f82a857f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
@@ -19,8 +19,9 @@
 import java.util.Map.Entry;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 
 /**
  * {@link Query} implementation to be used to for performing full text searches.
@@ -100,6 +101,7 @@ public static TextQuery queryText(TextCriteria criteria) {
 	 * @see TextQuery#includeScore()
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public TextQuery sortByScore() {
 
 		this.sortByScoreIndex = getSortObject().size();
@@ -113,6 +115,7 @@ public TextQuery sortByScore() {
 	 *
 	 * @return this.
 	 */
+	@Contract("-> this")
 	public TextQuery includeScore() {
 
 		this.includeScore = true;
@@ -125,6 +128,7 @@ public TextQuery includeScore() {
 	 * @param fieldname must not be {@literal null}.
 	 * @return this.
 	 */
+	@Contract("_ -> this")
 	public TextQuery includeScore(String fieldname) {
 
 		setScoreFieldName(fieldname);
@@ -170,9 +174,8 @@ public Document getSortObject() {
 
 			int sortByScoreIndex = this.sortByScoreIndex;
 
-			return sortByScoreIndex != 0
-				? sortByScoreAtPosition(super.getSortObject(), sortByScoreIndex)
-				: sortByScoreAtPositionZero();
+			return sortByScoreIndex != 0 ? sortByScoreAtPosition(super.getSortObject(), sortByScoreIndex)
+					: sortByScoreAtPositionZero();
 		}
 
 		return super.getSortObject();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java
index 677575c9e4..c02425214d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java
@@ -17,8 +17,8 @@
 
 import java.util.Set;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.ExampleMatcher;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
index 32d98f5804..cfb214a5a3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
@@ -27,11 +27,12 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.domain.Sort.Order;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -114,6 +115,7 @@ public static Update fromDocument(Document object, String... exclude) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.com/manual/reference/operator/update/set/">MongoDB Update operator: $set</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update set(String key, @Nullable Object value) {
 		addMultiFieldOperation("$set", key, value);
 		return this;
@@ -128,6 +130,7 @@ public Update set(String key, @Nullable Object value) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/">MongoDB Update operator:
 	 *      $setOnInsert</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update setOnInsert(String key, @Nullable Object value) {
 		addMultiFieldOperation("$setOnInsert", key, value);
 		return this;
@@ -140,6 +143,7 @@ public Update setOnInsert(String key, @Nullable Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/unset/">MongoDB Update operator: $unset</a>
 	 */
+	@Contract("_ -> this")
 	public Update unset(String key) {
 		addMultiFieldOperation("$unset", key, 1);
 		return this;
@@ -153,12 +157,14 @@ public Update unset(String key) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/inc/">MongoDB Update operator: $inc</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update inc(String key, Number inc) {
 		addMultiFieldOperation("$inc", key, inc);
 		return this;
 	}
 
 	@Override
+	@Contract("_ -> this")
 	public void inc(String key) {
 		inc(key, 1L);
 	}
@@ -171,6 +177,7 @@ public void inc(String key) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/push/">MongoDB Update operator: $push</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update push(String key, @Nullable Object value) {
 		addMultiFieldOperation("$push", key, value);
 		return this;
@@ -207,6 +214,7 @@ public PushOperatorBuilder push(String key) {
 	 * @return new instance of {@link AddToSetBuilder}.
 	 * @since 1.5
 	 */
+	@Contract("_ -> new")
 	public AddToSetBuilder addToSet(String key) {
 		return new AddToSetBuilder(key);
 	}
@@ -220,6 +228,7 @@ public AddToSetBuilder addToSet(String key) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/addToSet/">MongoDB Update operator:
 	 *      $addToSet</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update addToSet(String key, @Nullable Object value) {
 		addMultiFieldOperation("$addToSet", key, value);
 		return this;
@@ -233,6 +242,7 @@ public Update addToSet(String key, @Nullable Object value) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pop/">MongoDB Update operator: $pop</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update pop(String key, Position pos) {
 		addMultiFieldOperation("$pop", key, pos == Position.FIRST ? -1 : 1);
 		return this;
@@ -246,6 +256,7 @@ public Update pop(String key, Position pos) {
 	 * @return this.
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pull/">MongoDB Update operator: $pull</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update pull(String key, @Nullable Object value) {
 		addMultiFieldOperation("$pull", key, value);
 		return this;
@@ -260,6 +271,7 @@ public Update pull(String key, @Nullable Object value) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pullAll/">MongoDB Update operator:
 	 *      $pullAll</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update pullAll(String key, Object[] values) {
 		addMultiFieldOperation("$pullAll", key, Arrays.asList(values));
 		return this;
@@ -274,6 +286,7 @@ public Update pullAll(String key, Object[] values) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/rename/">MongoDB Update operator:
 	 *      $rename</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update rename(String oldName, String newName) {
 		addMultiFieldOperation("$rename", oldName, newName);
 		return this;
@@ -288,6 +301,7 @@ public Update rename(String oldName, String newName) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator:
 	 *      $currentDate</a>
 	 */
+	@Contract("_ -> this")
 	public Update currentDate(String key) {
 
 		addMultiFieldOperation("$currentDate", key, true);
@@ -303,6 +317,7 @@ public Update currentDate(String key) {
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator:
 	 *      $currentDate</a>
 	 */
+	@Contract("_ -> this")
 	public Update currentTimestamp(String key) {
 
 		addMultiFieldOperation("$currentDate", key, new Document("$type", "timestamp"));
@@ -318,6 +333,7 @@ public Update currentTimestamp(String key) {
 	 * @since 1.7
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/mul/">MongoDB Update operator: $mul</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update multiply(String key, Number multiplier) {
 
 		Assert.notNull(multiplier, "Multiplier must not be null");
@@ -335,6 +351,7 @@ public Update multiply(String key, Number multiplier) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/bson-type-comparison-order/">Comparison/Sort Order</a>
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/max/">MongoDB Update operator: $max</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update max(String key, Object value) {
 
 		Assert.notNull(value, "Value for max operation must not be null");
@@ -352,6 +369,7 @@ public Update max(String key, Object value) {
 	 * @see <a href="https://docs.mongodb.com/manual/reference/bson-type-comparison-order/">Comparison/Sort Order</a>
 	 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/min/">MongoDB Update operator: $min</a>
 	 */
+	@Contract("_, _ -> this")
 	public Update min(String key, Object value) {
 
 		Assert.notNull(value, "Value for min operation must not be null");
@@ -366,6 +384,7 @@ public Update min(String key, Object value) {
 	 * @return this.
 	 * @since 1.7
 	 */
+	@Contract("_ -> new")
 	public BitwiseOperatorBuilder bitwise(String key) {
 		return new BitwiseOperatorBuilder(this, key);
 	}
@@ -378,6 +397,7 @@ public BitwiseOperatorBuilder bitwise(String key) {
 	 * @return this.
 	 * @since 2.0
 	 */
+	@Contract("-> this")
 	public Update isolated() {
 
 		isolated = true;
@@ -392,6 +412,7 @@ public Update isolated() {
 	 * @return this.
 	 * @since 2.2
 	 */
+	@Contract("_ -> this")
 	public Update filterArray(CriteriaDefinition criteria) {
 
 		if (arrayFilters == Collections.EMPTY_LIST) {
@@ -411,6 +432,7 @@ public Update filterArray(CriteriaDefinition criteria) {
 	 * @return this.
 	 * @since 2.2
 	 */
+	@Contract("_, _ -> this")
 	public Update filterArray(String identifier, Object expression) {
 
 		if (arrayFilters == Collections.EMPTY_LIST) {
@@ -815,6 +837,7 @@ public Update each(Object... values) {
 		 * @return never {@literal null}.
 		 * @since 1.10
 		 */
+		@Contract("_ -> this")
 		public PushOperatorBuilder slice(int count) {
 
 			this.modifiers.addModifier(new Slice(count));
@@ -829,6 +852,7 @@ public PushOperatorBuilder slice(int count) {
 		 * @return never {@literal null}.
 		 * @since 1.10
 		 */
+		@Contract("_ -> this")
 		public PushOperatorBuilder sort(Direction direction) {
 
 			Assert.notNull(direction, "Direction must not be null");
@@ -844,6 +868,7 @@ public PushOperatorBuilder sort(Direction direction) {
 		 * @return never {@literal null}.
 		 * @since 1.10
 		 */
+		@Contract("_ -> this")
 		public PushOperatorBuilder sort(Sort sort) {
 
 			Assert.notNull(sort, "Sort must not be null");
@@ -859,6 +884,7 @@ public PushOperatorBuilder sort(Sort sort) {
 		 * @return never {@literal null}.
 		 * @since 1.7
 		 */
+		@Contract("_ -> this")
 		public PushOperatorBuilder atPosition(int position) {
 
 			this.modifiers.addModifier(new PositionModifier(position));
@@ -872,6 +898,7 @@ public PushOperatorBuilder atPosition(int position) {
 		 * @return never {@literal null}.
 		 * @since 1.7
 		 */
+		@Contract("_ -> this")
 		public PushOperatorBuilder atPosition(@Nullable Position position) {
 
 			if (position == null || Position.LAST.equals(position)) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java
index d3f67790a1..7c6889e45b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB specific query and update support.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.query;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java
index b59c20c6b6..da77a0199f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.schema;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -31,8 +31,7 @@ class DefaultMongoJsonSchema implements MongoJsonSchema {
 
 	private final JsonSchemaObject root;
 
-	@Nullable //
-	private final Document encryptionMetadata;
+	private final @Nullable Document encryptionMetadata;
 
 	DefaultMongoJsonSchema(JsonSchemaObject root) {
 		this(root, null);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java
index 29cedfd6ce..3b7c3255eb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java
@@ -23,7 +23,7 @@
 import java.util.UUID;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject;
@@ -33,7 +33,7 @@
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.TimestampJsonSchemaObject;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -97,6 +97,7 @@ public static class UntypedJsonSchemaProperty extends IdentifiableJsonSchemaProp
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) {
 			return possibleValues(Arrays.asList(possibleValues));
 		}
@@ -106,6 +107,7 @@ public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 			return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
 		}
@@ -115,6 +117,7 @@ public UntypedJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 			return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
 		}
@@ -124,6 +127,7 @@ public UntypedJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 			return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
 		}
@@ -133,6 +137,7 @@ public UntypedJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
 		}
@@ -142,6 +147,7 @@ public UntypedJsonSchemaProperty possibleValues(Collection<Object> possibleValue
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
 		}
@@ -151,6 +157,7 @@ public UntypedJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
 		}
@@ -160,6 +167,7 @@ public UntypedJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
 		}
@@ -169,6 +177,7 @@ public UntypedJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
 		}
@@ -178,6 +187,7 @@ public UntypedJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty description(String description) {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -186,6 +196,7 @@ public UntypedJsonSchemaProperty description(String description) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#generateDescription()
 		 */
+		@Contract("_ -> new")
 		public UntypedJsonSchemaProperty generatedDescription() {
 			return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
 		}
@@ -213,6 +224,7 @@ public static class StringJsonSchemaProperty extends IdentifiableJsonSchemaPrope
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#minLength(int)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty minLength(int length) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minLength(length));
 		}
@@ -222,6 +234,7 @@ public StringJsonSchemaProperty minLength(int length) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#maxLength(int)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty maxLength(int length) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxLength(length));
 		}
@@ -231,6 +244,7 @@ public StringJsonSchemaProperty maxLength(int length) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#matching(String)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty matching(String pattern) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.matching(pattern));
 		}
@@ -240,6 +254,7 @@ public StringJsonSchemaProperty matching(String pattern) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty possibleValues(String... possibleValues) {
 			return possibleValues(Arrays.asList(possibleValues));
 		}
@@ -249,6 +264,7 @@ public StringJsonSchemaProperty possibleValues(String... possibleValues) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 			return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
 		}
@@ -258,6 +274,7 @@ public StringJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 			return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
 		}
@@ -267,6 +284,7 @@ public StringJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 			return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
 		}
@@ -276,6 +294,7 @@ public StringJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
 		}
@@ -285,6 +304,7 @@ public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
 		}
@@ -294,6 +314,7 @@ public StringJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
 		}
@@ -303,6 +324,7 @@ public StringJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
 		}
@@ -312,6 +334,7 @@ public StringJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
 		}
@@ -321,6 +344,7 @@ public StringJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty description(String description) {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -329,6 +353,7 @@ public StringJsonSchemaProperty description(String description) {
 		 * @return new instance of {@link StringJsonSchemaProperty}.
 		 * @see StringJsonSchemaObject#generateDescription()
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaProperty generatedDescription() {
 			return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
 		}
@@ -355,6 +380,7 @@ public static class ObjectJsonSchemaProperty extends IdentifiableJsonSchemaPrope
 		 * @param range must not be {@literal null}.
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty propertiesCount(Range<Integer> range) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.propertiesCount(range));
 		}
@@ -364,6 +390,7 @@ public ObjectJsonSchemaProperty propertiesCount(Range<Integer> range) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#minProperties(int)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty minProperties(int count) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minProperties(count));
 		}
@@ -373,6 +400,7 @@ public ObjectJsonSchemaProperty minProperties(int count) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#maxProperties(int)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty maxProperties(int count) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxProperties(count));
 		}
@@ -382,6 +410,7 @@ public ObjectJsonSchemaProperty maxProperties(int count) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#required(String...)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty required(String... properties) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.required(properties));
 		}
@@ -391,6 +420,7 @@ public ObjectJsonSchemaProperty required(String... properties) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#additionalProperties(boolean)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty additionalProperties(boolean additionalPropertiesAllowed) {
 			return new ObjectJsonSchemaProperty(identifier,
 					jsonSchemaObjectDelegate.additionalProperties(additionalPropertiesAllowed));
@@ -401,6 +431,7 @@ public ObjectJsonSchemaProperty additionalProperties(boolean additionalPropertie
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty additionalProperties(ObjectJsonSchemaObject additionalProperties) {
 			return new ObjectJsonSchemaProperty(identifier,
 					jsonSchemaObjectDelegate.additionalProperties(additionalProperties));
@@ -411,6 +442,7 @@ public ObjectJsonSchemaProperty additionalProperties(ObjectJsonSchemaObject addi
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty properties(JsonSchemaProperty... properties) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.properties(properties));
 		}
@@ -420,6 +452,7 @@ public ObjectJsonSchemaProperty properties(JsonSchemaProperty... properties) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) {
 			return possibleValues(Arrays.asList(possibleValues));
 		}
@@ -429,6 +462,7 @@ public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 			return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
 		}
@@ -438,6 +472,7 @@ public ObjectJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 			return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
 		}
@@ -447,6 +482,7 @@ public ObjectJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 			return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
 		}
@@ -456,6 +492,7 @@ public ObjectJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
 		}
@@ -465,6 +502,7 @@ public ObjectJsonSchemaProperty possibleValues(Collection<Object> possibleValues
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
 		}
@@ -474,6 +512,7 @@ public ObjectJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
 		}
@@ -483,6 +522,7 @@ public ObjectJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
 		}
@@ -492,6 +532,7 @@ public ObjectJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
 		}
@@ -501,6 +542,7 @@ public ObjectJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty description(String description) {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -509,6 +551,7 @@ public ObjectJsonSchemaProperty description(String description) {
 		 * @return new instance of {@link ObjectJsonSchemaProperty}.
 		 * @see ObjectJsonSchemaObject#generateDescription()
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaProperty generatedDescription() {
 			return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription());
 		}
@@ -540,6 +583,7 @@ public NumericJsonSchemaProperty(String identifier, NumericJsonSchemaObject sche
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#multipleOf
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty multipleOf(Number value) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.multipleOf(value));
 		}
@@ -549,6 +593,7 @@ public NumericJsonSchemaProperty multipleOf(Number value) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#within(Range)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty within(Range<? extends Number> range) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.within(range));
 		}
@@ -558,6 +603,7 @@ public NumericJsonSchemaProperty within(Range<? extends Number> range) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#gt(Number)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty gt(Number min) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gt(min));
 		}
@@ -567,6 +613,7 @@ public NumericJsonSchemaProperty gt(Number min) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#gte(Number)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty gte(Number min) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gte(min));
 		}
@@ -576,6 +623,7 @@ public NumericJsonSchemaProperty gte(Number min) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#lt(Number)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty lt(Number max) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lt(max));
 		}
@@ -585,6 +633,7 @@ public NumericJsonSchemaProperty lt(Number max) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#lte(Number)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty lte(Number max) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lte(max));
 		}
@@ -594,6 +643,7 @@ public NumericJsonSchemaProperty lte(Number max) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty possibleValues(Number... possibleValues) {
 			return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues)));
 		}
@@ -603,6 +653,7 @@ public NumericJsonSchemaProperty possibleValues(Number... possibleValues) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 			return allOf(Arrays.asList(allOf));
 		}
@@ -612,6 +663,7 @@ public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 			return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
 		}
@@ -621,6 +673,7 @@ public NumericJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 			return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
 		}
@@ -630,6 +683,7 @@ public NumericJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValues) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
 		}
@@ -639,6 +693,7 @@ public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValue
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
 		}
@@ -648,6 +703,7 @@ public NumericJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
 		}
@@ -657,6 +713,7 @@ public NumericJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
 		}
@@ -666,6 +723,7 @@ public NumericJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
 		}
@@ -675,6 +733,7 @@ public NumericJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see NumericJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaProperty description(String description) {
 			return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -710,6 +769,7 @@ public ArrayJsonSchemaProperty(String identifier, ArrayJsonSchemaObject schemaOb
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#uniqueItems(boolean)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty uniqueItems(boolean uniqueItems) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.uniqueItems(uniqueItems));
 		}
@@ -719,6 +779,7 @@ public ArrayJsonSchemaProperty uniqueItems(boolean uniqueItems) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#range(Range)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty range(Range<Integer> range) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.range(range));
 		}
@@ -728,6 +789,7 @@ public ArrayJsonSchemaProperty range(Range<Integer> range) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#minItems(int)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty minItems(int count) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minItems(count));
 		}
@@ -737,6 +799,7 @@ public ArrayJsonSchemaProperty minItems(int count) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#maxItems(int)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty maxItems(int count) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxItems(count));
 		}
@@ -746,6 +809,7 @@ public ArrayJsonSchemaProperty maxItems(int count) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#items(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty items(JsonSchemaObject... items) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(Arrays.asList(items)));
 		}
@@ -755,6 +819,7 @@ public ArrayJsonSchemaProperty items(JsonSchemaObject... items) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#items(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty items(Collection<JsonSchemaObject> items) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(items));
 		}
@@ -764,6 +829,7 @@ public ArrayJsonSchemaProperty items(Collection<JsonSchemaObject> items) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#additionalItems(boolean)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty additionalItems(boolean additionalItemsAllowed) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.additionalItems(additionalItemsAllowed));
 		}
@@ -773,6 +839,7 @@ public ArrayJsonSchemaProperty additionalItems(boolean additionalItemsAllowed) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty possibleValues(Object... possibleValues) {
 			return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues)));
 		}
@@ -782,6 +849,7 @@ public ArrayJsonSchemaProperty possibleValues(Object... possibleValues) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 			return allOf(new LinkedHashSet<>(Arrays.asList(allOf)));
 		}
@@ -791,6 +859,7 @@ public ArrayJsonSchemaProperty allOf(JsonSchemaObject... allOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 			return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf)));
 		}
@@ -800,6 +869,7 @@ public ArrayJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 			return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf)));
 		}
@@ -809,6 +879,7 @@ public ArrayJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty possibleValues(Collection<Object> possibleValues) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues));
 		}
@@ -818,6 +889,7 @@ public ArrayJsonSchemaProperty possibleValues(Collection<Object> possibleValues)
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf));
 		}
@@ -827,6 +899,7 @@ public ArrayJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf));
 		}
@@ -836,6 +909,7 @@ public ArrayJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf));
 		}
@@ -845,6 +919,7 @@ public ArrayJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @return new instance of {@link ArrayJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch));
 		}
@@ -854,6 +929,7 @@ public ArrayJsonSchemaProperty notMatch(JsonSchemaObject notMatch) {
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see ArrayJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaProperty description(String description) {
 			return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -884,6 +960,7 @@ public static class BooleanJsonSchemaProperty extends IdentifiableJsonSchemaProp
 		 * @return new instance of {@link NumericJsonSchemaProperty}.
 		 * @see BooleanJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public BooleanJsonSchemaProperty description(String description) {
 			return new BooleanJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -914,6 +991,7 @@ public static class NullJsonSchemaProperty extends IdentifiableJsonSchemaPropert
 		 * @return new instance of {@link NullJsonSchemaProperty}.
 		 * @see NullJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> new")
 		public NullJsonSchemaProperty description(String description) {
 			return new NullJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -944,6 +1022,7 @@ public static class DateJsonSchemaProperty extends IdentifiableJsonSchemaPropert
 		 * @return new instance of {@link DateJsonSchemaProperty}.
 		 * @see DateJsonSchemaProperty#description(String)
 		 */
+		@Contract("_ -> new")
 		public DateJsonSchemaProperty description(String description) {
 			return new DateJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -974,6 +1053,7 @@ public static class TimestampJsonSchemaProperty extends IdentifiableJsonSchemaPr
 		 * @return new instance of {@link TimestampJsonSchemaProperty}.
 		 * @see TimestampJsonSchemaProperty#description(String)
 		 */
+		@Contract("_ -> new")
 		public TimestampJsonSchemaProperty description(String description) {
 			return new TimestampJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description));
 		}
@@ -1073,6 +1153,7 @@ public static EncryptedJsonSchemaProperty encrypted(JsonSchemaProperty target) {
 		 *
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("-> new")
 		public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_random() {
 			return algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Random");
 		}
@@ -1082,6 +1163,7 @@ public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_random() {
 		 *
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("-> new")
 		public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_deterministic() {
 			return algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic");
 		}
@@ -1091,6 +1173,7 @@ public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_deterministic()
 		 *
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("_ -> new")
 		public EncryptedJsonSchemaProperty algorithm(String algorithm) {
 			return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, keyIds);
 		}
@@ -1099,6 +1182,7 @@ public EncryptedJsonSchemaProperty algorithm(String algorithm) {
 		 * @param keyId must not be {@literal null}.
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("_ -> new")
 		public EncryptedJsonSchemaProperty keyId(String keyId) {
 			return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, null);
 		}
@@ -1107,6 +1191,7 @@ public EncryptedJsonSchemaProperty keyId(String keyId) {
 		 * @param keyId must not be {@literal null}.
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("_ -> new")
 		public EncryptedJsonSchemaProperty keys(UUID... keyId) {
 			return new EncryptedJsonSchemaProperty(targetProperty, algorithm, null, Arrays.asList(keyId));
 		}
@@ -1115,6 +1200,7 @@ public EncryptedJsonSchemaProperty keys(UUID... keyId) {
 		 * @param keyId must not be {@literal null}.
 		 * @return new instance of {@link EncryptedJsonSchemaProperty}.
 		 */
+		@Contract("_ -> new")
 		public EncryptedJsonSchemaProperty keys(Object... keyId) {
 			return new EncryptedJsonSchemaProperty(targetProperty, algorithm, null, Arrays.asList(keyId));
 		}
@@ -1159,8 +1245,7 @@ public Set<Type> getTypes() {
 			return targetProperty.getTypes();
 		}
 
-		@Nullable
-		private Type extractPropertyType(Document source) {
+		private @Nullable Type extractPropertyType(Document source) {
 
 			if (source.containsKey("type")) {
 				return Type.of(source.get("type", String.class));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java
index a84f361d37..24a40efa5b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java
@@ -31,6 +31,7 @@
 import org.bson.types.Code;
 import org.bson.types.Decimal128;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.DateJsonSchemaObject;
@@ -39,7 +40,6 @@
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.TimestampJsonSchemaObject;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java
index 8529951db2..9a112941be 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java
@@ -17,10 +17,10 @@
 
 import java.util.Collection;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NumericJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
 import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.*;
-import org.springframework.lang.Nullable;
 
 /**
  * A {@literal property} or {@literal patternProperty} within a {@link JsonSchemaObject} of {@code type : 'object'}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java
index f64218cc56..87c46d63dc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java
@@ -23,8 +23,9 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 /**
@@ -212,7 +213,7 @@ interface Path {
 			/**
 			 * @return the name of the currently processed element
 			 */
-			String currentElement();
+			@Nullable String currentElement();
 
 			/**
 			 * @return the path leading to the currently processed element in dot {@literal '.'} notation.
@@ -285,11 +286,11 @@ static Resolution ofValue(Path path, Object value) {
 			 * @param value the value to apply.
 			 * @return
 			 */
-			static Resolution ofValue(String key, Object value) {
+			static Resolution ofValue(@Nullable String key, Object value) {
 
 				return new Resolution() {
 					@Override
-					public String getKey() {
+					public @Nullable String getKey() {
 						return key;
 					}
 
@@ -311,8 +312,7 @@ class MongoJsonSchemaBuilder {
 
 		private ObjectJsonSchemaObject root;
 
-		@Nullable //
-		private Document encryptionMetadata;
+		private @Nullable Document encryptionMetadata;
 
 		MongoJsonSchemaBuilder() {
 			root = new ObjectJsonSchemaObject();
@@ -323,6 +323,7 @@ class MongoJsonSchemaBuilder {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#minProperties(int)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder minProperties(int count) {
 
 			root = root.minProperties(count);
@@ -334,6 +335,7 @@ public MongoJsonSchemaBuilder minProperties(int count) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#maxProperties(int)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder maxProperties(int count) {
 
 			root = root.maxProperties(count);
@@ -345,6 +347,7 @@ public MongoJsonSchemaBuilder maxProperties(int count) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#required(String...)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder required(String... properties) {
 
 			root = root.required(properties);
@@ -356,6 +359,7 @@ public MongoJsonSchemaBuilder required(String... properties) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#additionalProperties(boolean)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesAllowed) {
 
 			root = root.additionalProperties(additionalPropertiesAllowed);
@@ -367,6 +371,7 @@ public MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesA
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema) {
 
 			root = root.additionalProperties(schema);
@@ -378,6 +383,7 @@ public MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) {
 
 			root = root.properties(properties);
@@ -389,6 +395,7 @@ public MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#patternProperties(JsonSchemaProperty...)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties) {
 
 			root = root.patternProperties(properties);
@@ -400,6 +407,7 @@ public MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#property(JsonSchemaProperty)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder property(JsonSchemaProperty property) {
 
 			root = root.property(property);
@@ -411,6 +419,7 @@ public MongoJsonSchemaBuilder property(JsonSchemaProperty property) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see ObjectJsonSchemaObject#possibleValues(Collection)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) {
 
 			root = root.possibleValues(possibleValues);
@@ -422,6 +431,7 @@ public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see UntypedJsonSchemaObject#allOf(Collection)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) {
 
 			root = root.allOf(allOf);
@@ -433,6 +443,7 @@ public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see UntypedJsonSchemaObject#anyOf(Collection)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) {
 
 			root = root.anyOf(anyOf);
@@ -444,6 +455,7 @@ public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see UntypedJsonSchemaObject#oneOf(Collection)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) {
 
 			root = root.oneOf(oneOf);
@@ -455,6 +467,7 @@ public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see UntypedJsonSchemaObject#notMatch(JsonSchemaObject)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) {
 
 			root = root.notMatch(notMatch);
@@ -466,6 +479,7 @@ public MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) {
 		 * @return {@code this} {@link MongoJsonSchemaBuilder}.
 		 * @see UntypedJsonSchemaObject#description(String)
 		 */
+		@Contract("_ -> this")
 		public MongoJsonSchemaBuilder description(String description) {
 
 			root = root.description(description);
@@ -487,6 +501,7 @@ public void encryptionMetadata(@Nullable Document encryptionMetadata) {
 		 *
 		 * @return new instance of {@link MongoJsonSchema}.
 		 */
+		@Contract("-> new")
 		public MongoJsonSchema build() {
 			return new DefaultMongoJsonSchema(root, encryptionMetadata);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java
index 95f116619f..87bdd8c618 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java
@@ -22,10 +22,10 @@
 import java.util.function.BiFunction;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction.Path;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction.Resolution;
-import org.springframework.lang.Nullable;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -119,8 +119,7 @@ private static String getTypeKeyToUse(String key, Document source) {
 		return key;
 	}
 
-	@Nullable
-	private static Object getUnifiedExistingType(String key, Document source) {
+	private static @Nullable Object getUnifiedExistingType(String key, Document source) {
 		return source.get(getTypeKeyToUse(key, source));
 	}
 
@@ -155,7 +154,7 @@ public SimplePath append(String next) {
 		}
 
 		@Override
-		public String currentElement() {
+		public @Nullable String currentElement() {
 			return CollectionUtils.lastElement(path);
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java
index abf8b0b8a2..7b299fd4d2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java
@@ -27,9 +27,10 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.domain.Range.Bound;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ObjectUtils;
@@ -100,6 +101,7 @@ public Set<Type> getTypes() {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject description(String description) {
 		return new TypedJsonSchemaObject(types, description, generateDescription, restrictions);
 	}
@@ -110,6 +112,7 @@ public TypedJsonSchemaObject description(String description) {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("-> new")
 	public TypedJsonSchemaObject generatedDescription() {
 		return new TypedJsonSchemaObject(types, description, true, restrictions);
 	}
@@ -121,6 +124,7 @@ public TypedJsonSchemaObject generatedDescription() {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 		return new TypedJsonSchemaObject(types, description, generateDescription,
 				restrictions.possibleValues(possibleValues));
@@ -133,6 +137,7 @@ public TypedJsonSchemaObject possibleValues(Collection<? extends Object> possibl
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 		return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.allOf(allOf));
 	}
@@ -144,6 +149,7 @@ public TypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 		return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.anyOf(anyOf));
 	}
@@ -155,6 +161,7 @@ public TypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 		return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.oneOf(oneOf));
 	}
@@ -166,6 +173,7 @@ public TypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
 	@Override
+	@Contract("_ -> new")
 	public TypedJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 		return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.notMatch(notMatch));
 	}
@@ -210,8 +218,7 @@ private Optional<String> getOrCreateDescription() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	protected String generateDescription() {
+	protected @Nullable String generateDescription() {
 		return null;
 	}
 
@@ -264,6 +271,7 @@ public ObjectJsonSchemaObject propertiesCount(Range<Integer> range) {
 		 * @param count the allowed minimal number of properties.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject minProperties(int count) {
 
 			Bound<Integer> upper = this.propertiesCount != null ? this.propertiesCount.getUpperBound() : Bound.unbounded();
@@ -276,6 +284,7 @@ public ObjectJsonSchemaObject minProperties(int count) {
 		 * @param count the allowed maximum number of properties.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject maxProperties(int count) {
 
 			Bound<Integer> lower = this.propertiesCount != null ? this.propertiesCount.getLowerBound() : Bound.unbounded();
@@ -288,6 +297,7 @@ public ObjectJsonSchemaObject maxProperties(int count) {
 		 * @param properties the names of required properties.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject required(String... properties) {
 
 			ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -305,6 +315,7 @@ public ObjectJsonSchemaObject required(String... properties) {
 		 * @param additionalPropertiesAllowed
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject additionalProperties(boolean additionalPropertiesAllowed) {
 
 			ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -319,6 +330,7 @@ public ObjectJsonSchemaObject additionalProperties(boolean additionalPropertiesA
 		 * @param schema must not be {@literal null}.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject additionalProperties(ObjectJsonSchemaObject schema) {
 
 			ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -332,6 +344,7 @@ public ObjectJsonSchemaObject additionalProperties(ObjectJsonSchemaObject schema
 		 * @param properties must not be {@literal null}.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject properties(JsonSchemaProperty... properties) {
 
 			ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -349,6 +362,7 @@ public ObjectJsonSchemaObject properties(JsonSchemaProperty... properties) {
 		 * @param regularExpressions must not be {@literal null}.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject patternProperties(JsonSchemaProperty... regularExpressions) {
 
 			ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -365,41 +379,49 @@ public ObjectJsonSchemaObject patternProperties(JsonSchemaProperty... regularExp
 		 * @param property must not be {@literal null}.
 		 * @return new instance of {@link ObjectJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject property(JsonSchemaProperty property) {
 			return properties(property);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return newInstance(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return newInstance(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return newInstance(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return newInstance(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject description(String description) {
 			return newInstance(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ObjectJsonSchemaObject generatedDescription() {
 			return newInstance(description, true, restrictions);
 		}
@@ -545,6 +567,7 @@ private NumericJsonSchemaObject(Set<Type> types, @Nullable String description, b
 		 * @param value must not be {@literal null}.
 		 * @return must not be {@literal null}.
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject multipleOf(Number value) {
 
 			Assert.notNull(value, "Value must not be null");
@@ -561,6 +584,7 @@ public NumericJsonSchemaObject multipleOf(Number value) {
 		 * @param range must not be {@literal null}.
 		 * @return new instance of {@link NumericJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject within(Range<? extends Number> range) {
 
 			Assert.notNull(range, "Range must not be null");
@@ -578,6 +602,7 @@ public NumericJsonSchemaObject within(Range<? extends Number> range) {
 		 * @return new instance of {@link NumericJsonSchemaObject}.
 		 */
 		@SuppressWarnings("unchecked")
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject gt(Number min) {
 
 			Assert.notNull(min, "Min must not be null");
@@ -593,6 +618,7 @@ public NumericJsonSchemaObject gt(Number min) {
 		 * @return new instance of {@link NumericJsonSchemaObject}.
 		 */
 		@SuppressWarnings("unchecked")
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject gte(Number min) {
 
 			Assert.notNull(min, "Min must not be null");
@@ -608,6 +634,7 @@ public NumericJsonSchemaObject gte(Number min) {
 		 * @return new instance of {@link NumericJsonSchemaObject}.
 		 */
 		@SuppressWarnings("unchecked")
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject lt(Number max) {
 
 			Assert.notNull(max, "Max must not be null");
@@ -623,6 +650,7 @@ public NumericJsonSchemaObject lt(Number max) {
 		 * @return new instance of {@link NumericJsonSchemaObject}.
 		 */
 		@SuppressWarnings("unchecked")
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject lte(Number max) {
 
 			Assert.notNull(max, "Max must not be null");
@@ -632,36 +660,43 @@ public NumericJsonSchemaObject lte(Number max) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return newInstance(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return newInstance(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return newInstance(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return newInstance(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject description(String description) {
 			return newInstance(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NumericJsonSchemaObject generatedDescription() {
 			return newInstance(description, true, restrictions);
 		}
@@ -785,6 +820,7 @@ private StringJsonSchemaObject(@Nullable String description, boolean generateDes
 		 * @param range must not be {@literal null}.
 		 * @return new instance of {@link StringJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaObject length(Range<Integer> range) {
 
 			Assert.notNull(range, "Range must not be null");
@@ -801,6 +837,7 @@ public StringJsonSchemaObject length(Range<Integer> range) {
 		 * @param length
 		 * @return new instance of {@link StringJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaObject minLength(int length) {
 
 			Bound<Integer> upper = this.length != null ? this.length.getUpperBound() : Bound.unbounded();
@@ -813,6 +850,7 @@ public StringJsonSchemaObject minLength(int length) {
 		 * @param length
 		 * @return new instance of {@link StringJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaObject maxLength(int length) {
 
 			Bound<Integer> lower = this.length != null ? this.length.getLowerBound() : Bound.unbounded();
@@ -825,6 +863,7 @@ public StringJsonSchemaObject maxLength(int length) {
 		 * @param pattern must not be {@literal null}.
 		 * @return new instance of {@link StringJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public StringJsonSchemaObject matching(String pattern) {
 
 			Assert.notNull(pattern, "Pattern must not be null");
@@ -836,36 +875,43 @@ public StringJsonSchemaObject matching(String pattern) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return newInstance(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return newInstance(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return newInstance(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return newInstance(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public StringJsonSchemaObject description(String description) {
 			return newInstance(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("-> new")
 		public StringJsonSchemaObject generatedDescription() {
 			return newInstance(description, true, restrictions);
 		}
@@ -946,6 +992,7 @@ private ArrayJsonSchemaObject(@Nullable String description, boolean generateDesc
 		 * @param uniqueItems
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject uniqueItems(boolean uniqueItems) {
 
 			ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -961,6 +1008,7 @@ public ArrayJsonSchemaObject uniqueItems(boolean uniqueItems) {
 		 * @param range must not be {@literal null}. Consider {@link Range#unbounded()} instead.
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject range(Range<Integer> range) {
 
 			ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -975,6 +1023,7 @@ public ArrayJsonSchemaObject range(Range<Integer> range) {
 		 * @param count the allowed minimal number of array items.
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject minItems(int count) {
 
 			Bound<Integer> upper = this.range != null ? this.range.getUpperBound() : Bound.unbounded();
@@ -987,6 +1036,7 @@ public ArrayJsonSchemaObject minItems(int count) {
 		 * @param count the allowed maximal number of array items.
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject maxItems(int count) {
 
 			Bound<Integer> lower = this.range != null ? this.range.getLowerBound() : Bound.unbounded();
@@ -999,6 +1049,7 @@ public ArrayJsonSchemaObject maxItems(int count) {
 		 * @param items the allowed items in the array.
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject items(Collection<JsonSchemaObject> items) {
 
 			ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -1013,6 +1064,7 @@ public ArrayJsonSchemaObject items(Collection<JsonSchemaObject> items) {
 		 * @param additionalItemsAllowed {@literal true} to allow additional items in the array, {@literal false} otherwise.
 		 * @return new instance of {@link ArrayJsonSchemaObject}.
 		 */
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject additionalItems(boolean additionalItemsAllowed) {
 
 			ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions);
@@ -1022,36 +1074,43 @@ public ArrayJsonSchemaObject additionalItems(boolean additionalItemsAllowed) {
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return newInstance(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return newInstance(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return newInstance(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return newInstance(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject description(String description) {
 			return newInstance(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public ArrayJsonSchemaObject generatedDescription() {
 			return newInstance(description, true, restrictions);
 		}
@@ -1147,41 +1206,49 @@ private BooleanJsonSchemaObject(@Nullable String description, boolean generateDe
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject description(String description) {
 			return new BooleanJsonSchemaObject(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public BooleanJsonSchemaObject generatedDescription() {
 			return new BooleanJsonSchemaObject(description, true, restrictions);
 		}
 
 		@Override
+		@Contract("-> new")
 		protected String generateDescription() {
 			return "Must be a boolean";
 		}
@@ -1208,36 +1275,43 @@ private NullJsonSchemaObject(@Nullable String description, boolean generateDescr
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public NullJsonSchemaObject description(String description) {
 			return new NullJsonSchemaObject(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("-> new")
 		public NullJsonSchemaObject generatedDescription() {
 			return new NullJsonSchemaObject(description, true, restrictions);
 		}
@@ -1268,36 +1342,43 @@ private DateJsonSchemaObject(@Nullable String description, boolean generateDescr
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public DateJsonSchemaObject description(String description) {
 			return new DateJsonSchemaObject(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("-> new")
 		public DateJsonSchemaObject generatedDescription() {
 			return new DateJsonSchemaObject(description, true, restrictions);
 		}
@@ -1328,37 +1409,44 @@ private TimestampJsonSchemaObject(@Nullable String description, boolean generate
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 			return new TimestampJsonSchemaObject(description, generateDescription,
 					restrictions.possibleValues(possibleValues));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 			return new TimestampJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 			return new TimestampJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 			return new TimestampJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 			return new TimestampJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch));
 		}
 
 		@Override
+		@Contract("_ -> new")
 		public TimestampJsonSchemaObject description(String description) {
 			return new TimestampJsonSchemaObject(description, generateDescription, restrictions);
 		}
 
 		@Override
+		@Contract("-> new")
 		public TimestampJsonSchemaObject generatedDescription() {
 			return new TimestampJsonSchemaObject(description, true, restrictions);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java
index 54ca29e0e3..d13f8d7985 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java
@@ -23,7 +23,8 @@
 import java.util.stream.Collectors;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -69,6 +70,7 @@ public Set<Type> getTypes() {
 	 * @param description must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject description(String description) {
 		return new UntypedJsonSchemaObject(restrictions, description, generateDescription);
 	}
@@ -78,6 +80,7 @@ public UntypedJsonSchemaObject description(String description) {
 	 *
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("-> new")
 	public UntypedJsonSchemaObject generatedDescription() {
 		return new UntypedJsonSchemaObject(restrictions, description, true);
 	}
@@ -88,6 +91,7 @@ public UntypedJsonSchemaObject generatedDescription() {
 	 * @param possibleValues must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) {
 		return new UntypedJsonSchemaObject(restrictions.possibleValues(possibleValues), description, generateDescription);
 	}
@@ -98,6 +102,7 @@ public UntypedJsonSchemaObject possibleValues(Collection<? extends Object> possi
 	 * @param allOf must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 		return new UntypedJsonSchemaObject(restrictions.allOf(allOf), description, generateDescription);
 	}
@@ -108,6 +113,7 @@ public UntypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) {
 	 * @param anyOf must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 		return new UntypedJsonSchemaObject(restrictions.anyOf(anyOf), description, generateDescription);
 	}
@@ -118,6 +124,7 @@ public UntypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) {
 	 * @param oneOf must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 		return new UntypedJsonSchemaObject(restrictions.oneOf(oneOf), description, generateDescription);
 	}
@@ -128,6 +135,7 @@ public UntypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) {
 	 * @param notMatch must not be {@literal null}.
 	 * @return new instance of {@link TypedJsonSchemaObject}.
 	 */
+	@Contract("_ -> new")
 	public UntypedJsonSchemaObject notMatch(JsonSchemaObject notMatch) {
 		return new UntypedJsonSchemaObject(restrictions.notMatch(notMatch), description, generateDescription);
 	}
@@ -163,8 +171,7 @@ private Optional<String> getOrCreateDescription() {
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	protected String generateDescription() {
+	protected @Nullable String generateDescription() {
 		return null;
 	}
 
@@ -177,14 +184,14 @@ protected String generateDescription() {
 	 */
 	static class Restrictions {
 
-		private final Collection<? extends Object> possibleValues;
+		private final Collection<?> possibleValues;
 		private final Collection<JsonSchemaObject> allOf;
 		private final Collection<JsonSchemaObject> anyOf;
 		private final Collection<JsonSchemaObject> oneOf;
 		private final @Nullable JsonSchemaObject notMatch;
 
-		Restrictions(Collection<? extends Object> possibleValues, Collection<JsonSchemaObject> allOf,
-				Collection<JsonSchemaObject> anyOf, Collection<JsonSchemaObject> oneOf, JsonSchemaObject notMatch) {
+		Restrictions(Collection<?> possibleValues, Collection<JsonSchemaObject> allOf,
+				Collection<JsonSchemaObject> anyOf, Collection<JsonSchemaObject> oneOf, @Nullable JsonSchemaObject notMatch) {
 
 			this.possibleValues = possibleValues;
 			this.allOf = allOf;
@@ -206,7 +213,8 @@ static Restrictions empty() {
 		 * @param possibleValues must not be {@literal null}.
 		 * @return
 		 */
-		Restrictions possibleValues(Collection<? extends Object> possibleValues) {
+		@Contract("_ -> new")
+		Restrictions possibleValues(Collection<?> possibleValues) {
 
 			Assert.notNull(possibleValues, "PossibleValues must not be null");
 			return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch);
@@ -216,6 +224,7 @@ Restrictions possibleValues(Collection<? extends Object> possibleValues) {
 		 * @param allOf must not be {@literal null}.
 		 * @return
 		 */
+		@Contract("_ -> new")
 		Restrictions allOf(Collection<JsonSchemaObject> allOf) {
 
 			Assert.notNull(allOf, "AllOf must not be null");
@@ -226,6 +235,7 @@ Restrictions allOf(Collection<JsonSchemaObject> allOf) {
 		 * @param anyOf must not be {@literal null}.
 		 * @return
 		 */
+		@Contract("_ -> new")
 		Restrictions anyOf(Collection<JsonSchemaObject> anyOf) {
 
 			Assert.notNull(anyOf, "AnyOf must not be null");
@@ -236,6 +246,7 @@ Restrictions anyOf(Collection<JsonSchemaObject> anyOf) {
 		 * @param oneOf must not be {@literal null}.
 		 * @return
 		 */
+		@Contract("_ -> new")
 		Restrictions oneOf(Collection<JsonSchemaObject> oneOf) {
 
 			Assert.notNull(oneOf, "OneOf must not be null");
@@ -246,6 +257,7 @@ Restrictions oneOf(Collection<JsonSchemaObject> oneOf) {
 		 * @param notMatch must not be {@literal null}.
 		 * @return
 		 */
+		@Contract("_ -> new")
 		Restrictions notMatch(JsonSchemaObject notMatch) {
 
 			Assert.notNull(notMatch, "NotMatch must not be null");
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java
index 380d92af09..cdc583e038 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB-specific JSON schema implementation classes.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 @org.springframework.lang.NonNullFields
 package org.springframework.data.mongodb.core.schema;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java
index 34eb8ea890..976b238fbd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java
@@ -3,6 +3,6 @@
  *
  * @since 1.7
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.script;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java
index b4550ee8de..a5b4a2aabf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java
@@ -18,13 +18,13 @@
 import java.util.Collections;
 import java.util.Iterator;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.expression.spel.ExpressionState;
 import org.springframework.expression.spel.SpelNode;
 import org.springframework.expression.spel.ast.Literal;
 import org.springframework.expression.spel.ast.MethodReference;
 import org.springframework.expression.spel.ast.Operator;
 import org.springframework.expression.spel.ast.OperatorNot;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -150,8 +150,7 @@ public boolean isLiteral() {
 	 *
 	 * @return
 	 */
-	@Nullable
-	public Object getValue() {
+	public @Nullable Object getValue() {
 		return node.getValue(state);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java
index 8869f51e09..89edd4eab2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java
@@ -18,7 +18,7 @@
 import java.util.List;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -67,8 +67,7 @@ public T getCurrentNode() {
 	 *
 	 * @return
 	 */
-	@Nullable
-	public ExpressionNode getParentNode() {
+	public @Nullable ExpressionNode getParentNode() {
 		return parentNode;
 	}
 
@@ -81,8 +80,7 @@ public ExpressionNode getParentNode() {
 	 * @see #addToPreviousOrReturn(Object)
 	 * @return
 	 */
-	@Nullable
-	public Document getPreviousOperationObject() {
+	public @Nullable Document getPreviousOperationObject() {
 		return previousOperationObject;
 	}
 
@@ -110,7 +108,7 @@ public boolean parentIsSameOperation() {
 	 * @param value
 	 * @return
 	 */
-	public Document addToPreviousOperation(Object value) {
+	public Document addToPreviousOperation(@Nullable Object value) {
 
 		Assert.state(previousOperationObject != null, "No previous operation available");
 
@@ -124,11 +122,14 @@ public Document addToPreviousOperation(Object value) {
 	 * @param value
 	 * @return
 	 */
-	public Object addToPreviousOrReturn(Object value) {
+	public @Nullable Object addToPreviousOrReturn(@Nullable Object value) {
 		return hasPreviousOperation() ? addToPreviousOperation(value) : value;
 	}
 
+	@SuppressWarnings("unchecked")
 	private List<Object> extractArgumentListFrom(Document context) {
-		return (List<Object>) context.get(context.keySet().iterator().next());
+
+		Object o = context.get(context.keySet().iterator().next());
+		return o instanceof List<?> l ? (List<Object>) l : List.of();
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java
index 512f753042..da5748f523 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java
@@ -15,6 +15,8 @@
  */
 package org.springframework.data.mongodb.core.spel;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * SPI interface to implement components that can transform an {@link ExpressionTransformationContextSupport} into an
  * object.
@@ -29,5 +31,5 @@ public interface ExpressionTransformer<T extends ExpressionTransformationContext
 	 * @param context will never be {@literal null}.
 	 * @return
 	 */
-	Object transform(T context);
+	@Nullable Object transform(T context);
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java
index 030ef0d055..b276831860 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java
@@ -17,6 +17,7 @@
 
 import java.util.Set;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.expression.spel.ExpressionState;
 import org.springframework.expression.spel.ast.BooleanLiteral;
 import org.springframework.expression.spel.ast.FloatLiteral;
@@ -26,7 +27,6 @@
 import org.springframework.expression.spel.ast.NullLiteral;
 import org.springframework.expression.spel.ast.RealLiteral;
 import org.springframework.expression.spel.ast.StringLiteral;
-import org.springframework.lang.Nullable;
 
 /**
  * A node representing a literal in an expression.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 5f1b0c4309..db2801e649 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -21,9 +21,9 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.expression.spel.ExpressionState;
 import org.springframework.expression.spel.ast.MethodReference;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
@@ -272,8 +272,7 @@ public class MethodReferenceNode extends ExpressionNode {
 	 * @return can be {@literal null}.
 	 * @since 1.10
 	 */
-	@Nullable
-	public AggregationMethodReference getMethodReference() {
+	public @Nullable AggregationMethodReference getMethodReference() {
 
 		String name = getName();
 		String methodName = name.substring(0, name.indexOf('('));
@@ -288,7 +287,7 @@ public static final class AggregationMethodReference {
 
 		private final @Nullable String mongoOperator;
 		private final @Nullable ArgumentType argumentType;
-		private final @Nullable String[] argumentMap;
+		private final String @Nullable[] argumentMap;
 
 		/**
 		 * Creates new {@link AggregationMethodReference}.
@@ -298,7 +297,7 @@ public static final class AggregationMethodReference {
 		 * @param argumentMap can be {@literal null}.
 		 */
 		private AggregationMethodReference(@Nullable String mongoOperator, @Nullable ArgumentType argumentType,
-				@Nullable String[] argumentMap) {
+				String @Nullable[] argumentMap) {
 
 			this.mongoOperator = mongoOperator;
 			this.argumentType = argumentType;
@@ -310,8 +309,7 @@ private AggregationMethodReference(@Nullable String mongoOperator, @Nullable Arg
 		 *
 		 * @return can be {@literal null}.
 		 */
-		@Nullable
-		public String getMongoOperator() {
+		public @Nullable String getMongoOperator() {
 			return this.mongoOperator;
 		}
 
@@ -320,8 +318,7 @@ public String getMongoOperator() {
 		 *
 		 * @return never {@literal null}.
 		 */
-		@Nullable
-		public ArgumentType getArgumentType() {
+		public @Nullable ArgumentType getArgumentType() {
 			return this.argumentType;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java
index fbfa2ae78b..627cb8a04f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java
@@ -3,6 +3,6 @@
  * 
  * @since 1.4
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.core.spel;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java
index 779ed4ec9f..d739994e89 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java
@@ -16,10 +16,10 @@
 package org.springframework.data.mongodb.core.validation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.CriteriaDefinition;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java
index 5e27b99ad6..9adf5fcf79 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java
@@ -16,8 +16,8 @@
 package org.springframework.data.mongodb.core.validation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java
index 61ef8c5b4f..b04e65b1db 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java
@@ -16,9 +16,9 @@
 package org.springframework.data.mongodb.core.validation;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.SerializationUtils;
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java
index 002a4ee1fb..83fe5719a3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB schema validation specifics.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 @org.springframework.lang.NonNullFields
 package org.springframework.data.mongodb.core.validation;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java
index 54010a7c65..e0aa5bb2d5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.gridfs;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.lang.Nullable;
 
 /**
  * GridFs-specific helper class to define {@link Criteria}s.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java
index f73c0c943f..99807e63a2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java
@@ -16,9 +16,10 @@
 package org.springframework.data.mongodb.gridfs;
 
 import org.bson.Document;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.client.gridfs.model.GridFSFile;
+import org.springframework.lang.Contract;
 
 /**
  * A common interface when dealing with GridFs items using Spring Data.
@@ -110,6 +111,7 @@ public static Options from(@Nullable GridFSFile gridFSFile) {
 		 * @param contentType must not be {@literal null}.
 		 * @return new instance of {@link Options}.
 		 */
+		@Contract("_ -> new")
 		public Options contentType(String contentType) {
 
 			Options target = new Options(new Document(metadata), chunkSize);
@@ -121,6 +123,7 @@ public Options contentType(String contentType) {
 		 * @param metadata
 		 * @return new instance of {@link Options}.
 		 */
+		@Contract("_ -> new")
 		public Options metadata(Document metadata) {
 			return new Options(metadata, chunkSize);
 		}
@@ -129,6 +132,7 @@ public Options metadata(Document metadata) {
 		 * @param chunkSize the file chunk size to use.
 		 * @return new instance of {@link Options}.
 		 */
+		@Contract("_ -> new")
 		public Options chunkSize(int chunkSize) {
 			return new Options(metadata, chunkSize);
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java
index bf5a1d86e3..4878b431f4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java
@@ -19,11 +19,11 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.gridfs.GridFsUpload.GridFsUploadBuilder;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
@@ -181,8 +181,7 @@ default ObjectId store(InputStream content, @Nullable String filename, @Nullable
 	 * @param query must not be {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	com.mongodb.client.gridfs.model.GridFSFile findOne(Query query);
+	com.mongodb.client.gridfs.model.@Nullable GridFSFile findOne(Query query);
 
 	/**
 	 * Deletes all files matching the given {@link Query}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java
index b3d3771f3c..9a5621dcbc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java
@@ -18,9 +18,9 @@
 import java.util.Optional;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java
index 0873432977..db6ce9833d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java
@@ -21,10 +21,10 @@
 import java.io.InputStream;
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.core.io.Resource;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.MongoGridFSException;
@@ -105,6 +105,7 @@ public InputStream getInputStream() throws IOException, IllegalStateException {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public long contentLength() throws IOException {
 
 		verifyExists();
@@ -122,6 +123,7 @@ public boolean exists() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public long lastModified() throws IOException {
 
 		verifyExists();
@@ -139,6 +141,7 @@ public String getDescription() {
 	 * @return never {@literal null}.
 	 * @throws IllegalStateException if the file does not {@link #exists()}.
 	 */
+	@SuppressWarnings("NullAway")
 	public Object getId() {
 
 		Assert.state(exists(), () -> String.format("%s does not exist.", getDescription()));
@@ -147,7 +150,8 @@ public Object getId() {
 	}
 
 	@Override
-	public Object getFileId() {
+	@SuppressWarnings("NullAway")
+	public @Nullable Object getFileId() {
 
 		Assert.state(exists(), () -> String.format("%s does not exist.", getDescription()));
 		return BsonUtils.toJavaType(getGridFSFile().getId());
@@ -157,8 +161,7 @@ public Object getFileId() {
 	 * @return the underlying {@link GridFSFile}. Can be {@literal null} if absent.
 	 * @since 2.2
 	 */
-	@Nullable
-	public GridFSFile getGridFSFile() {
+	public @Nullable GridFSFile getGridFSFile() {
 		return this.file;
 	}
 
@@ -170,6 +173,7 @@ public GridFSFile getGridFSFile() {
 	 *           provided via {@link GridFSFile}.
 	 * @throws IllegalStateException if the file does not {@link #exists()}.
 	 */
+	@SuppressWarnings("NullAway")
 	public String getContentType() {
 
 		Assert.state(exists(), () -> String.format("%s does not exist.", getDescription()));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java
index 8187c7dbc3..722a57edc1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java
@@ -26,13 +26,13 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -167,7 +167,7 @@ public void delete(Query query) {
 	}
 
 	@Override
-	public ClassLoader getClassLoader() {
+	public @Nullable ClassLoader getClassLoader() {
 		return null;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java
index 9f8d9a47d2..6f2b9ed85b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java
@@ -20,9 +20,9 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.client.gridfs.model.GridFSFile;
@@ -61,8 +61,7 @@ private GridFsUpload(@Nullable ID id, Lazy<InputStream> dataStream, String filen
 	 * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId()
 	 */
 	@Override
-	@Nullable
-	public ID getFileId() {
+	public @Nullable ID getFileId() {
 		return id;
 	}
 
@@ -72,6 +71,7 @@ public String getFilename() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	public InputStream getContent() {
 		return dataStream.orElse(InputStream.nullInputStream());
 	}
@@ -98,9 +98,9 @@ public static GridFsUploadBuilder<ObjectId> fromStream(InputStream stream) {
 	 */
 	public static class GridFsUploadBuilder<T> {
 
-		private Object id;
-		private Lazy<InputStream> dataStream;
-		private String filename;
+		private @Nullable Object id;
+		private @Nullable Lazy<InputStream> dataStream;
+		private @Nullable String filename;
 		private Options options = Options.none();
 
 		private GridFsUploadBuilder() {}
@@ -124,6 +124,7 @@ public GridFsUploadBuilder<T> content(InputStream stream) {
 		 * @param stream the upload content.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> content(Supplier<InputStream> stream) {
 
 			Assert.notNull(stream, "InputStream Supplier must not be null");
@@ -139,6 +140,8 @@ public GridFsUploadBuilder<T> content(Supplier<InputStream> stream) {
 		 * @param <T1>
 		 * @return this.
 		 */
+		@SuppressWarnings("unchecked")
+		@Contract("_ -> this")
 		public <T1> GridFsUploadBuilder<T1> id(T1 id) {
 
 			this.id = id;
@@ -151,6 +154,7 @@ public <T1> GridFsUploadBuilder<T1> id(T1 id) {
 		 * @param filename the filename to use.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> filename(String filename) {
 
 			this.filename = filename;
@@ -163,6 +167,7 @@ public GridFsUploadBuilder<T> filename(String filename) {
 		 * @param options must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> options(Options options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -177,6 +182,7 @@ public GridFsUploadBuilder<T> options(Options options) {
 		 * @param metadata must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> metadata(Document metadata) {
 
 			this.options = this.options.metadata(metadata);
@@ -189,6 +195,7 @@ public GridFsUploadBuilder<T> metadata(Document metadata) {
 		 * @param chunkSize use negative number for default.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> chunkSize(int chunkSize) {
 
 			this.options = this.options.chunkSize(chunkSize);
@@ -201,6 +208,7 @@ public GridFsUploadBuilder<T> chunkSize(int chunkSize) {
 		 * @param gridFSFile must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) {
 
 			Assert.notNull(gridFSFile, "GridFSFile must not be null");
@@ -219,13 +227,20 @@ public GridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) {
 		 * @param contentType must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public GridFsUploadBuilder<T> contentType(String contentType) {
 
 			this.options = this.options.contentType(contentType);
 			return this;
 		}
 
+		@Contract("-> new")
 		public GridFsUpload<T> build() {
+
+			Assert.notNull(dataStream, "DataStream must be set first");
+			Assert.notNull(filename, "Filename must be set first");
+			Assert.notNull(options, "Options must be set first");
+
 			return new GridFsUpload(id, dataStream, filename, options);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java
index 9ee47e0bb9..f8a6bd804f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java
@@ -20,12 +20,12 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.gridfs.ReactiveGridFsUpload.ReactiveGridFsUploadBuilder;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java
index aec7cadef1..e889ec7183 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java
@@ -22,6 +22,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.bson.BsonValue;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.buffer.DataBuffer;
@@ -29,7 +30,6 @@
 import org.springframework.core.io.buffer.DataBufferUtils;
 import org.springframework.core.io.buffer.DefaultDataBufferFactory;
 import org.springframework.data.mongodb.util.BsonUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.client.gridfs.model.GridFSFile;
@@ -115,7 +115,7 @@ public static ReactiveGridFsResource absent(String filename) {
 	}
 
 	@Override
-	public Object getFileId() {
+	public @Nullable Object getFileId() {
 		return id instanceof BsonValue bsonValue ? BsonUtils.toJavaType(bsonValue) : id;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java
index 305e55aee4..092f81d1fa 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java
@@ -26,6 +26,7 @@
 import org.bson.BsonValue;
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.core.io.buffer.DataBufferFactory;
@@ -37,7 +38,6 @@
 import org.springframework.data.mongodb.core.query.SerializationUtils;
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.util.Lazy;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java
index 2f16c3b06e..09ea77798c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java
@@ -17,9 +17,10 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 
 import com.mongodb.client.gridfs.model.GridFSFile;
@@ -58,8 +59,7 @@ private ReactiveGridFsUpload(@Nullable ID id, Publisher<DataBuffer> dataStream,
 	 * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId()
 	 */
 	@Override
-	@Nullable
-	public ID getFileId() {
+	public @Nullable ID getFileId() {
 		return id;
 	}
 
@@ -96,8 +96,8 @@ public static ReactiveGridFsUploadBuilder<ObjectId> fromPublisher(Publisher<Data
 	public static class ReactiveGridFsUploadBuilder<T> {
 
 		private @Nullable Object id;
-		private Publisher<DataBuffer> dataStream;
-		private String filename;
+		private @Nullable Publisher<DataBuffer> dataStream;
+		private @Nullable String filename;
 		private Options options = Options.none();
 
 		private ReactiveGridFsUploadBuilder() {}
@@ -108,6 +108,7 @@ private ReactiveGridFsUploadBuilder() {}
 		 * @param source the upload content.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> content(Publisher<DataBuffer> source) {
 			this.dataStream = source;
 			return this;
@@ -120,6 +121,7 @@ public ReactiveGridFsUploadBuilder<T> content(Publisher<DataBuffer> source) {
 		 * @param <T1>
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public <T1> ReactiveGridFsUploadBuilder<T1> id(T1 id) {
 
 			this.id = id;
@@ -132,6 +134,7 @@ public <T1> ReactiveGridFsUploadBuilder<T1> id(T1 id) {
 		 * @param filename the filename to use.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> filename(String filename) {
 
 			this.filename = filename;
@@ -144,6 +147,7 @@ public ReactiveGridFsUploadBuilder<T> filename(String filename) {
 		 * @param options must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> options(Options options) {
 
 			Assert.notNull(options, "Options must not be null");
@@ -156,8 +160,9 @@ public ReactiveGridFsUploadBuilder<T> options(Options options) {
 		 * Set the file metadata.
 		 *
 		 * @param metadata must not be {@literal null}.
-		 * @return
+		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> metadata(Document metadata) {
 
 			this.options = this.options.metadata(metadata);
@@ -168,8 +173,9 @@ public ReactiveGridFsUploadBuilder<T> metadata(Document metadata) {
 		 * Set the upload chunk size in bytes.
 		 *
 		 * @param chunkSize use negative number for default.
-		 * @return
+		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> chunkSize(int chunkSize) {
 
 			this.options = this.options.chunkSize(chunkSize);
@@ -182,6 +188,7 @@ public ReactiveGridFsUploadBuilder<T> chunkSize(int chunkSize) {
 		 * @param gridFSFile must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) {
 
 			Assert.notNull(gridFSFile, "GridFSFile must not be null");
@@ -200,13 +207,20 @@ public ReactiveGridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) {
 		 * @param contentType must not be {@literal null}.
 		 * @return this.
 		 */
+		@Contract("_ -> this")
 		public ReactiveGridFsUploadBuilder<T> contentType(String contentType) {
 
 			this.options = this.options.contentType(contentType);
 			return this;
 		}
 
+		@Contract("-> new")
 		public ReactiveGridFsUpload<T> build() {
+
+			Assert.notNull(dataStream, "DataStream must be set first");
+			Assert.notNull(filename, "Filename must be set first");
+			Assert.notNull(options, "Options must be set first");
+
 			return new ReactiveGridFsUpload(id, dataStream, filename, options);
 		}
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java
index 2f3b5af150..57726d69cc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Support for MongoDB GridFS feature.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.gridfs;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AbstractMonitor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AbstractMonitor.java
deleted file mode 100644
index 9572f53702..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AbstractMonitor.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.bson.Document;
-
-import com.mongodb.ServerAddress;
-import com.mongodb.client.MongoClient;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.connection.ServerDescription;
-
-/**
- * Base class to encapsulate common configuration settings when connecting to a database
- *
- * @author Mark Pollack
- * @author Oliver Gierke
- * @author Christoph Strobl
- */
-public abstract class AbstractMonitor {
-
-	private final MongoClient mongoClient;
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	protected AbstractMonitor(MongoClient mongoClient) {
-		this.mongoClient = mongoClient;
-	}
-
-	public Document getServerStatus() {
-		return getDb("admin").runCommand(new Document("serverStatus", 1).append("rangeDeleter", 1).append("repl", 1));
-	}
-
-	public MongoDatabase getDb(String databaseName) {
-		return mongoClient.getDatabase(databaseName);
-	}
-
-	protected MongoClient getMongoClient() {
-		return mongoClient;
-	}
-
-	protected List<ServerAddress> hosts() {
-
-		return mongoClient.getClusterDescription().getServerDescriptions().stream().map(ServerDescription::getAddress)
-				.collect(Collectors.toList());
-	}
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java
deleted file mode 100644
index ec8186e30e..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for assertions
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Assertion Metrics")
-public class AssertMetrics extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	public AssertMetrics(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Regular")
-	public int getRegular() {
-		return getBtree("regular");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Warning")
-	public int getWarning() {
-		return getBtree("warning");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Msg")
-	public int getMsg() {
-		return getBtree("msg");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "User")
-	public int getUser() {
-		return getBtree("user");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Rollovers")
-	public int getRollovers() {
-		return getBtree("rollovers");
-	}
-
-	private int getBtree(String key) {
-		Document asserts = (Document) getServerStatus().get("asserts");
-		// Class c = btree.get(key).getClass();
-		return (Integer) asserts.get(key);
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java
deleted file mode 100644
index 67fa8f6562..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import java.util.Date;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for Background Flushing
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Background Flushing Metrics")
-public class BackgroundFlushingMetrics extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	public BackgroundFlushingMetrics(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Flushes")
-	public int getFlushes() {
-		return getFlushingData("flushes", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Total ms", unit = "ms")
-	public int getTotalMs() {
-		return getFlushingData("total_ms", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Average ms", unit = "ms")
-	public double getAverageMs() {
-		return getFlushingData("average_ms", java.lang.Double.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Last Ms", unit = "ms")
-	public int getLastMs() {
-		return getFlushingData("last_ms", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Last finished")
-	public Date getLastFinished() {
-		return getLast();
-	}
-
-	@SuppressWarnings("unchecked")
-	private <T> T getFlushingData(String key, Class<T> targetClass) {
-		Document mem = (Document) getServerStatus().get("backgroundFlushing");
-		return (T) mem.get(key);
-	}
-
-	private Date getLast() {
-		Document bgFlush = (Document) getServerStatus().get("backgroundFlushing");
-		Date lastFinished = (Date) bgFlush.get("last_finished");
-		return lastFinished;
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java
deleted file mode 100644
index 03924d88a0..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for B-tree index counters
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Btree Metrics")
-public class BtreeIndexCounters extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	public BtreeIndexCounters(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Accesses")
-	public int getAccesses() {
-		return getBtree("accesses");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Hits")
-	public int getHits() {
-		return getBtree("hits");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Misses")
-	public int getMisses() {
-		return getBtree("misses");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Resets")
-	public int getResets() {
-		return getBtree("resets");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Miss Ratio")
-	public int getMissRatio() {
-		return getBtree("missRatio");
-	}
-
-	private int getBtree(String key) {
-		Document indexCounters = (Document) getServerStatus().get("indexCounters");
-		if (indexCounters.get("note") != null) {
-			String message = (String) indexCounters.get("note");
-			if (message.contains("not supported")) {
-				return -1;
-			}
-		}
-		Document btree = (Document) indexCounters.get("btree");
-		// Class c = btree.get(key).getClass();
-		return (Integer) btree.get(key);
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java
deleted file mode 100644
index beb3932ea4..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for Connections
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Connection metrics")
-public class ConnectionMetrics extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	public ConnectionMetrics(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Current Connections")
-	public int getCurrent() {
-		return getConnectionData("current", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Available Connections")
-	public int getAvailable() {
-		return getConnectionData("available", java.lang.Integer.class);
-	}
-
-	@SuppressWarnings("unchecked")
-	private <T> T getConnectionData(String key, Class<T> targetClass) {
-		Document mem = (Document) getServerStatus().get("connections");
-		// Class c = mem.get(key).getClass();
-		return (T) mem.get(key);
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java
deleted file mode 100644
index 096c67b1a0..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.DBObject;
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for Global Locks
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Global Lock Metrics")
-public class GlobalLockMetrics extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient must not be {@literal null}.
-	 * @since 2.2
-	 */
-	public GlobalLockMetrics(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Total time")
-	public double getTotalTime() {
-		return getGlobalLockData("totalTime", java.lang.Double.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Lock time", unit = "s")
-	public double getLockTime() {
-		return getGlobalLockData("lockTime", java.lang.Double.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Lock time")
-	public double getLockTimeRatio() {
-		return getGlobalLockData("ratio", java.lang.Double.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Current Queue")
-	public int getCurrentQueueTotal() {
-		return getCurrentQueue("total");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Reader Queue")
-	public int getCurrentQueueReaders() {
-		return getCurrentQueue("readers");
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Writer Queue")
-	public int getCurrentQueueWriters() {
-		return getCurrentQueue("writers");
-	}
-
-	@SuppressWarnings("unchecked")
-	private <T> T getGlobalLockData(String key, Class<T> targetClass) {
-		DBObject globalLock = (DBObject) getServerStatus().get("globalLock");
-		return (T) globalLock.get(key);
-	}
-
-	private int getCurrentQueue(String key) {
-		Document globalLock = (Document) getServerStatus().get("globalLock");
-		Document currentQueue = (Document) globalLock.get("currentQueue");
-		return (Integer) currentQueue.get(key);
-	}
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java
deleted file mode 100644
index 75daa3de55..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for Memory
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Memory Metrics")
-public class MemoryMetrics extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient
-	 * @since 2.2
-	 */
-	public MemoryMetrics(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Memory address size")
-	public int getBits() {
-		return getMemData("bits", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Resident in Physical Memory", unit = "MB")
-	public int getResidentSpace() {
-		return getMemData("resident", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Virtual Address Space", unit = "MB")
-	public int getVirtualAddressSpace() {
-		return getMemData("virtual", java.lang.Integer.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Is memory info supported on this platform")
-	public boolean getMemoryInfoSupported() {
-		return getMemData("supported", java.lang.Boolean.class);
-	}
-
-	@ManagedMetric(metricType = MetricType.GAUGE, displayName = "Memory Mapped Space", unit = "MB")
-	public int getMemoryMappedSpace() {
-		return getMemData("mapped", java.lang.Integer.class);
-	}
-
-	@SuppressWarnings("unchecked")
-	private <T> T getMemData(String key, Class<T> targetClass) {
-		Document mem = (Document) getServerStatus().get("mem");
-		// Class c = mem.get(key).getClass();
-		return (T) mem.get(key);
-	}
-
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java
deleted file mode 100644
index 35281753e6..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import org.bson.Document;
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-import org.springframework.util.NumberUtils;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * JMX Metrics for Operation counters
- *
- * @author Mark Pollack
- */
-@ManagedResource(description = "Operation Counters")
-public class OperationCounters extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient
-	 * @since 2.2
-	 */
-	public OperationCounters(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Insert operation count")
-	public int getInsertCount() {
-		return getOpCounter("insert");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Query operation count")
-	public int getQueryCount() {
-		return getOpCounter("query");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Update operation count")
-	public int getUpdateCount() {
-		return getOpCounter("update");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Delete operation count")
-	public int getDeleteCount() {
-		return getOpCounter("delete");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "GetMore operation count")
-	public int getGetMoreCount() {
-		return getOpCounter("getmore");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Command operation count")
-	public int getCommandCount() {
-		return getOpCounter("command");
-	}
-
-	private int getOpCounter(String key) {
-		Document opCounters = (Document) getServerStatus().get("opcounters");
-		return NumberUtils.convertNumberToTargetClass((Number) opCounters.get(key), Integer.class);
-	}
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java
deleted file mode 100644
index bddf62d028..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2012-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import java.net.UnknownHostException;
-
-import org.springframework.jmx.export.annotation.ManagedMetric;
-import org.springframework.jmx.export.annotation.ManagedOperation;
-import org.springframework.jmx.export.annotation.ManagedResource;
-import org.springframework.jmx.support.MetricType;
-import org.springframework.util.StringUtils;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * Expose basic server information via JMX
- *
- * @author Mark Pollack
- * @author Thomas Darimont
- * @author Christoph Strobl
- */
-@ManagedResource(description = "Server Information")
-public class ServerInfo extends AbstractMonitor {
-
-	/**
-	 * @param mongoClient
-	 * @since 2.2
-	 */
-	protected ServerInfo(MongoClient mongoClient) {
-		super(mongoClient);
-	}
-
-	/**
-	 * Returns the hostname of the used server reported by MongoDB.
-	 *
-	 * @return the reported hostname can also be an IP address.
-	 * @throws UnknownHostException
-	 */
-	@ManagedOperation(description = "Server host name")
-	public String getHostName() throws UnknownHostException {
-
-		/*
-		 * UnknownHostException is not necessary anymore, but clients could have
-		 * called this method in a try..catch(UnknownHostException) already
-		 */
-		return StringUtils.collectionToDelimitedString(hosts(), ",");
-	}
-
-	@ManagedMetric(displayName = "Uptime Estimate")
-	public double getUptimeEstimate() {
-		return (Double) getServerStatus().get("uptimeEstimate");
-	}
-
-	@ManagedOperation(description = "MongoDB Server Version")
-	public String getVersion() {
-		return (String) getServerStatus().get("version");
-	}
-
-	@ManagedOperation(description = "Local Time")
-	public String getLocalTime() {
-		return (String) getServerStatus().get("localTime");
-	}
-
-	@ManagedMetric(metricType = MetricType.COUNTER, displayName = "Server uptime in seconds", unit = "seconds")
-	public double getUptime() {
-		return (Double) getServerStatus().get("uptime");
-	}
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java
index 0d495584a9..40073d6022 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB specific JMX monitoring support.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.monitor;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java
index b823ce223b..132a51ba46 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java
@@ -21,6 +21,7 @@
 
 import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames;
 import org.springframework.data.mongodb.util.MongoCompatibilityAdapter;
+import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 
 import com.mongodb.ConnectionString;
@@ -67,6 +68,10 @@ public KeyValues getLowCardinalityKeyValues(MongoHandlerContext context) {
 					.and(LowCardinalityCommandKeyNames.MONGODB_COLLECTION.withValue(context.getCollectionName()));
 		}
 
+		if(context.getCommandStartedEvent() == null) {
+			throw new IllegalStateException("not command started event present");
+		}
+
 		ConnectionDescription connectionDescription = context.getCommandStartedEvent().getConnectionDescription();
 
 		if (connectionDescription != null) {
@@ -111,6 +116,8 @@ public String getContextualName(MongoHandlerContext context) {
 		String collectionName = context.getCollectionName();
 		CommandStartedEvent commandStartedEvent = context.getCommandStartedEvent();
 
+		Assert.notNull(commandStartedEvent, "CommandStartedEvent must not be null");
+
 		if (ObjectUtils.isEmpty(collectionName)) {
 			return commandStartedEvent.getCommandName();
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java
index 854e1481fc..6185c95db5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java
@@ -17,9 +17,11 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.stream.Stream;
 
 import com.mongodb.RequestContext;
+import org.jspecify.annotations.Nullable;
 
 /**
  * A {@link Map}-based {@link RequestContext}.
@@ -42,7 +44,13 @@ public MapRequestContext(Map<Object, Object> context) {
 
 	@Override
 	public <T> T get(Object key) {
-		return (T) map.get(key);
+
+
+		T value = (T) map.get(key);
+		if(value != null) {
+			return value;
+		}
+		throw new NoSuchElementException("%s is missing".formatted(key));
 	}
 
 	@Override
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java
index cc58aac56e..cab9cd5cb8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java
@@ -25,8 +25,7 @@
 
 import org.bson.BsonDocument;
 import org.bson.BsonValue;
-
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 import com.mongodb.ConnectionString;
 import com.mongodb.RequestContext;
@@ -55,12 +54,12 @@ public class MongoHandlerContext extends SenderContext<Object> {
 					"killCursors", "listIndexes", "reIndex"));
 
 	private final @Nullable ConnectionString connectionString;
-	private final CommandStartedEvent commandStartedEvent;
-	private final RequestContext requestContext;
-	private final String collectionName;
+	private final @Nullable CommandStartedEvent commandStartedEvent;
+	private final @Nullable RequestContext requestContext;
+	private final @Nullable String collectionName;
 
-	private CommandSucceededEvent commandSucceededEvent;
-	private CommandFailedEvent commandFailedEvent;
+	private @Nullable CommandSucceededEvent commandSucceededEvent;
+	private @Nullable CommandFailedEvent commandFailedEvent;
 
 	public MongoHandlerContext(@Nullable ConnectionString connectionString, CommandStartedEvent commandStartedEvent,
 			RequestContext requestContext) {
@@ -72,28 +71,27 @@ public MongoHandlerContext(@Nullable ConnectionString connectionString, CommandS
 		this.collectionName = getCollectionName(commandStartedEvent);
 	}
 
-	public CommandStartedEvent getCommandStartedEvent() {
+	public @Nullable CommandStartedEvent getCommandStartedEvent() {
 		return this.commandStartedEvent;
 	}
 
-	public RequestContext getRequestContext() {
+	public @Nullable RequestContext getRequestContext() {
 		return this.requestContext;
 	}
 
 	public String getDatabaseName() {
-		return commandStartedEvent.getDatabaseName();
+		return commandStartedEvent != null ? commandStartedEvent.getDatabaseName() : "n/a";
 	}
 
-	public String getCollectionName() {
+	public @Nullable String getCollectionName() {
 		return this.collectionName;
 	}
 
 	public String getCommandName() {
-		return commandStartedEvent.getCommandName();
+		return commandStartedEvent != null ? commandStartedEvent.getCommandName() : "n/a";
 	}
 
-	@Nullable
-	public ConnectionString getConnectionString() {
+	public @Nullable ConnectionString getConnectionString() {
 		return connectionString;
 	}
 
@@ -135,8 +133,7 @@ private static String getCollectionName(CommandStartedEvent event) {
 	 *
 	 * @return trimmed string from {@code bsonValue} or null if the trimmed string was empty or the value wasn't a string
 	 */
-	@Nullable
-	private static String getNonEmptyBsonString(@Nullable BsonValue bsonValue) {
+	private static @Nullable String getNonEmptyBsonString(@Nullable BsonValue bsonValue) {
 
 		if (bsonValue == null || !bsonValue.isString()) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java
index 9360a95de2..914396ab96 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java
@@ -23,7 +23,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.ConnectionString;
@@ -197,8 +197,7 @@ private void doInObservation(@Nullable RequestContext requestContext,
 	 * @param context
 	 * @return
 	 */
-	@Nullable
-	private static Observation observationFromContext(RequestContext context) {
+	private static @Nullable Observation observationFromContext(RequestContext context) {
 
 		Observation observation = context.getOrDefault(ObservationThreadLocalAccessor.KEY, null);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java
index d240e12f9e..d6319e5f4f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java
@@ -1,5 +1,5 @@
 /**
  * Infrastructure to provide driver observability using Micrometer.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.observability;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java
index 900342bbcb..989655f4a6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java
@@ -1,5 +1,5 @@
 /**
  * Spring Data's MongoDB abstraction.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java
index b1ba6ea3f0..00ff498731 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java
@@ -19,6 +19,7 @@
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.aot.hint.MemberCategory;
 import org.springframework.aot.hint.RuntimeHints;
 import org.springframework.aot.hint.RuntimeHintsRegistrar;
@@ -28,7 +29,6 @@
 import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor;
 import org.springframework.data.mongodb.repository.support.ReactiveQuerydslMongoPredicateExecutor;
 import org.springframework.data.querydsl.QuerydslUtils;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java
index 9016519d9b..750cc38678 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java
@@ -1,5 +1,5 @@
 /**
  * Ahead-Of-Time processors for MongoDB repositories.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository.aot;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java
index a2cbf659dd..db7edc05bd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java
@@ -1,6 +1,6 @@
 /**
  * CDI support for MongoDB specific repository implementation.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository.cdi;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java
index d0d9b07081..e276d4d1e0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Support infrastructure for the configuration of MongoDB specific repositories.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository.config;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java
index 8deddfe939..799597e19c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java
@@ -1,6 +1,6 @@
 /**
  * MongoDB specific repository implementation.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
index 4d0d604a27..e160fd879a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
@@ -20,12 +20,9 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
-
-import org.springframework.core.env.StandardEnvironment;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.expression.ValueEvaluationContextProvider;
 import org.springframework.data.expression.ValueExpression;
-import org.springframework.data.expression.ValueExpressionParser;
-import org.springframework.data.mapping.model.SpELExpressionEvaluator;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
 import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
@@ -47,17 +44,10 @@
 import org.springframework.data.mongodb.util.json.ParameterBindingContext;
 import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
 import org.springframework.data.repository.query.ParameterAccessor;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
-import org.springframework.data.spel.ExpressionDependencies;
 import org.springframework.data.util.Lazy;
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -84,36 +74,6 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
 	private final ValueExpressionDelegate valueExpressionDelegate;
 	private final ValueEvaluationContextProvider valueEvaluationContextProvider;
 
-	/**
-	 * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param operations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated use the constructor version with {@link ValueExpressionDelegate}
-	 */
-	@Deprecated(since = "4.4.0")
-	public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser,
-			QueryMethodEvaluationContextProvider evaluationContextProvider) {
-
-		Assert.notNull(operations, "MongoOperations must not be null");
-		Assert.notNull(method, "MongoQueryMethod must not be null");
-		Assert.notNull(expressionParser, "SpelExpressionParser must not be null");
-		Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null");
-
-		this.method = method;
-		this.operations = operations;
-
-		MongoEntityMetadata<?> metadata = method.getEntityInformation();
-		Class<?> type = metadata.getCollectionEntity().getType();
-
-		this.executableFind = operations.query(type);
-		this.executableUpdate = operations.update(type);
-		this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser));
-		this.valueEvaluationContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
-	}
-
 	/**
 	 * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
 	 *
@@ -145,7 +105,7 @@ public MongoQueryMethod getQueryMethod() {
 	}
 
 	@Override
-	public Object execute(Object[] parameters) {
+	public @Nullable Object execute(Object[] parameters) {
 
 		ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(operations.getConverter(),
 				new MongoParametersParameterAccessor(method, parameters));
@@ -165,8 +125,7 @@ public Object execute(Object[] parameters) {
 	 * @param accessor for providing invocation arguments. Never {@literal null}.
 	 * @param typeToRead the desired component target type. Can be {@literal null}.
 	 */
-	@Nullable
-	protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor,
+	protected @Nullable Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor,
 			@Nullable Class<?> typeToRead) {
 
 		Query query = createQuery(accessor);
@@ -185,7 +144,8 @@ protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, C
 	}
 
 	/**
-	 * If present apply the {@link com.mongodb.ReadPreference} from the {@link org.springframework.data.mongodb.repository.ReadPreference} annotation.
+	 * If present apply the {@link com.mongodb.ReadPreference} from the
+	 * {@link org.springframework.data.mongodb.repository.ReadPreference} annotation.
 	 *
 	 * @param query must not be {@literal null}.
 	 * @return never {@literal null}.
@@ -200,6 +160,7 @@ private Query applyAnnotatedReadPreferenceIfPresent(Query query) {
 		return query.withReadPreference(com.mongodb.ReadPreference.valueOf(method.getAnnotatedReadPreference()));
 	}
 
+	@SuppressWarnings("NullAway")
 	private MongoQueryExecution getExecution(ConvertingParameterAccessor accessor, FindWithQuery<?> operation) {
 
 		if (isDeleteQuery()) {
@@ -320,6 +281,7 @@ protected Query createCountQuery(ConvertingParameterAccessor accessor) {
 	 * @throws IllegalStateException if no update could be found.
 	 * @since 3.4
 	 */
+	@SuppressWarnings("NullAway")
 	protected UpdateDefinition createUpdate(ConvertingParameterAccessor accessor) {
 
 		if (accessor.getUpdate() != null) {
@@ -396,20 +358,6 @@ protected ParameterBindingDocumentCodec getParameterBindingCodec() {
 		return codec.get();
 	}
 
-	/**
-	 * Obtain a the {@link EvaluationContext} suitable to evaluate expressions backed by the given dependencies.
-	 *
-	 * @param dependencies must not be {@literal null}.
-	 * @param accessor must not be {@literal null}.
-	 * @return the {@link SpELExpressionEvaluator}.
-	 * @since 2.4
-	 */
-	protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDependencies dependencies,
-			ConvertingParameterAccessor accessor) {
-
-		return new DefaultSpELExpressionEvaluator(new SpelExpressionParser(), valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), dependencies).getEvaluationContext());
-	}
-
 	/**
 	 * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions.
 	 *
@@ -418,14 +366,16 @@ protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDepend
 	 * @since 4.4.0
 	 */
 	protected ValueExpressionEvaluator getExpressionEvaluatorFor(MongoParameterAccessor accessor) {
-		return new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, (ValueExpression expression) ->
-				valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies()));
+		return new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate,
+				(ValueExpression expression) -> valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(),
+						expression.getExpressionDependencies()));
 	}
 
 	/**
 	 * @return the {@link CodecRegistry} used.
 	 * @since 2.4
 	 */
+	@SuppressWarnings("NullAway")
 	protected CodecRegistry getCodecRegistry() {
 		return operations.execute(MongoDatabase::getCodecRegistry);
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
index a5754a4e46..d363c93442 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
@@ -24,17 +24,15 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 
 import org.springframework.core.convert.converter.Converter;
-import org.springframework.core.env.StandardEnvironment;
 import org.springframework.data.expression.ReactiveValueEvaluationContextProvider;
 import org.springframework.data.expression.ValueEvaluationContext;
 import org.springframework.data.expression.ValueEvaluationContextProvider;
 import org.springframework.data.expression.ValueExpression;
-import org.springframework.data.expression.ValueExpressionParser;
 import org.springframework.data.mapping.model.EntityInstantiators;
-import org.springframework.data.mapping.model.SpELExpressionEvaluator;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithProjection;
@@ -56,15 +54,10 @@
 import org.springframework.data.mongodb.util.json.ParameterBindingContext;
 import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
 import org.springframework.data.repository.query.ParameterAccessor;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.spel.ExpressionDependencies;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -89,45 +82,6 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
 	private final ValueExpressionDelegate valueExpressionDelegate;
 	private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider;
 
-	/**
-	 * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
-	 * {@link MongoOperations}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param operations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated use the constructor version with {@link ValueExpressionDelegate}
-	 */
-	@Deprecated(since = "4.4.0")
-	public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
-			ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
-
-		Assert.notNull(method, "MongoQueryMethod must not be null");
-		Assert.notNull(operations, "ReactiveMongoOperations must not be null");
-		Assert.notNull(expressionParser, "SpelExpressionParser must not be null");
-		Assert.notNull(evaluationContextProvider, "ReactiveEvaluationContextExtension must not be null");
-
-		this.method = method;
-		this.operations = operations;
-		this.instantiators = new EntityInstantiators();
-		this.valueExpressionDelegate = new ValueExpressionDelegate(
-				new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(),
-						evaluationContextProvider.getEvaluationContextProvider()),
-				ValueExpressionParser.create(() -> expressionParser));
-
-		MongoEntityMetadata<?> metadata = method.getEntityInformation();
-		Class<?> type = metadata.getCollectionEntity().getType();
-
-		this.findOperationWithProjection = operations.query(type);
-		this.updateOps = operations.update(type);
-		ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate
-				.createValueContextProvider(method.getParameters());
-		Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider,
-				"ValueEvaluationContextProvider must be reactive");
-		this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
-	}
-
 	/**
 	 * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
 	 * {@link MongoOperations}.
@@ -241,6 +195,7 @@ private ReactiveMongoQueryExecution getExecution(MongoParameterAccessor accessor
 		return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing);
 	}
 
+	@SuppressWarnings("NullAway")
 	private ReactiveMongoQueryExecution getExecutionToWrap(MongoParameterAccessor accessor, FindWithQuery<?> operation) {
 
 		if (isDeleteQuery()) {
@@ -380,6 +335,7 @@ protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) {
 	 * @throws IllegalStateException if no update could be found.
 	 * @since 3.4
 	 */
+	@SuppressWarnings("NullAway")
 	protected Mono<UpdateDefinition> createUpdate(MongoParameterAccessor accessor) {
 
 		if (accessor.getUpdate() != null) {
@@ -460,26 +416,6 @@ protected Mono<ParameterBindingDocumentCodec> getParameterBindingCodec() {
 		return getCodecRegistry().map(ParameterBindingDocumentCodec::new);
 	}
 
-	/**
-	 * Obtain a {@link Mono publisher} emitting the {@link SpELExpressionEvaluator} suitable to evaluate expressions
-	 * backed by the given dependencies.
-	 *
-	 * @param dependencies must not be {@literal null}.
-	 * @param accessor must not be {@literal null}.
-	 * @return a {@link Mono} emitting the {@link SpELExpressionEvaluator} when ready.
-	 * @since 3.4
-	 * @deprecated since 4.4.0, use
-	 *             {@link #getValueExpressionEvaluatorLater(ExpressionDependencies, MongoParameterAccessor)} instead
-	 */
-	@Deprecated(since = "4.4.0")
-	protected Mono<SpELExpressionEvaluator> getSpelEvaluatorFor(ExpressionDependencies dependencies,
-			MongoParameterAccessor accessor) {
-		return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
-				.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(
-						new SpelExpressionParser(), evaluationContext.getEvaluationContext()))
-				.defaultIfEmpty(DefaultSpELExpressionEvaluator.unsupported());
-	}
-
 	/**
 	 * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions.
 	 *
@@ -491,7 +427,7 @@ ValueExpressionEvaluator getValueExpressionEvaluator(MongoParameterAccessor acce
 		return new ValueExpressionEvaluator() {
 
 			@Override
-			public <T> T evaluate(String expressionString) {
+			public <T> @Nullable T evaluate(String expressionString) {
 				ValueExpression expression = valueExpressionDelegate.parse(expressionString);
 				ValueEvaluationContext evaluationContext = valueEvaluationContextProvider
 						.getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
index 6eb6a5da89..639c694ef9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
@@ -22,7 +22,7 @@
 import java.util.function.LongUnaryOperator;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort.Order;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
@@ -41,7 +41,6 @@
 import org.springframework.data.repository.query.ReturnedType;
 import org.springframework.data.util.ReflectionUtils;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
 
@@ -166,8 +165,7 @@ static AggregationOptions computeOptions(MongoQueryMethod method, ConvertingPara
 	 * Prepares the AggregationPipeline including type discovery and calling {@link AggregationCallback} to run the
 	 * aggregation.
 	 */
-	@Nullable
-	static <T> T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor,
+	static <T> @Nullable T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor,
 			ConvertingParameterAccessor accessor,
 			Function<MongoParameterAccessor, ValueExpressionEvaluator> evaluatorFunction, AggregationCallback<T> callback) {
 
@@ -308,8 +306,7 @@ static void appendLimitAndOffsetIfPresent(AggregationPipeline aggregationPipelin
 	 * @return can be {@literal null} if source {@link Document#isEmpty() is empty}.
 	 * @throws IllegalArgumentException when none of the above rules is met.
 	 */
-	@Nullable
-	static <T> T extractSimpleTypeResult(@Nullable Document source, Class<T> targetType, MongoConverter converter) {
+	static <T> @Nullable T extractSimpleTypeResult(@Nullable Document source, Class<T> targetType, MongoConverter converter) {
 
 		if (ObjectUtils.isEmpty(source)) {
 			return null;
@@ -336,9 +333,8 @@ static <T> T extractSimpleTypeResult(@Nullable Document source, Class<T> targetT
 				String.format("o_O no entry of type %s found in %s.", targetType.getSimpleName(), source.toJson()));
 	}
 
-	@Nullable
 	@SuppressWarnings("unchecked")
-	private static <T> T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value,
+	private static <T> @Nullable T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value,
 			Class<T> targetType) {
 
 		if (value == null) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java
index 2aac6b77a8..108c6ee796 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java
@@ -20,12 +20,12 @@
 import java.util.regex.Pattern;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.util.json.ParameterBindingContext;
 import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
-import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -55,8 +55,7 @@ private CollationUtils() {
 	 * @return can be {@literal null} if neither {@link ConvertingParameterAccessor#getCollation()} nor
 	 *         {@literal collationExpression} are present.
 	 */
-	@Nullable
-	static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor,
+	static @Nullable Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor,
 			ValueExpressionEvaluator expressionEvaluator) {
 
 		if (accessor.getCollation() != null) {
@@ -98,6 +97,7 @@ static Collation computeCollation(@Nullable String collationExpression, Converti
 					ObjectUtils.nullSafeClassName(placeholderValue)));
 		}
 
+		Assert.notNull(placeholderValue, "PlaceholderValue must not be null");
 		return Collation.parse(collationExpression.replace(placeholder, placeholderValue.toString()));
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
index dbf87f2f2e..d075b67efe 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
@@ -21,6 +21,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Limit;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Range;
@@ -35,7 +36,6 @@
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.repository.query.ParameterAccessor;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -74,7 +74,7 @@ public PotentiallyConvertingIterator iterator() {
 	}
 
 	@Override
-	public ScrollPosition getScrollPosition() {
+	public @Nullable ScrollPosition getScrollPosition() {
 		return delegate.getScrollPosition();
 	}
 
@@ -87,34 +87,34 @@ public Sort getSort() {
 	}
 
 	@Override
-	public Class<?> findDynamicProjection() {
+	public @Nullable Class<?> findDynamicProjection() {
 		return delegate.findDynamicProjection();
 	}
 
-	public Object getBindableValue(int index) {
+	public @Nullable Object getBindableValue(int index) {
 		return getConvertedValue(delegate.getBindableValue(index), null);
 	}
 
 	@Override
-	public Range<Distance> getDistanceRange() {
+	public @Nullable Range<Distance> getDistanceRange() {
 		return delegate.getDistanceRange();
 	}
 
-	public Point getGeoNearLocation() {
+	public @Nullable Point getGeoNearLocation() {
 		return delegate.getGeoNearLocation();
 	}
 
-	public TextCriteria getFullText() {
+	public @Nullable TextCriteria getFullText() {
 		return delegate.getFullText();
 	}
 
 	@Override
-	public Collation getCollation() {
+	public @Nullable Collation getCollation() {
 		return delegate.getCollation();
 	}
 
 	@Override
-	public UpdateDefinition getUpdate() {
+	public @Nullable UpdateDefinition getUpdate() {
 		return delegate.getUpdate();
 	}
 
@@ -130,8 +130,7 @@ public Limit getLimit() {
 	 * @param typeInformation can be {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	private Object getConvertedValue(Object value, @Nullable TypeInformation<?> typeInformation) {
+	private @Nullable Object getConvertedValue(@Nullable Object value, @Nullable TypeInformation<?> typeInformation) {
 		return writer.convertToMongoType(value, typeInformation == null ? null : typeInformation.getActualType());
 	}
 
@@ -161,11 +160,11 @@ public boolean hasNext() {
 			return delegate.hasNext();
 		}
 
-		public Object next() {
+		public @Nullable Object next() {
 			return delegate.next();
 		}
 
-		public Object nextConverted(MongoPersistentProperty property) {
+		public @Nullable Object nextConverted(MongoPersistentProperty property) {
 
 			Object next = next();
 
@@ -228,7 +227,7 @@ private static Collection<?> asCollection(@Nullable Object source) {
 	}
 
 	@Override
-	public Object[] getValues() {
+	public Object @Nullable[] getValues() {
 		return delegate.getValues();
 	}
 
@@ -244,6 +243,6 @@ public interface PotentiallyConvertingIterator extends Iterator<Object> {
 		 *
 		 * @return
 		 */
-		Object nextConverted(MongoPersistentProperty property);
+		@Nullable Object nextConverted(MongoPersistentProperty property);
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java
deleted file mode 100644
index 16a1e55226..0000000000
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2020-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.repository.query;
-
-import org.springframework.data.mapping.model.SpELExpressionEvaluator;
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.ExpressionParser;
-
-/**
- * Simple {@link SpELExpressionEvaluator} implementation using {@link ExpressionParser} and {@link EvaluationContext}.
- *
- * @author Mark Paluch
- * @since 3.1
- */
-class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
-
-	private final ExpressionParser parser;
-	private final EvaluationContext context;
-
-	DefaultSpELExpressionEvaluator(ExpressionParser parser, EvaluationContext context) {
-		this.parser = parser;
-		this.context = context;
-	}
-
-	/**
-	 * Return a {@link SpELExpressionEvaluator} that does not support expression evaluation.
-	 *
-	 * @return a {@link SpELExpressionEvaluator} that does not support expression evaluation.
-	 * @since 3.1
-	 */
-	public static SpELExpressionEvaluator unsupported() {
-		return NoOpExpressionEvaluator.INSTANCE;
-	}
-
-	@Override
-	@SuppressWarnings("unchecked")
-	public <T> T evaluate(String expression) {
-		return (T) parser.parseExpression(expression).getValue(context, Object.class);
-	}
-
-	/**
-	 * {@link SpELExpressionEvaluator} that does not support SpEL evaluation.
-	 *
-	 * @author Mark Paluch
-	 * @since 3.1
-	 */
-	enum NoOpExpressionEvaluator implements SpELExpressionEvaluator {
-
-		INSTANCE;
-
-		@Override
-		public <T> T evaluate(String expression) {
-			throw new UnsupportedOperationException("Expression evaluation not supported");
-		}
-	}
-}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java
index 8678e5a74c..c54d689b52 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java
@@ -15,9 +15,9 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.repository.core.EntityInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * Mongo specific {@link EntityInformation}.
@@ -58,8 +58,7 @@ default boolean isVersioned() {
 	 * @return can be {@literal null}.
 	 * @since 2.2
 	 */
-	@Nullable
-	default Object getVersion(T entity) {
+	default @Nullable Object getVersion(T entity) {
 		return null;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
index 5db853e810..00d748f8a9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.geo.Distance;
 import org.springframework.data.geo.Point;
@@ -23,7 +24,6 @@
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.repository.query.ParameterAccessor;
-import org.springframework.lang.Nullable;
 
 /**
  * Mongo-specific {@link ParameterAccessor} exposing a maximum distance parameter.
@@ -41,7 +41,7 @@ public interface MongoParameterAccessor extends ParameterAccessor {
 	 * @return the maximum distance to apply to the geo query or {@literal null} if there's no {@link Distance} parameter
 	 *         at all or the given value for it was {@literal null}.
 	 */
-	Range<Distance> getDistanceRange();
+	@Nullable Range<Distance> getDistanceRange();
 
 	/**
 	 * Returns the {@link Point} to use for a geo-near query.
@@ -75,7 +75,7 @@ public interface MongoParameterAccessor extends ParameterAccessor {
 	 * @return
 	 * @since 1.8
 	 */
-	Object[] getValues();
+	Object @Nullable[] getValues();
 
 	/**
 	 * Returns the {@link Update} to be used for an update execution.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
index 257d8d1918..3453e05617 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java
@@ -19,6 +19,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.MethodParameter;
 import org.springframework.data.domain.Range;
 import org.springframework.data.geo.Distance;
@@ -32,7 +33,6 @@
 import org.springframework.data.repository.query.Parameters;
 import org.springframework.data.repository.query.ParametersSource;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * Custom extension of {@link Parameters} discovering additional
@@ -46,9 +46,9 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter>
 
 	private final int rangeIndex;
 	private final int maxDistanceIndex;
-	private final @Nullable Integer fullTextIndex;
-	private final @Nullable Integer nearIndex;
-	private final @Nullable Integer collationIndex;
+	private final int fullTextIndex;
+	private final int nearIndex;
+	private final int collationIndex;
 	private final int updateIndex;
 	private final TypeInformation<?> domainType;
 
@@ -89,9 +89,8 @@ private MongoParameters(ParametersSource parametersSource, NearIndex nearIndex)
 		this.nearIndex = nearIndex.nearIndex;
 	}
 
-	private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @Nullable Integer nearIndex,
-			@Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex,
-			TypeInformation<?> domainType) {
+	private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, int nearIndex, int fullTextIndex,
+			int rangeIndex, int collationIndex, int updateIndex, TypeInformation<?> domainType) {
 
 		super(parameters);
 
@@ -106,7 +105,7 @@ private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @
 
 	static class NearIndex {
 
-		private final @Nullable Integer nearIndex;
+		private final int nearIndex;
 
 		public NearIndex(ParametersSource parametersSource, boolean isGeoNearMethod) {
 
@@ -191,7 +190,7 @@ public int getNearIndex() {
 	 * @since 1.6
 	 */
 	public int getFullTextParameterIndex() {
-		return fullTextIndex != null ? fullTextIndex : -1;
+		return fullTextIndex;
 	}
 
 	/**
@@ -199,7 +198,7 @@ public int getFullTextParameterIndex() {
 	 * @since 1.6
 	 */
 	public boolean hasFullTextParameter() {
-		return this.fullTextIndex != null && this.fullTextIndex >= 0;
+		return this.fullTextIndex >= 0;
 	}
 
 	/**
@@ -217,7 +216,7 @@ public int getRangeIndex() {
 	 * @since 2.2
 	 */
 	public int getCollationParameterIndex() {
-		return collationIndex != null ? collationIndex : -1;
+		return collationIndex;
 	}
 
 	/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
index ac1931e10c..66529dfce9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.domain.Range.Bound;
 import org.springframework.data.geo.Distance;
@@ -24,7 +25,7 @@
 import org.springframework.data.mongodb.core.query.TextCriteria;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.repository.query.ParametersParameterAccessor;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -53,7 +54,8 @@ public MongoParametersParameterAccessor(MongoQueryMethod method, Object[] values
 		this.method = method;
 	}
 
-	public Range<Distance> getDistanceRange() {
+	@SuppressWarnings("NullAway")
+	public @Nullable Range<Distance> getDistanceRange() {
 
 		MongoParameters mongoParameters = method.getParameters();
 
@@ -70,7 +72,7 @@ public Range<Distance> getDistanceRange() {
 		return Range.of(Bound.unbounded(), maxDistance);
 	}
 
-	public Point getGeoNearLocation() {
+	public @Nullable Point getGeoNearLocation() {
 
 		int nearIndex = method.getParameters().getNearIndex();
 
@@ -95,14 +97,14 @@ public Point getGeoNearLocation() {
 		return (Point) value;
 	}
 
-	@Nullable
 	@Override
-	public TextCriteria getFullText() {
+	public @Nullable TextCriteria getFullText() {
 		int index = method.getParameters().getFullTextParameterIndex();
 		return index >= 0 ? potentiallyConvertFullText(getValue(index)) : null;
 	}
 
-	protected TextCriteria potentiallyConvertFullText(Object fullText) {
+	@Contract("null -> fail")
+	protected TextCriteria potentiallyConvertFullText(@Nullable Object fullText) {
 
 		Assert.notNull(fullText, "Fulltext parameter must not be 'null'.");
 
@@ -124,7 +126,7 @@ protected TextCriteria potentiallyConvertFullText(Object fullText) {
 	}
 
 	@Override
-	public Collation getCollation() {
+	public @Nullable Collation getCollation() {
 
 		if (method.getParameters().getCollationParameterIndex() == -1) {
 			return null;
@@ -134,12 +136,12 @@ public Collation getCollation() {
 	}
 
 	@Override
-	public Object[] getValues() {
+	public Object @Nullable[] getValues() {
 		return super.getValues();
 	}
 
 	@Override
-	public UpdateDefinition getUpdate() {
+	public @Nullable UpdateDefinition getUpdate() {
 
 		int updateIndex = method.getParameters().getUpdateIndex();
 		return updateIndex == -1 ? null : (UpdateDefinition) getValue(updateIndex);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
index 66a8870623..7e327f4e20 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
@@ -26,6 +26,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.bson.BsonRegularExpression;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.domain.Range.Bound;
 import org.springframework.data.domain.Sort;
@@ -52,7 +53,6 @@
 import org.springframework.data.repository.query.parser.Part.Type;
 import org.springframework.data.repository.query.parser.PartTree;
 import org.springframework.data.util.Streamable;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -111,7 +111,7 @@ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor,
 	protected Criteria create(Part part, Iterator<Object> iterator) {
 
 		if (isGeoNearQuery && part.getType().equals(Type.NEAR)) {
-			return null;
+			return new Criteria();
 		}
 
 		PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
@@ -141,7 +141,7 @@ protected Criteria or(Criteria base, Criteria criteria) {
 	}
 
 	@Override
-	protected Query complete(Criteria criteria, Sort sort) {
+	protected Query complete(@Nullable Criteria criteria, Sort sort) {
 
 		Query query = (criteria == null ? new Query() : new Query(criteria)).with(sort);
 
@@ -161,6 +161,7 @@ protected Query complete(Criteria criteria, Sort sort) {
 	 * @param parameters
 	 * @return
 	 */
+	@SuppressWarnings("NullAway")
 	private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria, Iterator<Object> parameters) {
 
 		Type type = part.getType();
@@ -333,6 +334,7 @@ private Criteria createContainingCriteria(Part part, MongoPersistentProperty pro
 	 * @param value
 	 * @return the criteria extended with the regex.
 	 */
+	@SuppressWarnings("NullAway")
 	private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, Object value) {
 
 		if (value == null) {
@@ -348,8 +350,7 @@ private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, Object
 	 * @param part
 	 * @return the regex options or {@literal null}.
 	 */
-	@Nullable
-	private String toRegexOptions(Part part) {
+	private @Nullable String toRegexOptions(Part part) {
 
 		String regexOptions = null;
 		switch (part.shouldIgnoreCase()) {
@@ -414,10 +415,11 @@ private Streamable<?> asStreamable(Object value) {
 		return Streamable.of(value);
 	}
 
-	private String toLikeRegex(String source, Part part) {
+	private @Nullable String toLikeRegex(String source, Part part) {
 		return MongoRegexCreator.INSTANCE.toRegularExpression(source, toMatchMode(part.getType()));
 	}
 
+	@SuppressWarnings("NullAway")
 	private boolean isSpherical(MongoPersistentProperty property) {
 
 		if (property.isAnnotationPresent(GeoSpatialIndexed.class)) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java
index dd2b78de59..abdcf62930 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java
@@ -18,6 +18,7 @@
 import java.util.List;
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Range;
@@ -39,7 +40,6 @@
 import org.springframework.data.mongodb.repository.util.SliceUtils;
 import org.springframework.data.support.PageableExecutionUtils;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -171,10 +171,12 @@ public Object execute(Query query) {
 			return isListOfGeoResult(method.getReturnType()) ? results.getContent() : results;
 		}
 
-		@SuppressWarnings("unchecked")
+		@SuppressWarnings({"unchecked","NullAway"})
 		GeoResults<Object> doExecuteQuery(Query query) {
 
 			Point nearLocation = accessor.getGeoNearLocation();
+			Assert.notNull(nearLocation, "[query.location] must not be null");
+
 			NearQuery nearQuery = NearQuery.near(nearLocation);
 
 			if (query != null) {
@@ -182,6 +184,8 @@ GeoResults<Object> doExecuteQuery(Query query) {
 			}
 
 			Range<Distance> distances = accessor.getDistanceRange();
+			Assert.notNull(nearLocation, "[query.distance] must not be null");
+
 			distances.getLowerBound().getValue().ifPresent(it -> nearQuery.minDistance(it).in(it.getMetric()));
 			distances.getUpperBound().getValue().ifPresent(it -> nearQuery.maxDistance(it).in(it.getMetric()));
 
@@ -267,7 +271,7 @@ public DeleteExecution(MongoOperations operations, MongoQueryMethod method) {
 		}
 
 		@Override
-		public Object execute(Query query) {
+		public @Nullable Object execute(Query query) {
 
 			String collectionName = method.getEntityInformation().getCollectionName();
 			Class<?> type = method.getEntityInformation().getJavaType();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
index dcfef7de00..fc4ea90f49 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
@@ -23,6 +23,7 @@
 import java.util.Map;
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.annotation.AnnotatedElementUtils;
 import org.springframework.data.geo.GeoPage;
 import org.springframework.data.geo.GeoResult;
@@ -48,7 +49,6 @@
 import org.springframework.data.util.ReactiveWrappers;
 import org.springframework.data.util.ReflectionUtils;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ConcurrentReferenceHashMap;
@@ -478,7 +478,7 @@ public boolean hasAnnotatedUpdate() {
 	 * @return the {@link Update} or {@literal null} if not present.
 	 * @since 3.4
 	 */
-	public Update getUpdateSource() {
+	public @Nullable Update getUpdateSource() {
 		return lookupUpdateAnnotation().orElse(null);
 	}
 
@@ -488,6 +488,7 @@ public Update getUpdateSource() {
 	 * @since 3.4
 	 * @throws IllegalStateException
 	 */
+	@SuppressWarnings("NullAway")
 	public void verify() {
 
 		if (isModifyingQuery()) {
@@ -526,6 +527,7 @@ public void verify() {
 		}
 	}
 
+	@SuppressWarnings("NullAway")
 	private boolean isNumericOrVoidReturnValue() {
 
 		Class<?> resultType = getReturnedObjectType();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
index afabf9c37e..6116cc5534 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
@@ -18,8 +18,6 @@
 import org.bson.Document;
 import org.bson.json.JsonParseException;
 
-import org.springframework.core.env.StandardEnvironment;
-import org.springframework.data.expression.ValueExpressionParser;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.MongoTemplate;
@@ -29,14 +27,11 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.TextCriteria;
 import org.springframework.data.repository.query.QueryMethod;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ReturnedType;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.repository.query.parser.PartTree;
-import org.springframework.expression.ExpressionParser;
 import org.springframework.util.StringUtils;
 
 /**
@@ -54,26 +49,6 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
 	private final MappingContext<?, MongoPersistentProperty> context;
 	private final ResultProcessor processor;
 
-	/**
-	 * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, ExpressionParser expressionParser,
-			QueryMethodEvaluationContextProvider evaluationContextProvider) {
-		super(method, mongoOperations, expressionParser, evaluationContextProvider);
-
-		this.processor = method.getResultProcessor();
-		this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
-		this.isGeoNearQuery = method.isGeoNearQuery();
-		this.context = mongoOperations.getConverter().getMappingContext();
-	}
-
 	/**
 	 * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
 	 *
@@ -103,6 +78,7 @@ public PartTree getTree() {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	protected Query createQuery(ConvertingParameterAccessor accessor) {
 
 		MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java
index 431510f11b..4b7262749a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java
@@ -21,13 +21,12 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.aop.framework.ProxyFactory;
 import org.springframework.data.mongodb.core.query.BasicQuery;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java
index 324f01d61f..9534a9cf4f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
+import org.jspecify.annotations.Nullable;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -51,16 +52,21 @@ public ReactiveMongoParameterAccessor(MongoQueryMethod method, Object[] values)
 	 * @see org.springframework.data.mongodb.repository.query.MongoParametersParameterAccessor#getValues()
 	 */
 	@Override
-	public Object[] getValues() {
+	public Object @Nullable[] getValues() {
 
-		Object[] result = new Object[super.getValues().length];
+		Object[] values = super.getValues();
+		if(values == null) {
+			return new Object[0];
+		}
+
+		Object[] result = new Object[values.length];
 		for (int i = 0; i < result.length; i++) {
 			result[i] = getValue(i);
 		}
 		return result;
 	}
 
-	public Object getBindableValue(int index) {
+	public @Nullable Object getBindableValue(int index) {
 		return getValue(getParameters().getBindableParameter(index).getIndex());
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java
index d18c6a989c..06f946d745 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java
@@ -18,6 +18,7 @@
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.DtoInstantiatingConverter;
@@ -37,7 +38,6 @@
 import org.springframework.data.util.ReactiveWrappers;
 import org.springframework.data.util.ReflectionUtils;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -86,6 +86,8 @@ public Publisher<? extends Object> execute(Query query, Class<?> type, String co
 		private Flux<GeoResult<Object>> doExecuteQuery(@Nullable Query query, Class<?> type, String collection) {
 
 			Point nearLocation = accessor.getGeoNearLocation();
+			Assert.notNull(nearLocation, "[query.location] ist not present");
+
 			NearQuery nearQuery = NearQuery.near(nearLocation);
 
 			if (query != null) {
@@ -93,6 +95,8 @@ private Flux<GeoResult<Object>> doExecuteQuery(@Nullable Query query, Class<?> t
 			}
 
 			Range<Distance> distances = accessor.getDistanceRange();
+
+			Assert.notNull(distances, "[query.range] ist not present");
 			distances.getUpperBound().getValue().ifPresent(it -> nearQuery.maxDistance(it).in(it.getMetric()));
 			distances.getLowerBound().getValue().ifPresent(it -> nearQuery.minDistance(it).in(it.getMetric()));
 
@@ -195,6 +199,7 @@ public ResultProcessingExecution(ReactiveMongoQueryExecution delegate, Converter
 		}
 
 		@Override
+		@SuppressWarnings("NullAway")
 		public Publisher<? extends Object> execute(Query query, Class<?> type, String collection) {
 			return (Publisher) converter.convert(delegate.execute(query, type, collection));
 		}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java
index 8df1ba487e..966bbc0a26 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java
@@ -15,8 +15,6 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
-import static org.springframework.data.repository.util.ClassUtils.*;
-
 import java.lang.reflect.Method;
 
 import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -35,6 +33,7 @@
 import org.springframework.data.repository.util.ReactiveWrapperConverters;
 import org.springframework.data.util.Lazy;
 import org.springframework.data.util.ReactiveWrappers;
+import org.springframework.data.util.ReflectionUtils;
 import org.springframework.data.util.TypeInformation;
 import org.springframework.util.ClassUtils;
 
@@ -130,7 +129,7 @@ public boolean hasReactiveWrapperParameter() {
 	@Override
 	public void verify() {
 
-		if (hasParameterOfType(method, Pageable.class)) {
+		if (ReflectionUtils.hasParameterOfType(method, Pageable.class)) {
 
 			TypeInformation<?> returnType = TypeInformation.fromReturnTypeOf(method);
 
@@ -139,7 +138,7 @@ public void verify() {
 					&& (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())
 							|| SLICE_TYPE.isAssignableFrom(returnType.getRequiredComponentType()));
 
-			if (hasParameterOfType(method, Sort.class)) {
+			if (ReflectionUtils.hasParameterOfType(method, Sort.class)) {
 				throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter;"
 						+ " Use sorting capabilities on Pageable instead; Offending method: %s", method));
 			}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
index 5787cca5a5..4aa773091b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
@@ -28,14 +28,11 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.TextCriteria;
 import org.springframework.data.repository.query.QueryMethod;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ReturnedType;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.repository.query.parser.PartTree;
-import org.springframework.expression.ExpressionParser;
 import org.springframework.util.StringUtils;
 
 /**
@@ -52,26 +49,6 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
 	private final MappingContext<?, MongoPersistentProperty> context;
 	private final ResultProcessor processor;
 
-	/**
-	 * Creates a new {@link ReactivePartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
-			ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
-		super(method, mongoOperations, expressionParser, evaluationContextProvider);
-
-		this.processor = method.getResultProcessor();
-		this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
-		this.isGeoNearQuery = method.isGeoNearQuery();
-		this.context = mongoOperations.getConverter().getMappingContext();
-	}
-
 	/**
 	 * Creates a new {@link ReactivePartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}.
 	 *
@@ -110,6 +87,7 @@ protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) {
 		return Mono.fromSupplier(() -> createQueryInternal(accessor, true));
 	}
 
+	@SuppressWarnings("NullAway")
 	private Query createQueryInternal(ConvertingParameterAccessor accessor, boolean isCountQuery) {
 
 		MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, !isCountQuery && isGeoNearQuery);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
index ff01d8f8a3..ebc33cef96 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
@@ -21,6 +21,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -29,12 +30,9 @@
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.util.ReflectionUtils;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.lang.Nullable;
 
 /**
  * A reactive {@link org.springframework.data.repository.query.RepositoryQuery} to use a plain JSON String to create an
@@ -49,24 +47,6 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
 	private final ReactiveMongoOperations reactiveMongoOperations;
 	private final MongoConverter mongoConverter;
 
-	/**
-	 * @param method must not be {@literal null}.
-	 * @param reactiveMongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method,
-			ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser,
-			ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
-
-		super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider);
-
-		this.reactiveMongoOperations = reactiveMongoOperations;
-		this.mongoConverter = reactiveMongoOperations.getConverter();
-	}
-
 	/**
 	 * @param method must not be {@literal null}.
 	 * @param reactiveMongoOperations must not be {@literal null}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
index 0e980fcfaf..4bfe2ca39f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
@@ -15,12 +15,14 @@
  */
 package org.springframework.data.mongodb.repository.query;
 
+import org.jspecify.annotations.Nullable;
+import org.springframework.lang.Contract;
 import reactor.core.publisher.Mono;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.bson.Document;
-
+import org.jspecify.annotations.NonNull;
 import org.springframework.data.expression.ValueExpressionParser;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -28,13 +30,8 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.util.json.ParameterBindingContext;
 import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
-import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.spel.ExpressionDependencies;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.NonNull;
 import org.springframework.util.Assert;
 
 /**
@@ -50,7 +47,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
 	private static final Log LOG = LogFactory.getLog(ReactiveStringBasedMongoQuery.class);
 
 	private final String query;
-	private final String fieldSpec;
+	private final @Nullable String fieldSpec;
 
 	private final ValueExpressionParser expressionParser;
 
@@ -59,73 +56,15 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
 	private final boolean isDeleteQuery;
 
 	/**
-	 * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod} and
-	 * {@link MongoOperations}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
-			ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
-		this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
-	}
-
-	/**
-	 * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link String}, {@link MongoQueryMethod},
-	 * {@link MongoOperations}, {@link SpelExpressionParser} and
-	 * {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider}.
-	 *
-	 * @param query must not be {@literal null}.
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
-			ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser,
-			ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
-		super(method, mongoOperations, expressionParser, evaluationContextProvider);
-
-		Assert.notNull(query, "Query must not be null");
-
-		this.query = query;
-		this.expressionParser = ValueExpressionParser.create(() -> expressionParser);
-		this.fieldSpec = method.getFieldSpecification();
-
-		if (method.hasAnnotatedQuery()) {
-
-			org.springframework.data.mongodb.repository.Query queryAnnotation = method.getQueryAnnotation();
-
-			this.isCountQuery = queryAnnotation.count();
-			this.isExistsQuery = queryAnnotation.exists();
-			this.isDeleteQuery = queryAnnotation.delete();
-
-			if (hasAmbiguousProjectionFlags(this.isCountQuery, this.isExistsQuery, this.isDeleteQuery)) {
-				throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method));
-			}
-
-		} else {
-
-			this.isCountQuery = false;
-			this.isExistsQuery = false;
-			this.isDeleteQuery = false;
-		}
-	}
-
-	/**
-	 * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod},
-	 * {@link MongoOperations} and {@link ValueExpressionDelegate}.
+	 * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations}
+	 * and {@link ValueExpressionDelegate}.
 	 *
 	 * @param method must not be {@literal null}.
 	 * @param mongoOperations must not be {@literal null}.
 	 * @param delegate must not be {@literal null}.
 	 * @since 4.4.0
 	 */
+	@SuppressWarnings("NullAway")
 	public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
 			ValueExpressionDelegate delegate) {
 		this(method.getAnnotatedQuery(), method, mongoOperations, delegate);
@@ -141,7 +80,8 @@ public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMo
 	 * @param delegate must not be {@literal null}.
 	 * @since 4.4.0
 	 */
-	public ReactiveStringBasedMongoQuery(@NonNull String query, ReactiveMongoQueryMethod method,
+	@SuppressWarnings("NullAway")
+	public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
 			ReactiveMongoOperations mongoOperations, ValueExpressionDelegate delegate) {
 
 		super(method, mongoOperations, delegate);
@@ -195,7 +135,7 @@ protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) {
 		});
 	}
 
-	private Mono<ParameterBindingContext> getBindingContext(String json, ConvertingParameterAccessor accessor,
+	private Mono<ParameterBindingContext> getBindingContext(@Nullable String json, ConvertingParameterAccessor accessor,
 			ParameterBindingDocumentCodec codec) {
 
 		ExpressionDependencies dependencies = codec.captureExpressionDependencies(json, accessor::getBindableValue,
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java
index 724c8f29ef..289b953b27 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java
@@ -20,9 +20,9 @@
 import java.util.regex.Pattern;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
 import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
-import org.springframework.lang.Nullable;
 
 /**
  * String-based aggregation operation for a repository query method.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
index 7ad5d78fa6..3f6a48e84c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
@@ -20,7 +20,7 @@
 import java.util.stream.Stream;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.SliceImpl;
 import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
@@ -29,13 +29,9 @@
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
 import org.springframework.data.repository.query.ResultProcessor;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.util.ReflectionUtils;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link AbstractMongoQuery} implementation to run string-based aggregations using
@@ -51,30 +47,6 @@ public class StringBasedAggregation extends AbstractMongoQuery {
 	private final MongoOperations mongoOperations;
 	private final MongoConverter mongoConverter;
 
-	/**
-	 * Creates a new {@link StringBasedAggregation} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations,
-			ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
-		super(method, mongoOperations, expressionParser, evaluationContextProvider);
-
-		if (method.isPageQuery()) {
-			throw new InvalidMongoDbApiUsageException(String.format(
-					"Repository aggregation method '%s' does not support '%s' return type; Please use 'Slice' or 'List' instead",
-					method.getName(), method.getReturnType().getType().getSimpleName()));
-		}
-
-		this.mongoOperations = mongoOperations;
-		this.mongoConverter = mongoOperations.getConverter();
-	}
-
 	/**
 	 * Creates a new {@link StringBasedAggregation} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
 	 *
@@ -99,8 +71,7 @@ public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOper
 
 	@SuppressWarnings("unchecked")
 	@Override
-	@Nullable
-	protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor,
+	protected @Nullable Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor,
 			@Nullable Class<?> ignore) {
 
 		return AggregationUtils.doAggregate(AggregationUtils.computePipeline(this, method, accessor), method, processor,
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
index abc158f88a..c990d3269d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
@@ -22,11 +22,8 @@
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.query.BasicQuery;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
-import org.springframework.expression.ExpressionParser;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.util.Assert;
 
 /**
@@ -49,47 +46,6 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
 	private final boolean isExistsQuery;
 	private final boolean isDeleteQuery;
 
-	/**
-	 * Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations},
-	 * {@link SpelExpressionParser} and {@link QueryMethodEvaluationContextProvider}.
-	 *
-	 * @param method must not be {@literal null}.
-	 * @param mongoOperations must not be {@literal null}.
-	 * @param expressionParser must not be {@literal null}.
-	 * @param evaluationContextProvider must not be {@literal null}.
-	 * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
-			ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
-		super(method, mongoOperations, expressionParser, evaluationContextProvider);
-
-		String query = method.getAnnotatedQuery();
-		Assert.notNull(query, "Query must not be null");
-
-		this.query = query;
-		this.fieldSpec = method.getFieldSpecification();
-
-		if (method.hasAnnotatedQuery()) {
-
-			org.springframework.data.mongodb.repository.Query queryAnnotation = method.getQueryAnnotation();
-
-			this.isCountQuery = queryAnnotation.count();
-			this.isExistsQuery = queryAnnotation.exists();
-			this.isDeleteQuery = queryAnnotation.delete();
-
-			if (hasAmbiguousProjectionFlags(this.isCountQuery, this.isExistsQuery, this.isDeleteQuery)) {
-				throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method));
-			}
-
-		} else {
-
-			this.isCountQuery = false;
-			this.isExistsQuery = false;
-			this.isDeleteQuery = false;
-		}
-	}
-
 	/**
 	 * Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations},
 	 * {@link ValueExpressionDelegate}.
@@ -99,6 +55,7 @@ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOpera
 	 * @param expressionSupport must not be {@literal null}.
 	 * @since 4.4.0
 	 */
+	@SuppressWarnings("NullAway")
 	public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
 			ValueExpressionDelegate expressionSupport) {
 		this(method.getAnnotatedQuery(), method, mongoOperations, expressionSupport);
@@ -114,6 +71,7 @@ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOpera
 	 * @param expressionSupport must not be {@literal null}.
 	 * @since 4.3
 	 */
+	@SuppressWarnings("NullAway")
 	public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations,
 			ValueExpressionDelegate expressionSupport) {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java
index c479f3faa9..360f5e80eb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java
@@ -17,6 +17,7 @@
 
 import java.util.function.Function;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.expression.ValueEvaluationContext;
 import org.springframework.data.expression.ValueExpression;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
@@ -34,7 +35,7 @@ class ValueExpressionDelegateValueExpressionEvaluator implements ValueExpression
 
 	@SuppressWarnings("unchecked")
 	@Override
-	public <T> T evaluate(String expressionString) {
+	public <T> @Nullable T evaluate(String expressionString) {
 		ValueExpression expression = delegate.parse(expressionString);
 		return (T) expression.evaluate(expressionToContext.apply(expression));
 	}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java
index 20c77e22aa..5f0cc21049 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Query derivation mechanism for MongoDB specific repositories.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository.query;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java
index f59a995170..abd828a9f7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java
@@ -25,6 +25,7 @@
 
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
+import org.jspecify.annotations.Nullable;
 import org.springframework.aop.TargetSource;
 import org.springframework.aop.framework.ProxyFactory;
 import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -32,7 +33,6 @@
 import org.springframework.core.annotation.AnnotatedElementUtils;
 import org.springframework.data.repository.core.RepositoryInformation;
 import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
-import org.springframework.lang.Nullable;
 import org.springframework.transaction.support.TransactionSynchronizationManager;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
@@ -54,7 +54,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B
 	private @Nullable ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
 
 	@Override
-	public void setBeanClassLoader(ClassLoader classLoader) {
+	public void setBeanClassLoader(@Nullable ClassLoader classLoader) {
 		this.classLoader = classLoader;
 	}
 
@@ -121,7 +121,7 @@ static MethodInvocation currentInvocation() throws IllegalStateException {
 		}
 
 		@Override
-		public Object invoke(MethodInvocation invocation) throws Throwable {
+		public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {
 
 			Method method = invocation.getMethod();
 
@@ -220,7 +220,7 @@ public boolean isStatic() {
 		}
 
 		@Override
-		public Object getTarget() {
+		public @Nullable Object getTarget() {
 
 			MethodInvocation invocation = CrudMethodMetadataPopulatingMethodInterceptor.currentInvocation();
 			return TransactionSynchronizationManager.getResource(invocation.getMethod());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java
index 1d876289be..443108d2f0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java
@@ -16,12 +16,12 @@
 package org.springframework.data.mongodb.repository.support;
 
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.query.Collation;
 import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
 import org.springframework.data.repository.core.support.PersistentEntityInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link MongoEntityInformation} implementation using a {@link MongoPersistentEntity} instance to lookup the necessary
@@ -113,7 +113,7 @@ public boolean isVersioned() {
 	}
 
 	@Override
-	public Object getVersion(T entity) {
+	public @Nullable Object getVersion(T entity) {
 
 		if (!isVersioned()) {
 			return null;
@@ -124,8 +124,7 @@ public Object getVersion(T entity) {
 		return accessor.getProperty(this.entityMetadata.getRequiredVersionProperty());
 	}
 
-	@Nullable
-	public Collation getCollation() {
+	public @Nullable Collation getCollation() {
 		return this.entityMetadata.getCollation();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java
index 3c029ee5aa..6deee469e1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java
@@ -22,9 +22,8 @@
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.SourceVersion;
 import javax.tools.Diagnostic;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.Document;
-import org.springframework.lang.Nullable;
 
 import com.querydsl.apt.AbstractQuerydslProcessor;
 import com.querydsl.apt.Configuration;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java
index d0a3f7a1e4..1a39198757 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java
@@ -15,9 +15,9 @@
  */
 package org.springframework.data.mongodb.repository.support;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
index baf069c3a4..e1abcdc2ab 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
@@ -21,6 +21,7 @@
 import java.lang.reflect.Method;
 import java.util.Optional;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.data.mapping.context.MappingContext;
@@ -42,10 +43,8 @@
 import org.springframework.data.repository.core.support.RepositoryFactorySupport;
 import org.springframework.data.repository.query.QueryLookupStrategy;
 import org.springframework.data.repository.query.QueryLookupStrategy.Key;
-import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -61,7 +60,6 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
 	private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor();
 	private final MongoOperations operations;
 	private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
-	@Nullable private QueryMethodValueEvaluationContextAccessor accessor;
 
 	/**
 	 * Creates a new {@link MongoRepositoryFactory} with the given {@link MongoOperations}.
@@ -79,14 +77,14 @@ public MongoRepositoryFactory(MongoOperations mongoOperations) {
 	}
 
 	@Override
-	public void setBeanClassLoader(ClassLoader classLoader) {
+	public void setBeanClassLoader(@Nullable ClassLoader classLoader) {
 
 		super.setBeanClassLoader(classLoader);
 		crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader);
 	}
 
 	@Override
-	protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) {
+	protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader, @Nullable BeanFactory beanFactory) {
 		return this.operations.getConverter().getProjectionFactory();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java
index c98d38c5f5..cec54de0bb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java
@@ -17,13 +17,13 @@
 
 import java.io.Serializable;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.repository.Repository;
 import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
 import org.springframework.data.repository.core.support.RepositoryFactorySupport;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -31,6 +31,7 @@
  *
  * @author Oliver Gierke
  */
+@SuppressWarnings("NullAway")
 public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
 		extends RepositoryFactoryBeanSupport<T, S, ID> {
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
index ec845510ce..833ce69458 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java
@@ -23,6 +23,7 @@
 
 import org.bson.Document;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -245,12 +246,12 @@ protected <R> FluentQuerydsl<R> create(Predicate predicate, Sort sort, int limit
 		}
 
 		@Override
-		public T oneValue() {
+		public @Nullable T oneValue() {
 			return createQuery().fetchOne();
 		}
 
 		@Override
-		public T firstValue() {
+		public @Nullable T firstValue() {
 			return createQuery().fetchFirst();
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java
index 3edfcdd2db..ae8561bc17 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java
@@ -21,7 +21,7 @@
 import java.lang.reflect.Method;
 import java.util.Optional;
 
-import org.springframework.beans.BeansException;
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -44,10 +44,8 @@
 import org.springframework.data.repository.query.QueryLookupStrategy;
 import org.springframework.data.repository.query.QueryLookupStrategy.Key;
 import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.RepositoryQuery;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -77,19 +75,19 @@ public ReactiveMongoRepositoryFactory(ReactiveMongoOperations mongoOperations) {
 		this.operations = mongoOperations;
 		this.mappingContext = mongoOperations.getConverter().getMappingContext();
 
-		setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
 		addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor);
 	}
 
 	@Override
-	public void setBeanClassLoader(ClassLoader classLoader) {
+	public void setBeanClassLoader(@Nullable ClassLoader classLoader) {
 
 		super.setBeanClassLoader(classLoader);
 		crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader);
 	}
 
 	@Override
-	protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) {
+	@SuppressWarnings("NullAway")
+	protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader, @Nullable BeanFactory beanFactory) {
 		return this.operations.getConverter().getProjectionFactory();
 	}
 
@@ -132,7 +130,9 @@ protected Object getTargetRepository(RepositoryInformation information) {
 		return targetRepository;
 	}
 
-	@Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
+	@Override
+	@SuppressWarnings("NullAway")
+	protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
 			ValueExpressionDelegate valueExpressionDelegate) {
 		return Optional.of(new MongoQueryLookupStrategy(operations, mappingContext, valueExpressionDelegate));
 	}
@@ -159,8 +159,8 @@ private <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> doma
 	 * @author Christoph Strobl
 	 */
 	private record MongoQueryLookupStrategy(ReactiveMongoOperations operations,
-		MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
-		ValueExpressionDelegate delegate) implements QueryLookupStrategy {
+			MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
+			ValueExpressionDelegate delegate) implements QueryLookupStrategy {
 
 		@Override
 		public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java
index 4f9c0d945c..e3d71325f9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java
@@ -16,18 +16,14 @@
 package org.springframework.data.mongodb.repository.support;
 
 import java.io.Serializable;
-import java.util.Optional;
 
-import org.springframework.beans.factory.ListableBeanFactory;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
 import org.springframework.data.mongodb.core.index.IndexOperationsAdapter;
 import org.springframework.data.repository.Repository;
 import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
 import org.springframework.data.repository.core.support.RepositoryFactorySupport;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -82,6 +78,7 @@ public void setMappingContext(MappingContext<?, ?> mappingContext) {
 	}
 
 	@Override
+	@SuppressWarnings("NullAway")
 	protected RepositoryFactorySupport createRepositoryFactory() {
 
 		RepositoryFactorySupport factory = getFactoryInstance(operations);
@@ -94,12 +91,6 @@ protected RepositoryFactorySupport createRepositoryFactory() {
 		return factory;
 	}
 
-	@Override
-	protected Optional<QueryMethodEvaluationContextProvider> createDefaultQueryMethodEvaluationContextProvider(
-			ListableBeanFactory beanFactory) {
-		return Optional.of(new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(beanFactory));
-	}
-
 	/**
 	 * Creates and initializes a {@link RepositoryFactorySupport} instance.
 	 *
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java
index cf5191fd42..a86ada0aad 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java
@@ -25,6 +25,7 @@
 import java.util.function.Consumer;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.ScrollPosition;
@@ -36,7 +37,6 @@
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.query.BasicQuery;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
@@ -304,7 +304,7 @@ static class NoMatchException extends RuntimeException {
 
 		@Override
 		public synchronized Throwable fillInStackTrace() {
-			return null;
+			return this;
 		}
 	}
 }
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
index 2f4c30ee7a..7e6e2fc82e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java
@@ -27,6 +27,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.OptimisticLockingFailureException;
 import org.springframework.data.domain.Example;
 import org.springframework.data.domain.Page;
@@ -47,7 +48,6 @@
 import org.springframework.data.support.PageableExecutionUtils;
 import org.springframework.data.util.StreamUtils;
 import org.springframework.data.util.Streamable;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadPreference;
@@ -427,12 +427,12 @@ protected <R> FluentQueryByExample<S, R> create(Example<S> predicate, Sort sort,
 		}
 
 		@Override
-		public T oneValue() {
+		public @Nullable T oneValue() {
 			return createQuery().oneValue();
 		}
 
 		@Override
-		public T firstValue() {
+		public @Nullable T firstValue() {
 			return createQuery().firstValue();
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java
index 1c1df2c9a1..7e4a3aa665 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java
@@ -30,6 +30,7 @@
 import java.util.function.Function;
 import java.util.function.UnaryOperator;
 
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
 
 import org.springframework.dao.IncorrectResultSizeDataAccessException;
@@ -49,7 +50,6 @@
 import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
 import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
 import org.springframework.data.repository.query.FluentQuery;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 import com.mongodb.ReadPreference;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
index 0ef6c38744..809dbab8cf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java
@@ -22,21 +22,18 @@
 import java.util.stream.Stream;
 
 import org.bson.Document;
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.ScrollPosition;
-import org.springframework.data.domain.Slice;
 import org.springframework.data.domain.Window;
 import org.springframework.data.mongodb.core.ExecutableFindOperation;
 import org.springframework.data.mongodb.core.MongoOperations;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.query.BasicQuery;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.mongodb.repository.util.SliceUtils;
 import org.springframework.data.support.PageableExecutionUtils;
-import org.springframework.lang.Nullable;
 
 import com.mysema.commons.lang.CloseableIterator;
 import com.mysema.commons.lang.EmptyCloseableIterator;
@@ -47,6 +44,7 @@
 import com.querydsl.core.types.Expression;
 import com.querydsl.core.types.OrderSpecifier;
 import com.querydsl.core.types.Predicate;
+import org.springframework.lang.Contract;
 
 /**
  * Spring Data specific simple {@link com.querydsl.core.Fetchable} {@link com.querydsl.core.SimpleQuery Query}
@@ -185,22 +183,8 @@ public Page<T> fetchPage(Pageable pageable) {
 		}
 	}
 
-	/**
-	 * Fetch a {@link Slice}.
-	 *
-	 * @param pageable defines range and sort of requested slice
-	 * @return new instance of {@link Slice} containing matching results within range.
-	 * @since 4.5
-	 */
-	public Slice<T> fetchSlice(Pageable pageable) {
-
-		List<T> content = find.matching(SliceUtils.limitResult(createQuery(), pageable).with(pageable.getSort())).all();
-
-		return SliceUtils.sliceResult(content, pageable);
-	}
-
 	@Override
-	public T fetchFirst() {
+	public @Nullable T fetchFirst() {
 		try {
 			return find.matching(createQuery()).firstValue();
 		} catch (RuntimeException e) {
@@ -209,7 +193,7 @@ public T fetchFirst() {
 	}
 
 	@Override
-	public T fetchOne() {
+	public @Nullable T fetchOne() {
 		try {
 			return find.matching(createQuery()).oneValue();
 		} catch (RuntimeException e) {
@@ -279,7 +263,8 @@ protected List<Object> getIds(Class<?> targetType, Predicate condition) {
 		return mongoOperations.findDistinct(query, FieldName.ID.name(), targetType, Object.class);
 	}
 
-	private static <T> T handleException(RuntimeException e, T defaultValue) {
+	@Contract("_, !null -> !null")
+	private static <T> @Nullable T handleException(RuntimeException e, @Nullable T defaultValue) {
 
 		if (e.getClass().getName().endsWith("$NoResults")) {
 			return defaultValue;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
index d9a550a0f7..756d04d0c2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java
@@ -20,6 +20,8 @@
 import java.util.regex.Pattern;
 
 import org.bson.Document;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.context.MappingContext;
 import org.springframework.data.mongodb.core.convert.MongoConverter;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
@@ -27,7 +29,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 
@@ -48,6 +49,7 @@
  * @author Christoph Strobl
  * @author Mark Paluch
  */
+@NullUnmarked
 class SpringDataMongodbSerializer extends MongodbDocumentSerializer {
 
 	private static final String ID_KEY = FieldName.ID.name();
@@ -146,8 +148,7 @@ protected boolean isId(Path<?> arg) {
 	}
 
 	@Override
-	@Nullable
-	protected Object convert(@Nullable Path<?> path, @Nullable Constant<?> constant) {
+	protected @Nullable Object convert(@Nullable Path<?> path, @Nullable Constant<?> constant) {
 
 		if (constant == null) {
 			return null;
@@ -191,8 +192,7 @@ protected Object convert(@Nullable Path<?> path, @Nullable Constant<?> constant)
 		return asReference(constant.getConstant(), path);
 	}
 
-	@Nullable
-	private MongoPersistentProperty getPropertyFor(Path<?> path) {
+	private @Nullable MongoPersistentProperty getPropertyFor(Path<?> path) {
 
 		Path<?> parent = path.getMetadata().getParent();
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java
index 1d0b8beeba..42cd5a0b18 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java
@@ -1,6 +1,6 @@
 /**
  * Support infrastructure for query derivation of MongoDB specific repositories.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.repository.support;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
index cbbd4a37a9..83df5f7798 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
@@ -40,11 +40,12 @@
 import org.bson.types.Binary;
 import org.bson.types.Decimal128;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.mongodb.CodecRegistryProvider;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.mongodb.core.mapping.FieldName.Type;
-import org.springframework.lang.Nullable;
+import org.springframework.lang.Contract;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.CollectionUtils;
@@ -73,8 +74,8 @@ public class BsonUtils {
 	public static final Document EMPTY_DOCUMENT = new EmptyDocument();
 
 	@SuppressWarnings("unchecked")
-	@Nullable
-	public static <T> T get(Bson bson, String key) {
+	@Contract("null, _ -> null")
+	public static <T> @Nullable T get(@Nullable Bson bson, String key) {
 		return (T) asMap(bson).get(key);
 	}
 
@@ -85,7 +86,7 @@ public static <T> T get(Bson bson, String key) {
 	 * @param bson
 	 * @return
 	 */
-	public static Map<String, Object> asMap(Bson bson) {
+	public static Map<String, Object> asMap(@Nullable Bson bson) {
 		return asMap(bson, MongoClientSettings.getDefaultCodecRegistry());
 	}
 
@@ -126,7 +127,7 @@ public static Map<String, Object> asMap(@Nullable Bson bson, CodecRegistry codec
 	 * @return
 	 * @since 3.2.5
 	 */
-	public static Document asDocument(Bson bson) {
+	public static Document asDocument(@Nullable Bson bson) {
 		return asDocument(bson, MongoClientSettings.getDefaultCodecRegistry());
 	}
 
@@ -140,7 +141,7 @@ public static Document asDocument(Bson bson) {
 	 * @return never {@literal null}.
 	 * @since 4.0
 	 */
-	public static Document asDocument(Bson bson, CodecRegistry codecRegistry) {
+	public static Document asDocument(@Nullable Bson bson, CodecRegistry codecRegistry) {
 
 		Map<String, Object> map = asMap(bson, codecRegistry);
 
@@ -326,14 +327,14 @@ public static Object toJavaType(BsonValue value) {
 	 * @throws IllegalArgumentException if {@literal source} does not correspond to a {@link BsonValue} type.
 	 * @since 3.0
 	 */
-	public static BsonValue simpleToBsonValue(Object source) {
+	public static BsonValue simpleToBsonValue(@Nullable Object source) {
 		return simpleToBsonValue(source, MongoClientSettings.getDefaultCodecRegistry());
 	}
 
 	/**
 	 * Convert a given simple value (eg. {@link String}, {@link Long}) to its corresponding {@link BsonValue}.
 	 *
-	 * @param source must not be {@literal null}.
+	 * @param source can be {@literal null}.
 	 * @param codecRegistry The {@link CodecRegistry} used as a fallback to convert types using native {@link Codec}. Must
 	 *          not be {@literal null}.
 	 * @return the corresponding {@link BsonValue} representation.
@@ -341,7 +342,12 @@ public static BsonValue simpleToBsonValue(Object source) {
 	 * @since 4.2
 	 */
 	@SuppressWarnings("unchecked")
-	public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegistry) {
+	@Contract("null, _ -> !null")
+	public static BsonValue simpleToBsonValue(@Nullable Object source, CodecRegistry codecRegistry) {
+
+		if(source == null) {
+			return BsonNull.VALUE;
+		}
 
 		if (source instanceof BsonValue bsonValue) {
 			return bsonValue;
@@ -398,7 +404,9 @@ public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegi
 			BsonCapturingWriter writer = new BsonCapturingWriter(value.getClass());
 			codec.encode(writer, value,
 					ObjectUtils.isArray(value) || value instanceof Collection<?> ? EncoderContext.builder().build() : null);
-			return writer.getCapturedValue();
+			Object captured = writer.getCapturedValue();
+			return captured instanceof BsonValue bv ? bv : BsonNull.VALUE;
+
 		} catch (CodecConfigurationException e) {
 			throw new IllegalArgumentException(
 					String.format("Unable to convert %s to BsonValue.", source != null ? source.getClass().getName() : "null"));
@@ -450,8 +458,7 @@ public static Document toDocumentOrElse(String source, Function<String, Document
 	 * @return
 	 * @since 2.2.1
 	 */
-	@Nullable
-	public static String toJson(@Nullable Document source) {
+	public static @Nullable String toJson(@Nullable Document source) {
 
 		if (source == null) {
 			return null;
@@ -471,6 +478,7 @@ public static String toJson(@Nullable Document source) {
 	 * @return {@literal true} if the given value looks like a json document.
 	 * @since 3.0
 	 */
+	@Contract("null -> false")
 	public static boolean isJsonDocument(@Nullable String value) {
 
 		if (!StringUtils.hasText(value)) {
@@ -488,6 +496,7 @@ public static boolean isJsonDocument(@Nullable String value) {
 	 * @return {@literal true} if the given value looks like a json array.
 	 * @since 3.0
 	 */
+	@Contract("null -> false")
 	public static boolean isJsonArray(@Nullable String value) {
 		return StringUtils.hasText(value) && (value.startsWith("[") && value.endsWith("]"));
 	}
@@ -525,8 +534,7 @@ public static Document parse(String json, @Nullable CodecRegistryProvider codecR
 	 * @return can be {@literal null}.
 	 * @since 3.0.8
 	 */
-	@Nullable
-	public static Object resolveValue(Bson bson, String key) {
+	public static @Nullable Object resolveValue(Bson bson, String key) {
 		return resolveValue(asMap(bson), key);
 	}
 
@@ -541,7 +549,7 @@ public static Object resolveValue(Bson bson, String key) {
 	 * @return can be {@literal null}.
 	 * @since 4.2
 	 */
-	public static Object resolveValue(Bson bson, FieldName fieldName) {
+	public static @Nullable Object resolveValue(Bson bson, FieldName fieldName) {
 		return resolveValue(asMap(bson), fieldName);
 	}
 
@@ -556,8 +564,7 @@ public static Object resolveValue(Bson bson, FieldName fieldName) {
 	 * @return can be {@literal null}.
 	 * @since 4.2
 	 */
-	@Nullable
-	public static Object resolveValue(Map<String, Object> source, FieldName fieldName) {
+	public static @Nullable Object resolveValue(Map<String, Object> source, FieldName fieldName) {
 
 		if (fieldName.isKey()) {
 			return source.get(fieldName.name());
@@ -590,8 +597,7 @@ public static Object resolveValue(Map<String, Object> source, FieldName fieldNam
 	 * @return can be {@literal null}.
 	 * @since 4.1
 	 */
-	@Nullable
-	public static Object resolveValue(Map<String, Object> source, String key) {
+	public static @Nullable Object resolveValue(Map<String, Object> source, String key) {
 
 		if (source.containsKey(key)) {
 			return source.get(key);
@@ -643,9 +649,9 @@ public static boolean hasValue(Bson bson, String key) {
 	 * @param source can be {@literal null}.
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
 	@SuppressWarnings("unchecked")
-	private static Map<String, Object> getAsMap(Object source) {
+	@Contract("null -> null")
+	private static @Nullable Map<String, Object> getAsMap(@Nullable Object source) {
 
 		if (source instanceof Document document) {
 			return document;
@@ -745,8 +751,8 @@ public static Document mapEntries(Document source, Function<Entry<String, Object
 		return new Document(target);
 	}
 
-	@Nullable
-	private static String toJson(@Nullable Object value) {
+	@Contract("null -> null")
+	private static @Nullable String toJson(@Nullable Object value) {
 
 		if (value == null) {
 			return null;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java
index 191c7d24d3..549c7ff720 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.util;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.StringUtils;
 
 /**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java
index 67255b878a..78eb59a461 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java
@@ -18,6 +18,7 @@
 import java.time.Duration;
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.env.Environment;
 import org.springframework.data.expression.ValueEvaluationContext;
 import org.springframework.data.expression.ValueExpression;
@@ -25,7 +26,6 @@
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.format.datetime.standard.DurationFormatterUtils;
-import org.springframework.lang.Nullable;
 
 /**
  * Helper to evaluate Duration from expressions.
@@ -70,13 +70,11 @@ public static Duration evaluate(String value, ValueEvaluationContext evaluationC
 	public static Duration evaluate(String value, Supplier<EvaluationContext> evaluationContext) {
 
 		return evaluate(value, new ValueEvaluationContext() {
-			@Nullable
 			@Override
 			public Environment getEnvironment() {
-				return null;
+				throw new IllegalStateException();
 			}
 
-			@Nullable
 			@Override
 			public EvaluationContext getEvaluationContext() {
 				return evaluationContext.get();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
index ffc97402fe..23ea9409cc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
@@ -66,9 +66,8 @@ public boolean replace(String key, Object oldValue, Object newValue) {
 		throw new UnsupportedOperationException();
 	}
 
-	@Nullable
 	@Override
-	public Object replace(String key, Object value) {
+	public @Nullable Object replace(String key, Object value) {
 		throw new UnsupportedOperationException();
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java
index 991a7292fd..fbbba59e8f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb.util;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.util.Version;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.internal.build.MongoDriverVersion;
@@ -91,8 +91,7 @@ private static Version getMongoDbDriverVersion(ClassLoader classLoader) {
 		return version == null ? guessDriverVersionFromClassPath(classLoader) : version;
 	}
 
-	@Nullable
-	private static Version getVersionFromPackage(ClassLoader classLoader) {
+	private static @Nullable Version getVersionFromPackage(ClassLoader classLoader) {
 
 		if (ClassUtils.isPresent("com.mongodb.internal.build.MongoDriverVersion", classLoader)) {
 			try {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java
index f85be98c1f..ba1e32356a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java
@@ -20,8 +20,8 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.Nullable;
 import org.reactivestreams.Publisher;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ReflectionUtils;
@@ -44,6 +44,7 @@
  * @author Christoph Strobl
  * @since 4.3
  */
+@SuppressWarnings("NullAway")
 public class MongoCompatibilityAdapter {
 
 	private static final String NO_LONGER_SUPPORTED = "%s is no longer supported on Mongo Client 5 or newer";
@@ -174,8 +175,7 @@ public interface IndexOptionsAdapter {
 	}
 
 	public interface ClientSettingsAdapter {
-		@Nullable
-		<T> T getStreamFactoryFactory();
+		<T> @Nullable T getStreamFactoryFactory();
 	}
 
 	public interface ClientSettingsBuilderAdapter {
@@ -199,14 +199,12 @@ public interface MongoDatabaseAdapterBuilder {
 		MongoDatabaseAdapter forDb(com.mongodb.client.MongoDatabase db);
 	}
 
-	@SuppressWarnings({ "unchecked", "DataFlowIssue" })
+	@SuppressWarnings({ "unchecked", "DataFlowIssue", "NullAway" })
 	public static class MongoDatabaseAdapter {
 
-		@Nullable //
-		private static final Method LIST_COLLECTION_NAMES_METHOD;
+		private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD;
 
-		@Nullable //
-		private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION;
+		private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD_SESSION;
 
 		private static final Class<?> collectionNamesReturnType;
 
@@ -271,11 +269,9 @@ public interface ReactiveMongoDatabaseAdapterBuilder {
 	@SuppressWarnings({ "unchecked", "DataFlowIssue" })
 	public static class ReactiveMongoDatabaseAdapter {
 
-		@Nullable //
-		private static final Method LIST_COLLECTION_NAMES_METHOD;
+		private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD;
 
-		@Nullable //
-		private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION;
+		private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD_SESSION;
 
 		private static final Class<?> collectionNamesReturnType;
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java
index 326a5c1e88..7fcc1383d5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java
@@ -15,12 +15,14 @@
  */
 package org.springframework.data.mongodb.util;
 
+import java.util.Collections;
 import java.util.HashMap;
-
-import org.springframework.lang.Nullable;
+import java.util.Map;
 
 import com.mongodb.MongoException;
 
+import org.jspecify.annotations.Nullable;
+
 /**
  * {@link MongoDbErrorCodes} holds MongoDB specific error codes outlined in {@literal mongo/base/error_codes.yml}.
  *
@@ -128,7 +130,9 @@ public final class MongoDbErrorCodes {
 		clientSessionCodes.put(263, "OperationNotSupportedInTransaction");
 		clientSessionCodes.put(264, "TooManyLogicalSessions");
 
-		errorCodes = new HashMap<>(
+		transactionCodes = new HashMap<>(0);
+
+ 		errorCodes = new HashMap<>(
 				dataAccessResourceFailureCodes.size() + dataIntegrityViolationCodes.size() + duplicateKeyCodes.size()
 						+ invalidDataAccessApiUsageException.size() + permissionDeniedCodes.size() + clientSessionCodes.size(),
 				1f);
@@ -140,8 +144,7 @@ public final class MongoDbErrorCodes {
 		errorCodes.putAll(clientSessionCodes);
 	}
 
-	@Nullable
-	public static String getErrorDescription(@Nullable Integer errorCode) {
+	public static @Nullable String getErrorDescription(@Nullable Integer errorCode) {
 		return errorCode == null ? null : errorCodes.get(errorCode);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
index 23c96f9e46..8b0f4b83ce 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
@@ -17,7 +17,7 @@
 
 import java.util.regex.Pattern;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Utility to translate {@link Pattern#flags() regex flags} to MongoDB regex options and vice versa.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java
index 344244717e..950f9ec797 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java
@@ -17,6 +17,7 @@
 
 import org.bson.Document;
 import org.bson.codecs.configuration.CodecRegistry;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.aggregation.Aggregation;
 import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
 import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
@@ -27,7 +28,6 @@
 import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * @author Christoph Strobl
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java
index 9dd3f1d8fb..be9a2e1cff 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java
@@ -22,10 +22,10 @@
 import org.bson.BsonBinary;
 import org.bson.BsonBinarySubType;
 import org.bson.types.Binary;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.util.spel.ExpressionUtils;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -48,8 +48,7 @@ public final class EncryptionUtils {
 	 * @return can be {@literal null}.
 	 * @throws IllegalArgumentException if one of the required arguments is {@literal null}.
 	 */
-	@Nullable
-	public static Object resolveKeyId(String value, Supplier<EvaluationContext> evaluationContext) {
+	public static @Nullable Object resolveKeyId(String value, Supplier<EvaluationContext> evaluationContext) {
 
 		Assert.notNull(value, "Value must not be null");
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java
index b5c26755cf..3961fafc21 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java
@@ -23,6 +23,8 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 
+import org.jspecify.annotations.NullUnmarked;
+
 /**
  * DateTimeFormatter implementation borrowed from <a href=
  * "https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/json/DateTimeFormatter.java">MongoDB
@@ -33,6 +35,7 @@
  * @author Ross Lawley
  * @since 2.2
  */
+@NullUnmarked
 class DateTimeFormatter {
 
 	private static final int DATE_STRING_LENGTH = "1970-01-01".length();
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java
index 6c31a9721f..57fecd284c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java
@@ -18,12 +18,12 @@
 import java.util.Collections;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
-import org.springframework.lang.Nullable;
 
 /**
  * @author Christoph Strobl
@@ -40,9 +40,8 @@ class EvaluationContextExpressionEvaluator implements ValueExpressionEvaluator {
 		this.expressionParser = expressionParser;
 	}
 
-	@Nullable
 	@Override
-	public <T> T evaluate(String expression) {
+	public <T> @Nullable T evaluate(String expression) {
 		return evaluateExpression(expression, Collections.emptyMap());
 	}
 
@@ -55,7 +54,7 @@ Expression getParsedExpression(String expressionString) {
 	}
 
 	@SuppressWarnings("unchecked")
-	<T> T evaluateExpression(String expressionString, Map<String, Object> variables) {
+	<T> @Nullable T evaluateExpression(String expressionString, Map<String, Object> variables) {
 
 		Expression expression = getParsedExpression(expressionString);
 		EvaluationContext ctx = getEvaluationContext(expressionString);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java
index 4b4b497dae..dcb9a3ff13 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java
@@ -16,6 +16,7 @@
 package org.springframework.data.mongodb.util.json;
 
 import org.bson.json.JsonParseException;
+import org.jspecify.annotations.NullUnmarked;
 
 /**
  * JsonBuffer implementation borrowed from <a href=
@@ -27,6 +28,7 @@
  * @author Ross Lawley
  * @since 2.2
  */
+@NullUnmarked
 class JsonBuffer {
 
 	private final String buffer;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java
index ca4fbddd60..461e52489f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java
@@ -17,6 +17,7 @@
 
 import org.bson.BsonRegularExpression;
 import org.bson.json.JsonParseException;
+import org.jspecify.annotations.NullUnmarked;
 
 /**
  * Parses the string representation of a JSON object into a set of {@link JsonToken}-derived objects. <br />
@@ -32,6 +33,7 @@
  * @author Christoph Strobl
  * @since 2.2
  */
+@NullUnmarked
 class JsonScanner {
 
 	private final JsonBuffer buffer;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java
index 293736123e..e73d57774b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java
@@ -20,6 +20,7 @@
 import org.bson.BsonDouble;
 import org.bson.json.JsonParseException;
 import org.bson.types.Decimal128;
+import org.jspecify.annotations.NullUnmarked;
 
 /**
  * JsonToken implementation borrowed from <a href=
@@ -30,6 +31,7 @@
  * @author Ross Lawley
  * @since 2.2
  */
+@NullUnmarked
 class JsonToken {
 
 	private final Object value;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java
index b4fd13b3af..55dccad0c9 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java
@@ -19,7 +19,7 @@
 import java.util.function.Function;
 import java.util.function.Supplier;
 
-import org.springframework.data.mapping.model.SpELExpressionEvaluator;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.spel.ExpressionDependencies;
 import org.springframework.data.util.Lazy;
@@ -28,8 +28,6 @@
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.ParseException;
 import org.springframework.expression.ParserContext;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 
 /**
  * Reusable context for binding parameters to a placeholder or a SpEL expression within a JSON structure. <br />
@@ -44,29 +42,6 @@ public class ParameterBindingContext {
 	private final ValueProvider valueProvider;
 	private final ValueExpressionEvaluator expressionEvaluator;
 
-	/**
-	 * @param valueProvider
-	 * @param expressionParser
-	 * @param evaluationContext
-	 * @deprecated since 4.4.0, use {@link #ParameterBindingContext(ValueProvider, ExpressionParser, Supplier)} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
-			EvaluationContext evaluationContext) {
-		this(valueProvider, expressionParser, () -> evaluationContext);
-	}
-
-	/**
-	 * @param valueProvider
-	 * @param expressionEvaluator
-	 * @since 3.1
-	 * @deprecated since 4.4.0, use {@link #ParameterBindingContext(ValueProvider, ValueExpressionEvaluator)} instead.
-	 */
-	@Deprecated(since = "4.4.0")
-	public ParameterBindingContext(ValueProvider valueProvider, SpELExpressionEvaluator expressionEvaluator) {
-		this(valueProvider, (ValueExpressionEvaluator) expressionEvaluator);
-	}
-
 	/**
 	 * @param valueProvider
 	 * @param expressionParser
@@ -153,18 +128,15 @@ public static ParameterBindingContext forExpressions(ValueProvider valueProvider
 		return new ParameterBindingContext(valueProvider, expressionEvaluator);
 	}
 
-	@Nullable
-	public Object bindableValueForIndex(int index) {
+	public @Nullable Object bindableValueForIndex(int index) {
 		return valueProvider.getBindableValue(index);
 	}
 
-	@Nullable
-	public Object evaluateExpression(String expressionString) {
+	public @Nullable Object evaluateExpression(String expressionString) {
 		return expressionEvaluator.evaluate(expressionString);
 	}
 
-	@Nullable
-	public Object evaluateExpression(String expressionString, Map<String, Object> variables) {
+	public @Nullable Object evaluateExpression(String expressionString, Map<String, Object> variables) {
 
 		if (expressionEvaluator instanceof EvaluationContextExpressionEvaluator expressionEvaluator) {
 			return expressionEvaluator.evaluateExpression(expressionString, variables);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java
index ffa226ab69..8138f397a6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java
@@ -40,14 +40,14 @@
 import org.bson.codecs.*;
 import org.bson.codecs.configuration.CodecRegistry;
 import org.bson.json.JsonParseException;
-
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.expression.ValueExpressionParser;
 import org.springframework.data.mapping.model.ValueExpressionEvaluator;
 import org.springframework.data.mongodb.core.mapping.FieldName;
 import org.springframework.data.spel.EvaluationContextProvider;
 import org.springframework.data.spel.ExpressionDependencies;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
@@ -66,6 +66,7 @@
  * @author Rocco Lagrotteria
  * @since 2.2
  */
+@NullUnmarked
 public class ParameterBindingDocumentCodec implements CollectibleCodec<Document> {
 
 	private static final String ID_FIELD_NAME = FieldName.ID.name();
@@ -170,7 +171,7 @@ public void encode(final BsonWriter writer, final Document document, final Encod
 	public Document decode(@Nullable String json, Object[] values) {
 
 		return decode(json, new ParameterBindingContext((index) -> values[index], new SpelExpressionParser(),
-				EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
+				() -> EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
 	}
 
 	public Document decode(@Nullable String json, ParameterBindingContext bindingContext) {
@@ -396,9 +397,8 @@ static class DependencyCapturingExpressionEvaluator implements ValueExpressionEv
 			this.expressionParser = expressionParser;
 		}
 
-		@Nullable
 		@Override
-		public <T> T evaluate(String expression) {
+		public <T> @Nullable T evaluate(String expression) {
 
 			dependencies.add(expressionParser.parse(expression).getExpressionDependencies());
 			return (T) PLACEHOLDER;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java
index 8dd42e2427..c1e519e2f5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java
@@ -39,10 +39,11 @@
 import org.bson.types.MaxKey;
 import org.bson.types.MinKey;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.NullUnmarked;
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.spel.EvaluationContextProvider;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.NumberUtils;
 import org.springframework.util.ObjectUtils;
@@ -62,6 +63,7 @@
  * @author Rocco Lagrotteria
  * @since 2.2
  */
+@NullUnmarked
 public class ParameterBindingJsonReader extends AbstractBsonReader {
 
 	private static final Pattern ENTIRE_QUERY_BINDING_PATTERN = Pattern.compile("^\\?(\\d+)$|^[\\?:][#$]\\{.*\\}$");
@@ -533,13 +535,11 @@ private BsonType bsonTypeForValue(Object value) {
 		return BsonType.UNDEFINED;
 	}
 
-	@Nullable
-	private Object evaluateExpression(String expressionString) {
+	private @Nullable Object evaluateExpression(String expressionString) {
 		return bindingContext.evaluateExpression(expressionString, Collections.emptyMap());
 	}
 
-	@Nullable
-	private Object evaluateExpression(String expressionString, Map<String,Object> variables) {
+	private @Nullable Object evaluateExpression(String expressionString, Map<String,Object> variables) {
 		return bindingContext.evaluateExpression(expressionString, variables);
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java
index 8f1d23885d..2ce22214fb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.util.json;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * A value provider to retrieve bindable values by their parameter index.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java
index 8a86b3522b..60e5e8c609 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java
@@ -1,5 +1,5 @@
 /**
  * MongoDB driver-specific utility classes for Json conversion.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.util.json;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java
index 7caec410f5..a697bb7000 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java
@@ -2,5 +2,5 @@
  * MongoDB driver-specific utility classes for {@link org.bson.conversions.Bson} and {@link com.mongodb.DBObject}
  * interaction.
  */
-@org.springframework.lang.NonNullApi
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.mongodb.util;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java
index 9fa66b3b2b..796f618906 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java
@@ -17,12 +17,12 @@
 
 import java.util.function.Supplier;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.common.LiteralExpression;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.StringUtils;
 
 /**
@@ -42,8 +42,7 @@ public final class ExpressionUtils {
 	 * @param potentialExpression can be {@literal null}
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public static Expression detectExpression(@Nullable String potentialExpression) {
+	public static @Nullable Expression detectExpression(@Nullable String potentialExpression) {
 
 		if (!StringUtils.hasText(potentialExpression)) {
 			return null;
@@ -53,8 +52,7 @@ public static Expression detectExpression(@Nullable String potentialExpression)
 		return expression instanceof LiteralExpression ? null : expression;
 	}
 
-	@Nullable
-	public static Object evaluate(String value, Supplier<EvaluationContext> evaluationContext) {
+	public static @Nullable Object evaluate(String value, Supplier<EvaluationContext> evaluationContext) {
 
 		Expression expression = detectExpression(value);
 		if (expression == null) {
diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt
index d132482f65..d7784a7768 100644
--- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt
+++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt
@@ -142,7 +142,7 @@ fun <T> Update.pull(key: KProperty<T>, value: Any) =
  * @since 4.4
  * @see Update.pullAll
  */
-fun <T> Update.pullAll(key: KProperty<Collection<T>>, values: Array<T>) =
+fun <T> Update.pullAll(key: KProperty<Collection<T>>, values: Array<Any>) =
     pullAll(key.toDotPath(), values)
 
 /**
diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas
index 57920f7449..c6c28dbab1 100644
--- a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas
+++ b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas
@@ -13,7 +13,8 @@ http\://www.springframework.org/schema/data/mongo/spring-mongo-2.2.xsd=org/sprin
 http\://www.springframework.org/schema/data/mongo/spring-mongo-3.0.xsd=org/springframework/data/mongodb/config/spring-mongo-3.0.xsd
 http\://www.springframework.org/schema/data/mongo/spring-mongo-3.3.xsd=org/springframework/data/mongodb/config/spring-mongo-3.3.xsd
 http\://www.springframework.org/schema/data/mongo/spring-mongo-4.0.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd
-http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd
+http\://www.springframework.org/schema/data/mongo/spring-mongo-5.0.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
+http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
 https\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd
 https\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd
 https\://www.springframework.org/schema/data/mongo/spring-mongo-1.2.xsd=org/springframework/data/mongodb/config/spring-mongo-1.2.xsd
@@ -29,4 +30,5 @@ https\://www.springframework.org/schema/data/mongo/spring-mongo-2.2.xsd=org/spri
 https\://www.springframework.org/schema/data/mongo/spring-mongo-3.0.xsd=org/springframework/data/mongodb/config/spring-mongo-3.0.xsd
 https\://www.springframework.org/schema/data/mongo/spring-mongo-3.3.xsd=org/springframework/data/mongodb/config/spring-mongo-3.3.xsd
 https\://www.springframework.org/schema/data/mongo/spring-mongo-4.0.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd
-https\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd
+https\://www.springframework.org/schema/data/mongo/spring-mongo-5.0.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
+https\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
new file mode 100644
index 0000000000..5fae630b6b
--- /dev/null
+++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd
@@ -0,0 +1,935 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2019-2025 the original author or authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      https://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<xsd:schema xmlns="http://www.springframework.org/schema/data/mongo"
+            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+            xmlns:beans="http://www.springframework.org/schema/beans"
+            xmlns:tool="http://www.springframework.org/schema/tool"
+            xmlns:repository="http://www.springframework.org/schema/data/repository"
+            targetNamespace="http://www.springframework.org/schema/data/mongo"
+            elementFormDefault="qualified" attributeFormDefault="unqualified">
+
+	<xsd:import namespace="http://www.springframework.org/schema/beans"/>
+	<xsd:import namespace="http://www.springframework.org/schema/tool"/>
+	<xsd:import namespace="http://www.springframework.org/schema/context"/>
+	<xsd:import namespace="http://www.springframework.org/schema/data/repository"
+	            schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd"/>
+
+	<xsd:element name="mongo-client" type="mongoClientType">
+		<xsd:annotation>
+			<xsd:documentation
+					source="org.springframework.data.mongodb.core.MongoClientFactoryBean">
+				<![CDATA[
+Defines a com.mongodb.client.MongoClient instance used for accessing MongoDB.
+			]]></xsd:documentation>
+			<xsd:appinfo>
+				<tool:annotation>
+					<tool:exports type="com.mongodb.client.MongoClient"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+	</xsd:element>
+
+	<xsd:element name="db-factory">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Defines a MongoDbFactory for connecting to a specific database
+			]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:attribute name="id" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The name of the MongoDatabaseFactory definition (by default "mongoDbFactory").]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="mongo-client-ref" type="mongoRef" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The reference to a com.mongodb.client.MongoClient instance.
+					]]>
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="dbname" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The name of the database to connect to. Default is 'db'.
+							]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="client-uri" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The MongoClientURI string.
+@Deprecated since 3.0 - Use connection-string instead.
+                    ]]>
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="connection-string" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The MongoDB Connection String. Supersedes client-uri.
+See https://docs.mongodb.com/manual/reference/connection-string/ for full documentation.
+@Since 3.0
+                    ]]>
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="write-concern">
+				<xsd:annotation>
+					<xsd:documentation>
+						The WriteConcern that will be the default value used when asking
+						the MongoDatabaseFactory for a DB object
+					</xsd:documentation>
+				</xsd:annotation>
+				<xsd:simpleType>
+					<xsd:union memberTypes="writeConcernEnumeration xsd:string"/>
+				</xsd:simpleType>
+			</xsd:attribute>
+		</xsd:complexType>
+	</xsd:element>
+
+	<xsd:attributeGroup name="mongo-repository-attributes">
+		<xsd:attribute name="mongo-template-ref" type="mongoTemplateRef"
+		               default="mongoTemplate">
+			<xsd:annotation>
+				<xsd:documentation>
+					The reference to a MongoTemplate. Will default to 'mongoTemplate'.
+				</xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="create-query-indexes" default="false">
+			<xsd:annotation>
+				<xsd:documentation>
+					Enables creation of indexes for queries that get derived from the
+					method name
+					and thus reference domain class properties. Defaults to false.
+				</xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="booleanType xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+	</xsd:attributeGroup>
+
+	<xsd:element name="repositories">
+		<xsd:complexType>
+			<xsd:complexContent>
+				<xsd:extension base="repository:repositories">
+					<xsd:attributeGroup ref="mongo-repository-attributes"/>
+					<xsd:attributeGroup ref="repository:repository-attributes"/>
+				</xsd:extension>
+			</xsd:complexContent>
+		</xsd:complexType>
+	</xsd:element>
+
+	<xsd:element name="mapping-converter">
+		<xsd:annotation>
+			<xsd:documentation>
+				<![CDATA[Defines a MongoConverter for getting rich mapping functionality.]]></xsd:documentation>
+			<xsd:appinfo>
+				<tool:exports
+						type="org.springframework.data.mongodb.core.convert.MappingMongoConverter"/>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:sequence>
+				<xsd:element name="custom-converters" minOccurs="0">
+					<xsd:annotation>
+						<xsd:documentation><![CDATA[
+		Top-level element that contains one or more custom converters to be used for mapping
+		domain objects to and from Mongo's Document]]>
+						</xsd:documentation>
+					</xsd:annotation>
+					<xsd:complexType>
+						<xsd:sequence>
+							<xsd:element name="converter" type="customConverterType"
+							             minOccurs="0" maxOccurs="unbounded"/>
+						</xsd:sequence>
+						<xsd:attribute name="base-package" type="xsd:string"/>
+					</xsd:complexType>
+				</xsd:element>
+			</xsd:sequence>
+			<xsd:attribute name="id" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The name of the MappingMongoConverter instance (by default "mappingConverter").]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="base-package" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The base package in which to scan for entities annotated with @Document
+							]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="db-factory-ref" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation>
+						The reference to a MongoDatabaseFactory.
+					</xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation kind="ref">
+							<tool:assignable-to
+									type="org.springframework.data.mongodb.MongoDatabaseFactory"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="type-mapper-ref" type="typeMapperRef" use="optional">
+				<xsd:annotation>
+					<xsd:documentation>
+						The reference to a MongoTypeMapper to be used by this
+						MappingMongoConverter.
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="mapping-context-ref" type="mappingContextRef"
+			               use="optional">
+				<xsd:annotation>
+					<xsd:documentation
+							source="org.springframework.data.mapping.model.MappingContext">
+						The reference to a MappingContext. Will default to
+						'mappingContext'.
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="disable-validation" use="optional">
+				<xsd:annotation>
+					<xsd:documentation
+							source="org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener">
+						Disables JSR-303 validation on MongoDB documents before they are
+						saved. By default it is set to false.
+					</xsd:documentation>
+				</xsd:annotation>
+				<xsd:simpleType>
+					<xsd:union memberTypes="xsd:boolean xsd:string"/>
+				</xsd:simpleType>
+			</xsd:attribute>
+			<xsd:attribute name="abbreviate-field-names" use="optional">
+				<xsd:annotation>
+					<xsd:documentation
+							source="org.springframework.data.mongodb.core.mapping.CamelCaseAbbreviatingFieldNamingStrategy">
+						Enables abbreviating the field names for domain class properties
+						to the
+						first character of their camel case names, e.g. fooBar -> fb.
+						Defaults to false.
+					</xsd:documentation>
+				</xsd:annotation>
+				<xsd:simpleType>
+					<xsd:union memberTypes="xsd:boolean xsd:string"/>
+				</xsd:simpleType>
+			</xsd:attribute>
+			<xsd:attribute name="field-naming-strategy-ref" type="fieldNamingStrategyRef"
+			               use="optional">
+				<xsd:annotation>
+					<xsd:documentation
+							source="org.springframework.data.mongodb.core.mapping.FieldNamingStrategy">
+						The reference to a FieldNamingStrategy.
+					</xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="auto-index-creation" use="optional">
+				<xsd:annotation>
+					<xsd:documentation>
+						Enable/Disable index creation for annotated properties/entities.
+					</xsd:documentation>
+				</xsd:annotation>
+				<xsd:simpleType>
+					<xsd:union memberTypes="xsd:boolean xsd:string"/>
+				</xsd:simpleType>
+			</xsd:attribute>
+		</xsd:complexType>
+	</xsd:element>
+
+	<xsd:element name="auditing">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation>
+					<tool:exports
+							type="org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback"/>
+					<tool:exports
+							type="org.springframework.data.auditing.IsNewAwareAuditingHandler"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:attributeGroup ref="repository:auditing-attributes"/>
+			<xsd:attribute name="mapping-context-ref" type="mappingContextRef"/>
+			<xsd:attribute name="mongo-converter-ref" type="mongoConverterRef"/>
+		</xsd:complexType>
+	</xsd:element>
+
+	<xsd:simpleType name="typeMapperRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.convert.MongoTypeMapper"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="mappingContextRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mapping.model.MappingContext"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="mongoConverterRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.convert.MongoConverter"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="fieldNamingStrategyRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.mapping.FieldNamingStrategy"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="mongoTemplateRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.MongoTemplate"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="mongoRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.MongoClientFactoryBean"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="sslSocketFactoryRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to type="javax.net.ssl.SSLSocketFactory"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="encryptionSettingsRef">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Reference to FactoryBean for com.mongodb.AutoEncryptionSettings - @since 2.2
+			]]></xsd:documentation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.MongoEncryptionSettingsFactoryBean"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="serverApiRef">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Reference to FactoryBean for com.mongodb.MongoServerApiFactoryBean - @since 3.3
+			]]></xsd:documentation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.MongoServerApiFactoryBean"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="readConcernEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="DEFAULT"/>
+			<xsd:enumeration value="LOCAL"/>
+			<xsd:enumeration value="MAJORITY"/>
+			<xsd:enumeration value="LINEARIZABLE"/>
+			<xsd:enumeration value="AVAILABLE"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="writeConcernEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="ACKNOWLEDGED"/>
+			<xsd:enumeration value="W1"/>
+			<xsd:enumeration value="W2"/>
+			<xsd:enumeration value="W3"/>
+			<xsd:enumeration value="UNACKNOWLEDGED"/>
+			<xsd:enumeration value="JOURNALED"/>
+			<xsd:enumeration value="MAJORITY"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="readPreferenceEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="PRIMARY"/>
+			<xsd:enumeration value="PRIMARY_PREFERRED"/>
+			<xsd:enumeration value="SECONDARY"/>
+			<xsd:enumeration value="SECONDARY_PREFERRED"/>
+			<xsd:enumeration value="NEAREST"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="uuidRepresentationEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="UNSPECIFIED"/>
+			<xsd:enumeration value="STANDARD"/>
+			<xsd:enumeration value="C_SHARP_LEGACY"/>
+			<xsd:enumeration value="JAVA_LEGACY"/>
+			<xsd:enumeration value="PYTHON_LEGACY"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="clusterConnectionModeEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="SINGLE"/>
+			<xsd:enumeration value="MULTIPLE"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="clusterTypeEnumeration">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="STANDALONE"/>
+			<xsd:enumeration value="REPLICA_SET"/>
+			<xsd:enumeration value="SHARDED"/>
+			<xsd:enumeration value="UNKNOWN"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="booleanType">
+		<xsd:restriction base="xsd:token">
+			<xsd:enumeration value="true"/>
+			<xsd:enumeration value="false"/>
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:complexType name="mongoClientType">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Configuration options for 'com.mongodb.client.MongoClient'
+			]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:sequence minOccurs="0" maxOccurs="1">
+			<xsd:element name="client-settings" type="clientSettingsType">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The Mongo driver settings
+							]]></xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation>
+							<tool:exports type="com.mongodb.MongoClientSettings"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:element>
+		</xsd:sequence>
+		<xsd:attribute name="id" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The name of the MongoClient definition (by default "mongoClient").]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-string" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The mongodb connection string. E.g. 'mongodb://localhost:27017?replicaSet=rs0'
+See https://docs.mongodb.com/manual/reference/connection-string/ for full documentation.
+							]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="port" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The port to connect to MongoDB server.  Default is 27017
+							]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="host" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The host to connect to a MongoDB server.  Default is localhost
+							]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="replica-set" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The replica set name when connecting to a cluster.
+							]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="credential" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+NOTE: Best way of setting up a connection is by providing the 'connection-string'.
+The comma delimited list of username:password@database entries to use for authentication. Appending ?uri.authMechanism allows to specify the authentication challenge mechanism. If the credential you're trying to pass contains a comma itself, quote it with single quotes: '…'.
+							]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:complexType name="clientSettingsType">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Configuration options for 'MongoClientSettings' - @since 3.0
+			]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:attribute name="application-name" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The application name to use when connecting to MongoDB. Mainly used to identify an operation in server logs, query logs and other profiling features.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="uuid-representation" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The storage format of UUID types.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="uuidRepresentationEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="read-preference" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The read preference to use for quries, map-reduce, aggregation and count operations.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="readPreferenceEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="read-concern" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set the global read isolation level.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="readConcernEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="write-concern">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set the default 'WriteConcern' that is controls the acknowledgment of write operations.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="writeConcernEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="retry-reads" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets whether reads should be retried if they fail due to a network error.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="booleanType xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="retry-writes" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets whether writes should be retried if they fail due to a network error.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="booleanType xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="socket-connect-timeout" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the socket connect timeout (msec).
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="socket-read-timeout" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the socket read timeoutn (msec).
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="socket-receive-buffer-size" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the receive buffer size.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="socket-send-buffer-size" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the send buffer size.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="server-heartbeat-frequency" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+This is the frequency that the driver will attempt to determine the current state of each server in the cluster.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="server-min-heartbeat-frequency" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+In the event that the driver has to frequently re-check a server's availability, it will wait at least this long since the previous check to avoid wasted effort.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-srv-host" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the host name to use in order to look up an SRV DNS record to find the MongoDB hosts.
+NOTE: do not use along with cluster-hosts.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-hosts" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the hosts for the cluster. Any duplicate server addresses are removed from the list.
+NOTE: do not use along with cluster-srv-host
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-connection-mode" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the cluster connection mode to either single node direct or multiple servers.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="clusterConnectionModeEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-type" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the cluster type (eg. SHARDED, REPLICA_SET,...).
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="clusterTypeEnumeration xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-local-threshold" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the local threshold when selecting a server based on fastes ping time.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="cluster-server-selection-timeout" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the timeout to apply when selecting a server.
+Zero indicates an immediate timeout while a negative value means indefinitely wait.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-max-size" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the maximum number of connections allowed.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-min-size" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the minimum number of connections.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-max-wait-time" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the maximum time a thread may wait for a connection to become available.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-max-connection-life-time" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+The maximum time a pooled connection can live for.
+Zero indicates no limit.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-max-connection-idle-time" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the maximum idle time of a pooled connection.
+Zero indicates no limit.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-maintenance-initial-delay" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the period of time to wait before running the first maintenance job on the connection pool.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="connection-pool-maintenance-frequency" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Sets the time period between runs of the maintenance job.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="ssl-enabled" default="false">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set whether SSL should be enabled or not.
+				]]></xsd:documentation>
+			</xsd:annotation>
+			<xsd:simpleType>
+				<xsd:union memberTypes="booleanType xsd:string"/>
+			</xsd:simpleType>
+		</xsd:attribute>
+		<xsd:attribute name="ssl-invalid-host-name-allowed" type="xsd:string"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set whether invalid host names should be allowed.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="ssl-provider" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set the SSL Context instance provider.
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="encryption-settings-ref" type="encryptionSettingsRef"
+		               use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+AutoEncryptionSettings for MongoDB 4.2+
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="server-api-version" type="xsd:string" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+Set the server API version for MongoDB 5.0+. Use server-api-ref if required to set additional modes like 'strict',...
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+		<xsd:attribute name="server-api-ref" type="serverApiRef" use="optional">
+			<xsd:annotation>
+				<xsd:documentation><![CDATA[
+ServerAPI for MongoDB 5.0+
+				]]></xsd:documentation>
+			</xsd:annotation>
+		</xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:group name="beanElementGroup">
+		<xsd:choice>
+			<xsd:element ref="beans:bean"/>
+			<xsd:element ref="beans:ref"/>
+		</xsd:choice>
+	</xsd:group>
+
+	<xsd:complexType name="customConverterType">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+	Element defining a custom converter.
+	]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/>
+		<xsd:attribute name="ref" type="xsd:string">
+			<xsd:annotation>
+				<xsd:documentation>
+					A reference to a custom converter.
+				</xsd:documentation>
+				<xsd:appinfo>
+					<tool:annotation kind="ref"/>
+				</xsd:appinfo>
+			</xsd:annotation>
+		</xsd:attribute>
+	</xsd:complexType>
+
+	<xsd:simpleType name="converterRef">
+		<xsd:annotation>
+			<xsd:appinfo>
+				<tool:annotation kind="ref">
+					<tool:assignable-to
+							type="org.springframework.data.mongodb.core.convert.MongoConverter"/>
+				</tool:annotation>
+			</xsd:appinfo>
+		</xsd:annotation>
+		<xsd:union memberTypes="xsd:string"/>
+	</xsd:simpleType>
+
+	<xsd:element name="template">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Defines a MongoTemplate.
+			]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:attribute name="id" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The name of the MongoTemplate definition (by default "mongoTemplate").]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="converter-ref" type="converterRef" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The reference to a MappingMongoConverter instance.
+					]]>
+					</xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation kind="ref">
+							<tool:assignable-to
+									type="org.springframework.data.mongodb.core.convert.MongoConverter"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="db-factory-ref" type="xsd:string"
+			               use="optional">
+				<xsd:annotation>
+					<xsd:documentation>
+						The reference to a MongoDatabaseFactory.
+					</xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation kind="ref">
+							<tool:assignable-to
+									type="org.springframework.data.mongodb.MongoDatabaseFactory"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="write-concern">
+				<xsd:annotation>
+					<xsd:documentation>
+						The WriteConcern that will be the default value used when asking
+						the MongoDatabaseFactory for a DB object
+					</xsd:documentation>
+				</xsd:annotation>
+				<xsd:simpleType>
+					<xsd:union memberTypes="writeConcernEnumeration xsd:string"/>
+				</xsd:simpleType>
+			</xsd:attribute>
+		</xsd:complexType>
+	</xsd:element>
+
+	<xsd:element name="gridFsTemplate">
+		<xsd:annotation>
+			<xsd:documentation><![CDATA[
+Defines a GridFsTemplate.
+			]]></xsd:documentation>
+		</xsd:annotation>
+		<xsd:complexType>
+			<xsd:attribute name="id" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The name of the GridFsTemplate definition (by default "gridFsTemplate").]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="converter-ref" type="converterRef" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The reference to a MappingMongoConverter instance.
+					]]>
+					</xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation kind="ref">
+							<tool:assignable-to
+									type="org.springframework.data.mongodb.core.convert.MongoConverter"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="db-factory-ref" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation>
+						The reference to a MongoDatabaseFactory.
+					</xsd:documentation>
+					<xsd:appinfo>
+						<tool:annotation kind="ref">
+							<tool:assignable-to
+									type="org.springframework.data.mongodb.MongoDatabaseFactory"/>
+						</tool:annotation>
+					</xsd:appinfo>
+				</xsd:annotation>
+			</xsd:attribute>
+			<xsd:attribute name="bucket" type="xsd:string" use="optional">
+				<xsd:annotation>
+					<xsd:documentation><![CDATA[
+The GridFs bucket string.]]></xsd:documentation>
+				</xsd:annotation>
+			</xsd:attribute>
+		</xsd:complexType>
+	</xsd:element>
+</xsd:schema>
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java
index 0448ad936c..c05122873c 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java
@@ -21,7 +21,7 @@
 
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.ListAssert;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.CollectionUtils;
 
 /**
@@ -36,9 +36,8 @@ public CapturingTransactionOptionsResolver(MongoTransactionOptionsResolver deleg
 		this.delegateResolver = delegateResolver;
 	}
 
-	@Nullable
 	@Override
-	public String getLabelPrefix() {
+	public @Nullable String getLabelPrefix() {
 		return delegateResolver.getLabelPrefix();
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java
index 44692348a0..d89edc6206 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java
@@ -20,8 +20,8 @@
 import java.time.Duration;
 import java.util.concurrent.TimeUnit;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.ReadConcern;
 import com.mongodb.ReadPreference;
@@ -90,27 +90,23 @@ void testEquals() {
 		assertThat(MongoTransactionOptions.NONE) //
 				.isSameAs(MongoTransactionOptions.NONE) //
 				.isNotEqualTo(new MongoTransactionOptions() {
-					@Nullable
 					@Override
-					public Duration getMaxCommitTime() {
+					public @Nullable Duration getMaxCommitTime() {
 						return null;
 					}
 
-					@Nullable
 					@Override
-					public ReadConcern getReadConcern() {
+					public @Nullable ReadConcern getReadConcern() {
 						return null;
 					}
 
-					@Nullable
 					@Override
-					public ReadPreference getReadPreference() {
+					public @Nullable ReadPreference getReadPreference() {
 						return null;
 					}
 
-					@Nullable
 					@Override
-					public WriteConcern getWriteConcern() {
+					public @Nullable WriteConcern getWriteConcern() {
 						return null;
 					}
 				});
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java
deleted file mode 100644
index cb8a8dcf45..0000000000
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.core;
-
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-/**
- * Server application than can be run as an app or unit test.
- *
- * @author Mark Pollack
- * @author Oliver Gierke
- */
-public class JmxServer {
-
-	public static void main(String[] args) {
-		new JmxServer().run();
-	}
-
-	@SuppressWarnings("resource")
-	public void run() {
-		new ClassPathXmlApplicationContext(new String[] { "infrastructure.xml", "server-jmx.xml" });
-	}
-}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java
index 9730e61e51..bdc151ec63 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java
@@ -18,6 +18,7 @@
 import static org.assertj.core.api.Assertions.*;
 
 import org.bson.BsonDocument;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
@@ -31,7 +32,6 @@
 import org.springframework.data.mongodb.ClientSessionException;
 import org.springframework.data.mongodb.MongoTransactionException;
 import org.springframework.data.mongodb.UncategorizedMongoDbException;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.MongoCursorNotFoundException;
 import com.mongodb.MongoException;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index 51b3b005a5..9a6bbb4f29 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -29,6 +29,7 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -48,7 +49,6 @@
 import org.springframework.data.mongodb.test.util.Client;
 import org.springframework.data.mongodb.test.util.MongoClientExtension;
 import org.springframework.data.mongodb.test.util.MongoTestTemplate;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.client.MongoClient;
 import com.mongodb.client.model.Filters;
@@ -1936,9 +1936,8 @@ public String toString() {
 
 	static class ReferencableConverter implements Converter<ReferenceAble, DocumentPointer<Object>> {
 
-		@Nullable
 		@Override
-		public DocumentPointer<Object> convert(ReferenceAble source) {
+		public @Nullable DocumentPointer<Object> convert(ReferenceAble source) {
 			return source::toReference;
 		}
 	}
@@ -1947,9 +1946,8 @@ public DocumentPointer<Object> convert(ReferenceAble source) {
 	static class DocumentToSimpleObjectRefWithReadingConverter
 			implements Converter<DocumentPointer<Document>, SimpleObjectRefWithReadingConverter> {
 
-		@Nullable
 		@Override
-		public SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> source) {
+		public @Nullable SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> source) {
 
 			Document document = client.getDatabase(DB_NAME).getCollection("simple-object-ref")
 					.find(Filters.eq("_id", source.getPointer().get("ref-key-from-custom-write-converter"))).first();
@@ -1961,9 +1959,8 @@ public SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> sou
 	static class SimpleObjectRefWithReadingConverterToDocumentConverter
 			implements Converter<SimpleObjectRefWithReadingConverter, DocumentPointer<Document>> {
 
-		@Nullable
 		@Override
-		public DocumentPointer<Document> convert(SimpleObjectRefWithReadingConverter source) {
+		public @Nullable DocumentPointer<Document> convert(SimpleObjectRefWithReadingConverter source) {
 			return () -> new Document("ref-key-from-custom-write-converter", source.getId());
 		}
 	}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java
index 766929c732..772392f037 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java
@@ -26,6 +26,7 @@
 import java.util.stream.Stream;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -47,7 +48,6 @@
 import org.springframework.data.mongodb.test.util.Client;
 import org.springframework.data.mongodb.test.util.MongoClientExtension;
 import org.springframework.data.mongodb.test.util.MongoTestTemplate;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 import com.mongodb.client.MongoClient;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
index 83d4e30cc5..ee01de7ebc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
@@ -35,6 +35,7 @@
 import java.util.stream.Stream;
 
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -48,7 +49,7 @@
 import org.springframework.dao.OptimisticLockingFailureException;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.annotation.LastModifiedDate;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.annotation.Version;
 import org.springframework.data.auditing.IsNewAwareAuditingHandler;
 import org.springframework.data.domain.PageRequest;
@@ -90,7 +91,6 @@
 import org.springframework.data.mongodb.test.util.MongoTestTemplate;
 import org.springframework.data.mongodb.test.util.MongoTestUtils;
 import org.springframework.data.mongodb.test.util.MongoVersion;
-import org.springframework.lang.Nullable;
 import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.ObjectUtils;
@@ -4469,7 +4469,7 @@ static class TestClass {
 
 		LocalDateTime myDate;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		TestClass(LocalDateTime myDate) {
 			this.myDate = myDate;
 		}
@@ -4726,7 +4726,7 @@ static class DocumentWithLazyDBrefUsedInPresistenceConstructor {
 		@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocUsedInCtor;
 		@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocNotUsedInCtor;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public DocumentWithLazyDBrefUsedInPresistenceConstructor(Document refToDocUsedInCtor) {
 			this.refToDocUsedInCtor = refToDocUsedInCtor;
 		}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
index 79a0bb1fcb..b5892c2ca0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
@@ -39,6 +39,7 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -99,7 +100,6 @@
 import org.springframework.data.mongodb.core.timeseries.Granularity;
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
-import org.springframework.lang.Nullable;
 import org.springframework.mock.env.MockEnvironment;
 import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.util.CollectionUtils;
@@ -2908,8 +2908,7 @@ public List<T> getValues() {
 			return values;
 		}
 
-		@Nullable
-		public T getValue() {
+		public @Nullable T getValue() {
 			return CollectionUtils.lastElement(values);
 		}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java
index 18da8c516d..fd1b70f3c7 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -39,7 +40,6 @@
 import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
 import org.springframework.data.mongodb.test.util.Client;
 import org.springframework.data.mongodb.test.util.MongoClientExtension;
-import org.springframework.lang.Nullable;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import com.mongodb.client.MongoClient;
@@ -242,13 +242,11 @@ public SimpleBean(@Nullable String nonNullString, @Nullable Integer rangedIntege
 			this.customFieldName = customFieldName;
 		}
 
-		@Nullable
-		public String getNonNullString() {
+		public @Nullable String getNonNullString() {
 			return this.nonNullString;
 		}
 
-		@Nullable
-		public Integer getRangedInteger() {
+		public @Nullable Integer getRangedInteger() {
 			return this.rangedInteger;
 		}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java
index bc126e05f0..7b07cd9448 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core;
 
 import org.bson.types.ObjectId;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 public class Person {
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java
index 609a456912..b5a40f5738 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java
@@ -74,7 +74,7 @@ void usesExtractedCollectionName() {
 		mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).all();
 
 		verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION),
-				eq(REDUCE_FUNCTION), isNull());
+				eq(REDUCE_FUNCTION), any());
 	}
 
 	@Test // DATAMONGO-1929
@@ -84,7 +84,7 @@ void usesExplicitCollectionName() {
 				.inCollection("the-night-angel").all();
 
 		verify(template).mapReduce(any(Query.class), eq(Person.class), eq("the-night-angel"), eq(Person.class),
-				eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), isNull());
+				eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), any());
 	}
 
 	@Test // DATAMONGO-1929
@@ -108,7 +108,7 @@ void usesQueryWhenPresent() {
 		mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).matching(query).all();
 
 		verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION),
-				eq(REDUCE_FUNCTION), isNull());
+				eq(REDUCE_FUNCTION), any());
 	}
 
 	@Test // DATAMONGO-2416
@@ -121,7 +121,7 @@ void usesCriteriaWhenPresent() {
 				.matching(where("lastname").is("skywalker")).all();
 
 		verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION),
-				eq(REDUCE_FUNCTION), isNull());
+				eq(REDUCE_FUNCTION), any());
 	}
 
 	@Test // DATAMONGO-1929
@@ -132,7 +132,7 @@ void usesProjectionWhenPresent() {
 		mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).as(Jedi.class).all();
 
 		verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Jedi.class), eq(MAP_FUNCTION),
-				eq(REDUCE_FUNCTION), isNull());
+				eq(REDUCE_FUNCTION), any());
 	}
 
 	interface Contact {}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java
index f89b2fa8c1..5ba0d947fe 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java
@@ -43,6 +43,7 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -93,7 +94,6 @@
 import org.springframework.data.mongodb.core.timeseries.Granularity;
 import org.springframework.data.mongodb.util.BsonUtils;
 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
-import org.springframework.lang.Nullable;
 import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.util.CollectionUtils;
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java
index 8968f53a74..b8cc9cc972 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java
@@ -18,7 +18,7 @@
 import java.util.function.Function;
 import java.util.function.UnaryOperator;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -48,45 +48,38 @@ public T saveWithinMaxCommitTime(T entity) {
 		return saveFunction.apply(entity);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readConcern=available" })
-	public T availableReadConcernFind(Object id) {
+	public @Nullable T availableReadConcernFind(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readConcern=invalid" })
-	public T invalidReadConcernFind(Object id) {
+	public @Nullable T invalidReadConcernFind(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readConcern=${tx.read.concern}" })
-	public T environmentReadConcernFind(Object id) {
+	public @Nullable T environmentReadConcernFind(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readConcern=majority" })
-	public T majorityReadConcernFind(Object id) {
+	public @Nullable T majorityReadConcernFind(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readPreference=primaryPreferred" })
-	public T findFromPrimaryPreferredReplica(Object id) {
+	public @Nullable T findFromPrimaryPreferredReplica(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readPreference=invalid" })
-	public T findFromInvalidReplica(Object id) {
+	public @Nullable T findFromInvalidReplica(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
-	@Nullable
 	@Transactional(transactionManager = "txManager", label = { "mongo:readPreference=primary" })
-	public T findFromPrimaryReplica(Object id) {
+	public @Nullable T findFromPrimaryReplica(Object id) {
 		return findByIdFunction.apply(id);
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java
index d4c2f37f63..61cd3ecce4 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java
@@ -20,6 +20,8 @@
 import java.util.Arrays;
 
 import org.bson.Document;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.CodecRegistryProvider;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@@ -29,8 +31,6 @@
 import org.springframework.data.mongodb.core.convert.UpdateMapper;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.MongoClientSettings;
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java
index 25fbbbcb83..e9eae082e3 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 public class User {
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java
index 09a0605ed7..13aba70afa 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java
@@ -19,7 +19,7 @@
 import java.util.Date;
 
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.mongodb.core.mapping.Document;
 
 @Document("newyork")
@@ -30,7 +30,7 @@ public class Venue {
 	private double[] location;
 	private Date openingDate;
 
-	@PersistenceConstructor
+	@PersistenceCreator
 	Venue(String name, double[] location) {
 		super();
 		this.name = name;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java
index 32c6d43220..3256b889d4 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java
@@ -20,13 +20,13 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
 import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link AddFieldsOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java
index 47176fd8ab..d830a44582 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java
@@ -19,6 +19,7 @@
 
 import java.util.Date;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.core.aggregation.DensifyOperation.DensifyUnits;
 import org.springframework.data.mongodb.core.aggregation.DensifyOperation.Range;
@@ -27,7 +28,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link DensifyOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java
index 9496a51c03..1b9aba1ba0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java
@@ -20,6 +20,7 @@
 import java.util.Arrays;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.geo.Distance;
@@ -33,7 +34,6 @@
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.NearQuery;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link GeoNearOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java
index 311496ba8d..18980f6a06 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java
@@ -23,6 +23,7 @@
 import java.util.Arrays;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@@ -30,7 +31,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link MergeOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java
index 1174507e1c..a463b72cff 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java
@@ -20,6 +20,8 @@
 import java.util.Date;
 import java.util.List;
 
+import org.springframework.lang.Contract;
+
 /**
  * @author Thomas Darimont
  */
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java
index 24566089e7..d29e32a988 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java
@@ -20,6 +20,7 @@
 import java.util.Arrays;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
 import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
@@ -27,7 +28,6 @@
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link RedactOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
index 093d4af7a0..82ad4c223f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
@@ -20,6 +20,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@@ -27,7 +28,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link SetOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java
index b5f5f596e6..18eb659cd0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java
@@ -20,6 +20,7 @@
 import java.util.Date;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
@@ -30,7 +31,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link SetWindowFieldsOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java
index e47fea289e..ef514ca882 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java
@@ -21,13 +21,13 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
 import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link UnionWithOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java
index 2f081cc9fc..c406b89626 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java
@@ -22,6 +22,7 @@
 import java.util.Collections;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
@@ -29,7 +30,6 @@
 import org.springframework.data.mongodb.core.convert.QueryMapper;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
-import org.springframework.lang.Nullable;
 
 /**
  * Unit tests for {@link UnsetOperation}.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java
index b53531f301..71c395e822 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java
@@ -34,6 +34,7 @@
 import org.bson.Document;
 import org.bson.conversions.Bson;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledForJreRange;
@@ -46,7 +47,7 @@
 import org.springframework.data.annotation.AccessType;
 import org.springframework.data.annotation.AccessType.Type;
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mapping.PropertyPath;
 import org.springframework.data.mongodb.MongoDatabaseFactory;
@@ -55,7 +56,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
-import org.springframework.lang.Nullable;
 import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.util.SerializationUtils;
 
@@ -774,7 +774,7 @@ static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget {
 
 		public LazyDbRefTargetWithPeristenceConstructor() {}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		LazyDbRefTargetWithPeristenceConstructor(String id, String value) {
 			super(id, value);
 			this.persistenceConstructorCalled = true;
@@ -790,7 +790,7 @@ static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor e
 
 		boolean persistenceConstructorCalled;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(String id, String value) {
 			super(id, value);
 			this.persistenceConstructorCalled = true;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
index cf6d69c6c3..5bd7e06b97 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
@@ -40,6 +40,8 @@
 import org.bson.types.Code;
 import org.bson.types.Decimal128;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -63,7 +65,7 @@
 import org.springframework.core.convert.ConverterNotFoundException;
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.annotation.Transient;
 import org.springframework.data.annotation.TypeAlias;
 import org.springframework.data.convert.ConverterBuilder;
@@ -103,10 +105,7 @@
 import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.projection.EntityProjectionIntrospector;
-import org.springframework.data.util.ClassTypeInformation;
 import org.springframework.data.util.TypeInformation;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
 import org.springframework.test.util.ReflectionTestUtils;
 
 import com.mongodb.BasicDBList;
@@ -1061,7 +1060,7 @@ void convertsSetToBasicDBList() {
 		address.city = "London";
 		address.street = "Foo";
 
-		Object result = converter.convertToMongoType(Collections.singleton(address), ClassTypeInformation.OBJECT);
+		Object result = converter.convertToMongoType(Collections.singleton(address), TypeInformation.OBJECT);
 		assertThat(result).isInstanceOf(List.class);
 
 		Set<?> readResult = converter.read(Set.class, (org.bson.Document) result);
@@ -1393,7 +1392,7 @@ void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
 		address.street = "Foo";
 
 		Object result = converter.convertToMongoType(Collections.singletonList(address),
-				ClassTypeInformation.from(InterfaceType.class));
+				TypeInformation.of(InterfaceType.class));
 
 		assertThat(result).isInstanceOf(List.class);
 
@@ -1421,7 +1420,7 @@ void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() {
 		address.city = "London";
 		address.street = "Foo";
 
-		Object result = converter.convertToMongoType(new Address[] { address }, ClassTypeInformation.OBJECT);
+		Object result = converter.convertToMongoType(new Address[] { address }, TypeInformation.OBJECT);
 
 		assertThat(result).isInstanceOf(List.class);
 
@@ -1712,7 +1711,7 @@ void shouldIncludeTextScorePropertyWhenReading() {
 	}
 
 	@Test // DATAMONGO-1001, DATAMONGO-1509
-	void shouldWriteCglibProxiedClassTypeInformationCorrectly() {
+	void shouldWriteCglibProxiedTypeInformationCorrectly() {
 
 		ProxyFactory factory = new ProxyFactory();
 		factory.setTargetClass(GenericType.class);
@@ -2318,7 +2317,7 @@ void readAndConvertDBRefNestedByMapCorrectly() {
 		Mockito.doReturn(cluster).when(spyConverter).readRef(dbRef);
 
 		Map<Object, Object> result = spyConverter.readMap(spyConverter.getConversionContext(ObjectPath.ROOT), data,
-				ClassTypeInformation.MAP);
+				TypeInformation.MAP);
 
 		assertThat(((Map) result.get("cluster")).get("_id")).isEqualTo(100L);
 	}
@@ -3168,15 +3167,14 @@ void beanConverter() {
 				registrar.registerConverter(WithValueConverters.class, "viaRegisteredConverter",
 						new PropertyValueConverter<String, org.bson.Document, MongoConversionContext>() {
 
-							@Nullable
 							@Override
-							public String read(@Nullable org.bson.Document nativeValue, MongoConversionContext context) {
+							public @Nullable String read(org.bson.@Nullable Document nativeValue, MongoConversionContext context) {
 								return nativeValue.getString("bar");
 							}
 
-							@Nullable
+
 							@Override
-							public org.bson.Document write(@Nullable String domainValue, MongoConversionContext context) {
+							public org.bson.@Nullable Document write(@Nullable String domainValue, MongoConversionContext context) {
 								return new org.bson.Document("bar", domainValue);
 							}
 						});
@@ -3522,7 +3520,7 @@ static class Person implements Contact {
 
 		}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Person(Set<Address> addresses) {
 			this.addresses = addresses;
 		}
@@ -3802,7 +3800,7 @@ static class PrimitiveContainer {
 
 		@Field("property") private int m_property;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public PrimitiveContainer(@Value("#root.property") int a_property) {
 			m_property = a_property;
 		}
@@ -3817,7 +3815,7 @@ static class ObjectContainer {
 
 		@Field("property") private PrimitiveContainer m_property;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public ObjectContainer(@Value("#root.property") PrimitiveContainer a_property) {
 			m_property = a_property;
 		}
@@ -4215,9 +4213,9 @@ public SubTypeOfGenericType convert(org.bson.Document source) {
 	@WritingConverter
 	static class TypeImplementingMapToDocumentConverter implements Converter<TypeImplementingMap, org.bson.Document> {
 
-		@Nullable
+
 		@Override
-		public org.bson.Document convert(TypeImplementingMap source) {
+		public org.bson.@Nullable Document convert(TypeImplementingMap source) {
 			return new org.bson.Document("1st", source.val1).append("2nd", source.val2);
 		}
 	}
@@ -4225,9 +4223,8 @@ public org.bson.Document convert(TypeImplementingMap source) {
 	@ReadingConverter
 	static class DocumentToTypeImplementingMapConverter implements Converter<org.bson.Document, TypeImplementingMap> {
 
-		@Nullable
 		@Override
-		public TypeImplementingMap convert(org.bson.Document source) {
+		public @Nullable TypeImplementingMap convert(org.bson.Document source) {
 			return new TypeImplementingMap(source.getString("1st"), source.getInteger("2nd"));
 		}
 	}
@@ -4413,30 +4410,28 @@ enum Converter2 implements MongoValueConverter<String, org.bson.Document> {
 
 		INSTANCE;
 
-		@Nullable
 		@Override
-		public String read(@Nullable org.bson.Document value, MongoConversionContext context) {
+		public @Nullable String read(org.bson.@Nullable Document value, MongoConversionContext context) {
 			return value.getString("bar");
 		}
 
-		@Nullable
+
 		@Override
-		public org.bson.Document write(@Nullable String value, MongoConversionContext context) {
+		public org.bson.@Nullable Document write(@Nullable String value, MongoConversionContext context) {
 			return new org.bson.Document("bar", value);
 		}
 	}
 
 	static class Converter1 implements MongoValueConverter<String, org.bson.Document> {
 
-		@Nullable
 		@Override
-		public String read(@Nullable org.bson.Document value, MongoConversionContext context) {
+		public @Nullable String read(org.bson.@Nullable Document value, MongoConversionContext context) {
 			return value.getString("foo");
 		}
 
-		@Nullable
+
 		@Override
-		public org.bson.Document write(@Nullable String value, MongoConversionContext context) {
+		public org.bson.@Nullable Document write(@Nullable String value, MongoConversionContext context) {
 			return new org.bson.Document("foo", value);
 		}
 	}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java
index b772772444..9e58693faa 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java
@@ -23,7 +23,7 @@
 import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
-import org.springframework.data.util.ClassTypeInformation;
+import org.springframework.data.util.TypeInformation;
 
 /**
  * Unit tests for {@link ObjectPath}.
@@ -39,9 +39,9 @@ public class ObjectPathUnitTests {
 	@BeforeEach
 	public void setUp() {
 
-		one = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityOne.class));
-		two = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityTwo.class));
-		three = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityThree.class));
+		one = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityOne.class));
+		two = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityTwo.class));
+		three = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityThree.class));
 	}
 
 	@Test // DATAMONGO-1703
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java
index eb3b1aba1a..0fe791784d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb.core.convert;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * @author Christoph Strobl
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
index d8e36c8f67..c646af5539 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
@@ -544,7 +544,7 @@ void doesNotConvertRawDocuments() {
 	}
 
 	@Test // DATAMONG0-471
-	void testUpdateShouldRetainClassTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() {
+	void testUpdateShouldRetainTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() {
 
 		Update update = new Update().addToSet("models").each(new ModelImpl(2014), new ModelImpl(1), new ModelImpl(28));
 		Document mappedObject = mapper.getMappedObject(update.getUpdateObject(),
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
index b81b51abd5..96c685275f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
@@ -33,7 +33,7 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.geo.GeoResults;
 import org.springframework.data.geo.Metrics;
 import org.springframework.data.geo.Point;
@@ -536,7 +536,7 @@ static class Venue2DSphere {
 		private String name;
 		private @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) double[] location;
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Venue2DSphere(String name, double[] location) {
 			this.name = name;
 			this.location = location;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java
index aa26445f2d..d1fa2b1b9a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java
@@ -53,7 +53,7 @@
 import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
 import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
 import org.springframework.data.mongodb.core.mapping.Unwrapped;
-import org.springframework.data.util.ClassTypeInformation;
+import org.springframework.data.util.TypeInformation;
 
 /**
  * Tests for {@link MongoPersistentEntityIndexResolver}.
@@ -1186,7 +1186,7 @@ public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() {
 		@SuppressWarnings({ "rawtypes", "unchecked" })
 		public void shouldCatchCyclicReferenceExceptionOnRoot() {
 
-			MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Object.class));
+			MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(TypeInformation.of(Object.class));
 
 			MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class);
 			when(propertyMock.isEntity()).thenReturn(true);
@@ -1195,7 +1195,7 @@ public void shouldCatchCyclicReferenceExceptionOnRoot() {
 					new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar"));
 
 			MongoPersistentEntity<SelfCyclingViaCollectionType> selfCyclingEntity = new BasicMongoPersistentEntity<>(
-					ClassTypeInformation.from(SelfCyclingViaCollectionType.class));
+					TypeInformation.of(SelfCyclingViaCollectionType.class));
 
 			new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class))
 					.resolveIndexForEntity(selfCyclingEntity);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
index 116505143e..1037ba4f19 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
@@ -39,7 +39,7 @@
 import org.springframework.data.mapping.model.Property;
 import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
 import org.springframework.data.mapping.model.SimpleTypeHolder;
-import org.springframework.data.util.ClassTypeInformation;
+import org.springframework.data.util.TypeInformation;
 import org.springframework.util.ReflectionUtils;
 
 /**
@@ -56,7 +56,7 @@ public class BasicMongoPersistentPropertyUnitTests {
 
 	@BeforeEach
 	void setup() {
-		entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class));
+		entity = new BasicMongoPersistentEntity<>(TypeInformation.of(Person.class));
 	}
 
 	@Test
@@ -90,7 +90,7 @@ void preventsNegativeOrder() {
 	void usesPropertyAccessForThrowableCause() {
 
 		BasicMongoPersistentEntity<Throwable> entity = new BasicMongoPersistentEntity<>(
-				ClassTypeInformation.from(Throwable.class));
+				TypeInformation.of(Throwable.class));
 		MongoPersistentProperty property = getPropertyFor(entity, "cause");
 
 		assertThat(property.usePropertyAccess()).isTrue();
@@ -99,7 +99,7 @@ void usesPropertyAccessForThrowableCause() {
 	@Test // DATAMONGO-607
 	void usesCustomFieldNamingStrategyByDefault() throws Exception {
 
-		ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class);
+		TypeInformation<Person> type = TypeInformation.of(Person.class);
 		Field field = ReflectionUtils.findField(Person.class, "lastname");
 
 		MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity,
@@ -116,7 +116,7 @@ void usesCustomFieldNamingStrategyByDefault() throws Exception {
 	@Test // DATAMONGO-607
 	void rejectsInvalidValueReturnedByFieldNamingStrategy() {
 
-		ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class);
+		TypeInformation<Person> type = TypeInformation.of(Person.class);
 		Field field = ReflectionUtils.findField(Person.class, "lastname");
 
 		MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity,
@@ -255,7 +255,7 @@ private MongoPersistentProperty getPropertyFor(Field field) {
 	}
 
 	private static <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) {
-		return getPropertyFor(new BasicMongoPersistentEntity<>(ClassTypeInformation.from(type)), fieldname);
+		return getPropertyFor(new BasicMongoPersistentEntity<>(TypeInformation.of(type)), fieldname);
 	}
 
 	private static MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> entity, String fieldname) {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java
index 06f0db6c35..eaed01fc3b 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java
@@ -18,7 +18,7 @@
 import java.util.List;
 
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.annotation.Transient;
 import org.springframework.data.mongodb.core.index.CompoundIndex;
 import org.springframework.data.mongodb.core.index.CompoundIndexes;
@@ -44,7 +44,7 @@ public Person(Integer ssn) {
 		this.ssn = ssn;
 	}
 
-	@PersistenceConstructor
+	@PersistenceCreator
 	public Person(Integer ssn, String firstName, String lastName, Integer age, T address) {
 		this.ssn = ssn;
 		this.firstName = firstName;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java
index a68fe0d531..4a4f7fb126 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java
@@ -16,7 +16,7 @@
 package org.springframework.data.mongodb.core.mapping;
 
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 
 /**
  * @author Jon Brisbin <jbrisbin@vmware.com>
@@ -30,7 +30,7 @@ public PersonCustomIdName(Integer ssn, String firstName) {
 		this.firstName = firstName;
 	}
 
-	@PersistenceConstructor
+	@PersistenceCreator
 	public PersonCustomIdName(Integer ssn, String firstName, String lastName) {
 		this.ssn = ssn;
 		this.firstName = firstName;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
index 6ea0f5aa9c..b12b83fe3a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java
@@ -21,6 +21,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -35,7 +36,6 @@
 import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
 import org.springframework.data.mongodb.test.util.MongoTestTemplate;
 import org.springframework.data.mongodb.test.util.Template;
-import org.springframework.lang.Nullable;
 
 /**
  * @author Christoph Strobl
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java
deleted file mode 100644
index e70b398f7f..0000000000
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2002-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import static org.assertj.core.api.Assertions.*;
-
-import java.net.UnknownHostException;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.data.mongodb.test.util.Client;
-import org.springframework.data.mongodb.test.util.MongoClientExtension;
-
-import com.mongodb.client.MongoClient;
-
-/**
- * This test class assumes that you are already running the MongoDB server.
- *
- * @author Mark Pollack
- * @author Thomas Darimont
- * @author Mark Paluch
- */
-@ExtendWith(MongoClientExtension.class)
-public class MongoMonitorIntegrationTests {
-
-	static @Client MongoClient mongoClient;
-
-	@Test
-	public void serverInfo() {
-		ServerInfo serverInfo = new ServerInfo(mongoClient);
-		serverInfo.getVersion();
-	}
-
-	@Test // DATAMONGO-685
-	public void getHostNameShouldReturnServerNameReportedByMongo() throws UnknownHostException {
-
-		ServerInfo serverInfo = new ServerInfo(mongoClient);
-
-		String hostName = null;
-		try {
-			hostName = serverInfo.getHostName();
-		} catch (UnknownHostException e) {
-			throw e;
-		}
-
-		assertThat(hostName).isNotNull();
-		assertThat(hostName).isEqualTo("127.0.0.1:27017");
-	}
-
-	@Test
-	public void operationCounters() {
-		OperationCounters operationCounters = new OperationCounters(mongoClient);
-		operationCounters.getInsertCount();
-	}
-}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java
deleted file mode 100644
index 1fdbb1f188..0000000000
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2018-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.mongodb.monitor;
-
-import java.util.function.Supplier;
-
-/**
- * @author Christoph Strobl
- * @since 2018/01
- */
-interface Resumeable<T> {
-
-	void resumeAt(Supplier<T> token);
-}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java
index e815cc6e7c..fb8cedd9b1 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java
@@ -28,7 +28,7 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.core.Constants;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
 import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
@@ -454,7 +454,7 @@ public Address(String zipCode, String city) {
 			this(zipCode, city, new HashSet<AddressType>(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values()))));
 		}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Address(String zipCode, String city, Set<AddressType> types) {
 			this.zipCode = zipCode;
 			this.city = city;
@@ -512,7 +512,7 @@ public Order(List<LineItem> lineItems, Date createdAt) {
 			this.status = Status.ORDERED;
 		}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Order(List<LineItem> lineItems, Date createdAt, Status status) {
 			this.lineItems = lineItems;
 			this.createdAt = createdAt;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java
index edda1aad01..a7fba9a046 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java
@@ -28,11 +28,12 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.core.Constants;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
 import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
 import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
@@ -48,7 +49,6 @@
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
 import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StopWatch;
 import org.springframework.util.StringUtils;
@@ -99,9 +99,8 @@ public void setUp() throws Exception {
 
 		converter = new MappingMongoConverter(new DbRefResolver() {
 
-			@Nullable
 			@Override
-			public Object resolveReference(MongoPersistentProperty property, Object source,
+			public @Nullable Object resolveReference(MongoPersistentProperty property, Object source,
 					ReferenceLookupDelegate referenceLookupDelegate, MongoEntityReader entityReader) {
 				return null;
 			}
@@ -513,7 +512,7 @@ public Address(String zipCode, String city) {
 			this(zipCode, city, new HashSet<AddressType>(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values()))));
 		}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Address(String zipCode, String city, Set<AddressType> types) {
 			this.zipCode = zipCode;
 			this.city = city;
@@ -571,7 +570,7 @@ public Order(List<LineItem> lineItems, Date createdAt) {
 			this.status = Status.ORDERED;
 		}
 
-		@PersistenceConstructor
+		@PersistenceCreator
 		public Order(List<LineItem> lineItems, Date createdAt, Status status) {
 			this.lineItems = lineItems;
 			this.createdAt = createdAt;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java
index 534f44c8fb..be5be2d9ba 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 package org.springframework.data.mongodb.repository;
-
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.ObjectUtils;
 
 import com.querydsl.core.annotations.QueryEmbeddable;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
index c41abf4aa1..e0c2caee31 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java
@@ -20,6 +20,7 @@
 import java.util.Arrays;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -38,7 +39,6 @@
 import org.springframework.data.mongodb.test.util.MongoTemplateExtension;
 import org.springframework.data.mongodb.test.util.MongoTestTemplate;
 import org.springframework.data.mongodb.test.util.Template;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java
index 3dace8928b..4e589d5892 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java
@@ -17,7 +17,7 @@
 
 import java.io.Serializable;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
index 664b5279c8..eeca60bc3e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
@@ -21,6 +21,7 @@
 import java.util.Set;
 import java.util.UUID;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.geo.Point;
 import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
 import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
@@ -30,7 +31,6 @@
 import org.springframework.data.mongodb.core.mapping.DocumentReference;
 import org.springframework.data.mongodb.core.mapping.Field;
 import org.springframework.data.mongodb.core.mapping.Unwrapped;
-import org.springframework.lang.Nullable;
 
 /**
  * Sample domain class.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java
index 16b2157bc8..da22801ba6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java
@@ -22,7 +22,7 @@
 import java.util.Set;
 
 import org.springframework.data.annotation.Id;
-import org.springframework.data.annotation.PersistenceConstructor;
+import org.springframework.data.annotation.PersistenceCreator;
 
 /**
  * @author Christoph Strobl
@@ -37,7 +37,7 @@ public PersonAggregate(String lastname, String name) {
 		this(lastname, Collections.singletonList(name));
 	}
 
-	@PersistenceConstructor
+	@PersistenceCreator
 	public PersonAggregate(String lastname, Collection<String> names) {
 
 		this.lastname = lastname;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
index c66b554078..93a293ecff 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
@@ -23,6 +23,7 @@
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Limit;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -43,7 +44,6 @@
 import org.springframework.data.mongodb.repository.Person.Sex;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.data.repository.query.Param;
-import org.springframework.lang.Nullable;
 
 /**
  * Sample repository managing {@link Person} entities.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
index 0af684b9c1..b2b350dc4d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java
@@ -27,6 +27,7 @@
 
 import org.bson.Document;
 import org.bson.types.ObjectId;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -46,7 +47,6 @@
 import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
 import org.springframework.data.mongodb.test.util.MongoClientExtension;
 import org.springframework.data.mongodb.test.util.ReplSetClient;
-import org.springframework.lang.Nullable;
 import org.springframework.test.annotation.Rollback;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 import org.springframework.test.context.transaction.AfterTransaction;
@@ -205,9 +205,8 @@ private AfterTransactionAssertion assertAfterTransaction(Person person) {
 
 		AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable<Object>() {
 
-			@Nullable
 			@Override
-			public Object getId() {
+			public @Nullable Object getId() {
 				return person.id;
 			}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java
index e89dec21bd..1a481b49ed 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java
@@ -72,7 +72,6 @@
 import org.springframework.data.mongodb.test.util.ReactiveMongoClientClosingTestConfiguration;
 import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
 import org.springframework.data.repository.Repository;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 /**
@@ -111,7 +110,6 @@ ReactiveMongoRepositoryFactory factory(ReactiveMongoOperations template, BeanFac
 			factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class);
 			factory.setBeanClassLoader(beanFactory.getClass().getClassLoader());
 			factory.setBeanFactory(beanFactory);
-			factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
 
 			return factory;
 		}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java
index 44235c54ef..751fc51ded 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java
@@ -26,6 +26,7 @@
 import java.util.Objects;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.RepeatedTest;
 import org.junit.jupiter.api.Test;
@@ -48,8 +49,6 @@
 import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository;
 import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
 import org.springframework.data.repository.query.FluentQuery;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
-import org.springframework.lang.Nullable;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 import org.springframework.transaction.TransactionDefinition;
@@ -96,7 +95,6 @@ void setUp() {
 		factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class);
 		factory.setBeanClassLoader(classLoader);
 		factory.setBeanFactory(beanFactory);
-		factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
 
 		repository = factory.getRepository(ReactivePersonRepository.class);
 		immutableRepository = factory.getRepository(ReactiveImmutablePersonRepository.class);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java
index 606cca8647..c3bb9cb724 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java
@@ -15,9 +15,9 @@
  */
 package org.springframework.data.mongodb.repository;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.mongodb.core.mapping.Document;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ObjectUtils;
 
 /**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java
index 294e4ea501..4f1adc714e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java
@@ -17,9 +17,9 @@
 
 import java.util.Objects;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.annotation.Version;
 import org.springframework.data.mongodb.core.mapping.Document;
-import org.springframework.lang.Nullable;
 
 /**
  * @author Christoph Strobl
@@ -48,8 +48,7 @@ public String getFirstname() {
 		return this.firstname;
 	}
 
-	@Nullable
-	public String getLastname() {
+	public @Nullable String getLastname() {
 		return this.lastname;
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java
index f613beb6d5..a222deca39 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java
@@ -27,7 +27,7 @@
 import org.springframework.core.env.StandardEnvironment;
 import org.springframework.core.io.ResourceLoader;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.core.type.StandardAnnotationMetadata;
+import org.springframework.core.type.AnnotationMetadata;
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.repository.Repository;
@@ -43,13 +43,13 @@
  */
 public class MongoRepositoryConfigurationExtensionUnitTests {
 
-	StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true);
+	AnnotationMetadata metadata = AnnotationMetadata.introspect(Config.class);
 	ResourceLoader loader = new PathMatchingResourcePatternResolver();
 	Environment environment = new StandardEnvironment();
 	BeanDefinitionRegistry registry = new DefaultListableBeanFactory();
 
 	RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata,
-			EnableMongoRepositories.class, loader, environment, registry);
+			EnableMongoRepositories.class, loader, environment, registry, null);
 
 	@Test // DATAMONGO-1009
 	public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java
index 45ecba992f..2b52204f74 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java
@@ -27,7 +27,7 @@
 import org.springframework.core.env.StandardEnvironment;
 import org.springframework.core.io.ResourceLoader;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.core.type.StandardAnnotationMetadata;
+import org.springframework.core.type.AnnotationMetadata;
 import org.springframework.data.mongodb.core.mapping.Document;
 import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
 import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
@@ -43,13 +43,13 @@
  */
 public class ReactiveMongoRepositoryConfigurationExtensionUnitTests {
 
-	StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true);
+	AnnotationMetadata metadata = AnnotationMetadata.introspect(Config.class);
 	ResourceLoader loader = new PathMatchingResourcePatternResolver();
 	Environment environment = new StandardEnvironment();
 	BeanDefinitionRegistry registry = new DefaultListableBeanFactory();
 
 	RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata,
-			EnableReactiveMongoRepositories.class, loader, environment, registry);
+			EnableReactiveMongoRepositories.class, loader, environment, registry, null);
 
 	@Test // DATAMONGO-1444
 	public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java
index 74ff20b148..326ccf5f3a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java
@@ -56,8 +56,7 @@
 import org.springframework.data.repository.Repository;
 import org.springframework.data.repository.core.RepositoryMetadata;
 import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.util.ReflectionUtils;
 
 import com.mongodb.client.result.DeleteResult;
@@ -81,15 +80,13 @@ class MongoQueryExecutionUnitTests {
 	@Mock TerminatingFindNear<Object> terminatingGeoMock;
 	@Mock DbRefResolver dbRefResolver;
 
-	private SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
 	private Point POINT = new Point(10, 20);
 	private Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS);
 	private RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
 	private MongoMappingContext context = new MongoMappingContext();
 	private ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
 	private Method method = ReflectionUtils.findMethod(PersonRepository.class, "findByLocationNear", Point.class,
-			Distance.class,
-			Pageable.class);
+			Distance.class, Pageable.class);
 	private MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context);
 	private MappingMongoConverter converter;
 
@@ -152,8 +149,8 @@ void pagingGeoExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSiz
 		ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter,
 				new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(0, 10) }));
 
-		PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER,
-				QueryMethodEvaluationContextProvider.DEFAULT);
+		PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock,
+				ValueExpressionDelegate.create());
 
 		PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query);
 		execution.execute(new Query());
@@ -173,8 +170,8 @@ void pagingGeoExecutionRetrievesObjectsForPageableOutOfRange() {
 		ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter,
 				new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(2, 10) }));
 
-		PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER,
-				QueryMethodEvaluationContextProvider.DEFAULT);
+		PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock,
+				ValueExpressionDelegate.create());
 
 		PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query);
 		execution.execute(new Query());
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
index e0b9b77099..07c10592d9 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
@@ -45,8 +45,7 @@
 import org.springframework.data.projection.ProjectionFactory;
 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
 import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
 
 /**
  * Unit tests for {@link PartTreeMongoQuery}.
@@ -206,8 +205,7 @@ private PartTreeMongoQuery createQueryForMethod(String methodName, Class<?>... p
 			MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory,
 					mappingContext);
 
-			return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, new SpelExpressionParser(),
-					QueryMethodEvaluationContextProvider.DEFAULT);
+			return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, ValueExpressionDelegate.create());
 		} catch (Exception e) {
 			throw new IllegalArgumentException(e.getMessage(), e);
 		}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java
index 21d5dc71fb..d7a3430048 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java
@@ -46,7 +46,7 @@
 import org.springframework.data.mongodb.repository.Person;
 import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.DeleteExecution;
 import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.GeoNearExecution;
-import org.springframework.data.util.ClassTypeInformation;
+import org.springframework.data.util.TypeInformation;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.client.result.DeleteResult;
@@ -74,7 +74,7 @@ public void geoNearExecutionShouldApplyQuerySettings() throws Exception {
 				.thenReturn(Range.from(Bound.inclusive(new Distance(10))).to(Bound.inclusive(new Distance(15))));
 		when(parameterAccessor.getPageable()).thenReturn(PageRequest.of(1, 10));
 
-		new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query,
+		new GeoNearExecution(operations, parameterAccessor, TypeInformation.fromReturnTypeOf(geoNear)).execute(query,
 				Person.class, "person");
 
 		ArgumentCaptor<NearQuery> queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class);
@@ -96,7 +96,7 @@ public void geoNearExecutionShouldApplyMinimalSettings() throws Exception {
 		when(parameterAccessor.getGeoNearLocation()).thenReturn(new Point(1, 2));
 		when(parameterAccessor.getDistanceRange()).thenReturn(Range.unbounded());
 
-		new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query,
+		new GeoNearExecution(operations, parameterAccessor, TypeInformation.fromReturnTypeOf(geoNear)).execute(query,
 				Person.class, "person");
 
 		ArgumentCaptor<NearQuery> queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
index c6047ce30d..b55ee77732 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
@@ -27,6 +27,7 @@
 import java.util.List;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -34,6 +35,7 @@
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.reactivestreams.Publisher;
+
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -54,10 +56,8 @@
 import org.springframework.data.projection.ProjectionFactory;
 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
 import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.repository.reactive.ReactiveCrudRepository;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.ReadPreference;
@@ -71,8 +71,6 @@
 @ExtendWith(MockitoExtension.class)
 public class ReactiveStringBasedAggregationUnitTests {
 
-	SpelExpressionParser PARSER = new SpelExpressionParser();
-
 	@Mock ReactiveMongoOperations operations;
 	@Mock DbRefResolver dbRefResolver;
 	MongoConverter converter;
@@ -226,8 +224,7 @@ private ReactiveStringBasedAggregation createAggregationForMethod(String name, C
 		ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
 		ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method,
 				new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext());
-		return new ReactiveStringBasedAggregation(queryMethod, operations, PARSER,
-				ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
+		return new ReactiveStringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create());
 	}
 
 	private List<Document> pipelineOf(AggregationInvocation invocation) {
@@ -242,27 +239,24 @@ private Class<?> inputTypeOf(AggregationInvocation invocation) {
 		return invocation.aggregation.getInputType();
 	}
 
-	@Nullable
-	private Collation collationOf(AggregationInvocation invocation) {
+	private @Nullable Collation collationOf(AggregationInvocation invocation) {
 		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null)
 				: null;
 	}
 
-	@Nullable
-	private Object hintOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null)
+	private @Nullable Object hintOf(AggregationInvocation invocation) {
+		return invocation.aggregation.getOptions() != null
+				? invocation.aggregation.getOptions().getHintObject().orElse(null)
 				: null;
 	}
 
 	private Boolean skipResultsOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults()
-				: false;
+		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() : false;
 	}
 
 	@Nullable
 	private ReadPreference readPreferenceOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference()
-				: null;
+		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() : null;
 	}
 
 	private Class<?> targetTypeOf(AggregationInvocation invocation) {
@@ -284,7 +278,7 @@ private interface SampleRepository extends ReactiveCrudRepository<Person, Long>
 		@Aggregation(GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER)
 		Mono<PersonAggregate> spelParameterReplacementAggregation(String arg0);
 
-		@Aggregation(pipeline = {RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER})
+		@Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER })
 		Mono<PersonAggregate> multiOperationPipeline(String arg0);
 
 		@Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT")
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java
index 72f9626a57..7358bf4ce6 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java
@@ -56,8 +56,6 @@
 import org.springframework.data.repository.Repository;
 import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
 import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
-import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
-import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
 import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.data.spel.spi.EvaluationContextExtension;
 import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension;
@@ -248,8 +246,8 @@ public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception {
 		ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
 
 		org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
-		org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
-				"{'lastname' : { '$binary' : '" + Base64.getEncoder().encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}");
+		org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '"
+				+ Base64.getEncoder().encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}");
 
 		assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson());
 	}
@@ -266,16 +264,14 @@ void shouldConsiderReactiveSpelExtension() throws Exception {
 		assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson());
 	}
 
-	private ReactiveStringBasedMongoQuery createQueryForMethod(
-			String name, Class<?>... parameters)
-			throws Exception {
+	private ReactiveStringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
 
 		Method method = SampleRepository.class.getMethod(name, parameters);
 		ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
 		ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method,
 				new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext());
-		QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor(
-				environment, Collections.singletonList(ReactiveSpelExtension.INSTANCE));
+		QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor(environment,
+				Collections.singletonList(ReactiveSpelExtension.INSTANCE));
 		return new ReactiveStringBasedMongoQuery(queryMethod, operations, new ValueExpressionDelegate(accessor, PARSER));
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
index 85a8650b26..827168007e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
@@ -27,6 +27,7 @@
 import java.util.stream.Stream;
 
 import org.bson.Document;
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -35,6 +36,7 @@
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
+
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
@@ -62,9 +64,7 @@
 import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
 import org.springframework.data.repository.Repository;
 import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
-import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
-import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.lang.Nullable;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
 import org.springframework.util.ClassUtils;
 
 import com.mongodb.MongoClientSettings;
@@ -81,8 +81,6 @@
 @MockitoSettings(strictness = Strictness.LENIENT)
 public class StringBasedAggregationUnitTests {
 
-	private SpelExpressionParser PARSER = new SpelExpressionParser();
-
 	@Mock MongoOperations operations;
 	@Mock DbRefResolver dbRefResolver;
 	@Mock AggregationResults aggregationResults;
@@ -254,8 +252,7 @@ void aggregateRaisesErrorOnInvalidReturnType() {
 				factory, converter.getMappingContext());
 
 		assertThatExceptionOfType(InvalidMongoDbApiUsageException.class) //
-				.isThrownBy(() -> new StringBasedAggregation(queryMethod, operations, PARSER,
-						QueryMethodEvaluationContextProvider.DEFAULT)) //
+				.isThrownBy(() -> new StringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create())) //
 				.withMessageContaining("pageIsUnsupported") //
 				.withMessageContaining("Page");
 	}
@@ -311,7 +308,7 @@ private StringBasedAggregation createAggregationForMethod(String name, Class<?>.
 		ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
 		MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
 				factory, converter.getMappingContext());
-		return new StringBasedAggregation(queryMethod, operations, PARSER, QueryMethodEvaluationContextProvider.DEFAULT);
+		return new StringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create());
 	}
 
 	private List<Document> pipelineOf(AggregationInvocation invocation) {
@@ -326,27 +323,24 @@ private Class<?> inputTypeOf(AggregationInvocation invocation) {
 		return invocation.aggregation.getInputType();
 	}
 
-	@Nullable
-	private Collation collationOf(AggregationInvocation invocation) {
+	private @Nullable Collation collationOf(AggregationInvocation invocation) {
 		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null)
 				: null;
 	}
 
-	@Nullable
-	private Object hintOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null)
+	private @Nullable Object hintOf(AggregationInvocation invocation) {
+		return invocation.aggregation.getOptions() != null
+				? invocation.aggregation.getOptions().getHintObject().orElse(null)
 				: null;
 	}
 
 	private Boolean skipResultsOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults()
-				: false;
+		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() : false;
 	}
 
 	@Nullable
 	private ReadPreference readPreferenceOf(AggregationInvocation invocation) {
-		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference()
-				: null;
+		return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() : null;
 	}
 
 	private Class<?> targetTypeOf(AggregationInvocation invocation) {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
index 1927378e80..3ed7ace0f9 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java
@@ -18,6 +18,7 @@
 import java.util.Arrays;
 import java.util.Iterator;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Range;
 import org.springframework.data.domain.Range.Bound;
@@ -30,7 +31,6 @@
 import org.springframework.data.mongodb.core.query.TextCriteria;
 import org.springframework.data.mongodb.core.query.UpdateDefinition;
 import org.springframework.data.repository.query.ParameterAccessor;
-import org.springframework.lang.Nullable;
 
 /**
  * Simple {@link ParameterAccessor} that returns the given parameters unfiltered.
@@ -121,7 +121,7 @@ public Collation getCollation() {
 	 * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues()
 	 */
 	@Override
-	public Object[] getValues() {
+	public Object @Nullable[] getValues() {
 		return this.values;
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java
index 15a0538600..eda1e501a0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java
@@ -20,7 +20,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Utility to configure {@link org.springframework.data.mongodb.core.mapping.MongoMappingContext} properties.
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java
index 09149c02ef..8300690ccd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java
@@ -20,6 +20,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.ObjectFactory;
 import org.springframework.context.ApplicationContext;
@@ -39,7 +40,6 @@
 import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
 import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback;
 import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent;
-import org.springframework.lang.Nullable;
 
 /**
  * @author Christoph Strobl
diff --git a/spring-data-mongodb/src/test/resources/server-jmx.xml b/spring-data-mongodb/src/test/resources/server-jmx.xml
deleted file mode 100644
index 54f985f4cb..0000000000
--- a/spring-data-mongodb/src/test/resources/server-jmx.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xmlns:p="http://www.springframework.org/schema/p"
-	xmlns:mongo="http://www.springframework.org/schema/data/mongo"
-	xmlns:context="http://www.springframework.org/schema/context"
-	xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
-		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
-		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
-
-  <mongo:jmx/>
-
-  <context:mbean-export/>
-
-  <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"
-        p:port="1099"/>
-
-  <!-- Expose JMX over RMI -->
-  <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry"
-        p:objectName="connector:name=rmi"
-        p:serviceUrl="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/>
-
-</beans>
\ No newline at end of file
diff --git a/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc b/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc
index d76266c36a..3b5b4e49fe 100644
--- a/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc
+++ b/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc
@@ -465,7 +465,7 @@ This can be a single value (the _id_ by default), or a `Document` provided via a
 * `@Transient`: By default, all fields are mapped to the document.
 This annotation excludes the field where it is applied from being stored in the database.
 Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument.
-* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database.
+* `@PersistenceCreator`: Marks a given constructor or a `static` factory method - even a package protected one - to use when instantiating the object from the database.
 Constructor arguments are mapped by name to the key values in the retrieved Document.
 * `@Value`: This annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments.
 This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object.
@@ -513,7 +513,7 @@ public class Person<T extends Address> {
     this.ssn = ssn;
   }
 
-  @PersistenceConstructor
+  @PersistenceCreator
   public Person(Integer ssn, String firstName, String lastName, Integer age, T address) {
     this.ssn = ssn;
     this.firstName = firstName;
@@ -673,7 +673,7 @@ Increased levels of nesting increase the complexity of the aggregation expressio
 [[mapping-custom-object-construction]]
 === Customized Object Construction
 
-The mapping subsystem allows the customization of the object construction by annotating a constructor with the `@PersistenceConstructor` annotation.
+The mapping subsystem allows the customization of the object construction by annotating a constructor with the `@PersistenceCreator` annotation.
 The values to be used for the constructor parameters are resolved in the following way:
 
 * If a parameter is annotated with the `@Value` annotation, the given expression is evaluated and the result is used as the parameter value.
@@ -706,7 +706,7 @@ OrderItem item = converter.read(OrderItem.class, input);
 
 NOTE: The SpEL expression in the `@Value` annotation of the `quantity` parameter falls back to the value `0` if the given property path cannot be resolved.
 
-Additional examples for using the `@PersistenceConstructor` annotation can be found in the https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java[MappingMongoConverterUnitTests] test suite.
+Additional examples for using the `@PersistenceCreator` annotation can be found in the https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java[MappingMongoConverterUnitTests] test suite.
 
 [[mapping-usage-events]]
 === Mapping Framework Events
diff --git a/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc b/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc
index a424748205..697af23a9e 100644
--- a/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc
+++ b/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc
@@ -342,7 +342,7 @@ public class Venue {
   private String name;
   private double[] location;
 
-  @PersistenceConstructor
+  @PersistenceCreator
   Venue(String name, double[] location) {
     super();
     this.name = name;
diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt
index 61b472b23b..776445bac4 100644
--- a/src/main/resources/notice.txt
+++ b/src/main/resources/notice.txt
@@ -1,4 +1,4 @@
-Spring Data MongoDB 4.5 M2 (2025.0.0)
+Spring Data MongoDB 5.0 M1 (2025.1.0)
 Copyright (c) [2010-2019] Pivotal Software, Inc.
 
 This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -58,6 +58,5 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
 
 
 
-