Switch to JSpecify annotations

This commit updates the whole Spring Framework codebase to use JSpecify
annotations instead of Spring null-safety annotations with JSR 305
semantics.

JSpecify provides signficant enhancements such as properly defined
specifications, a canonical dependency with no split-package issue,
better tooling, better Kotlin integration and the capability to specify
generic type, array and varargs element null-safety. Generic type
null-safety is not defined by this commit yet and will be specified
later.

A key difference is that Spring null-safety annotations, following
JSR 305 semantics, apply to fields, parameters and return values,
while JSpecify annotations apply to type usages. That's why this
commit moves nullability annotations closer to the type for fields
and return values.

See gh-28797
This commit is contained in:
Sébastien Deleuze
2024-12-03 15:22:37 +01:00
parent fcb8aed03f
commit bc5d771a06
3459 changed files with 14118 additions and 22059 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.jdbc.config;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.PropertyValue;
@@ -33,7 +34,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.AbstractDriverBasedDataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactoryBean;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -188,8 +188,7 @@ class JdbcNamespaceIntegrationTests {
}
}
@Nullable
private String extractDataSourceUrl(String file) {
private @Nullable String extractDataSourceUrl(String file) {
try (ConfigurableApplicationContext context = context(file)) {
DataSource dataSource = context.getBean(DataSource.class);
assertNumRowsInTestTable(new JdbcTemplate(dataSource), 1);

View File

@@ -24,11 +24,11 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.IntFunction;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.lang.Nullable;
import org.springframework.util.function.ThrowingBiFunction;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -20,13 +20,13 @@ import java.util.ArrayList;
import java.util.List;
import org.assertj.core.util.Strings;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.jdbc.datasource.init.ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;

View File

@@ -29,6 +29,7 @@ import java.util.Map;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -36,7 +37,6 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.Customer;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -108,7 +108,7 @@ class SqlQueryTests {
SqlQuery<Integer> query = new MappingSqlQueryWithParameters<>() {
@Override
protected Integer mapRow(ResultSet rs, int rownum, @Nullable Object[] params, @Nullable Map<? ,?> context)
protected Integer mapRow(ResultSet rs, int rownum, Object @Nullable [] params, @Nullable Map<? ,?> context)
throws SQLException {
assertThat(params).as("params were null").isNull();
assertThat(context).as("context was null").isNull();

View File

@@ -18,9 +18,10 @@ package org.springframework.jdbc.support;
import java.sql.SQLException;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.lang.Nullable;
/**
* Custom SQLException translation for testing.

View File

@@ -24,6 +24,7 @@ import java.sql.SQLException;
import javax.sql.DataSource;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.dao.CannotAcquireLockException;
@@ -33,7 +34,6 @@ import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.InvalidResultSetAccessException;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -139,8 +139,7 @@ class SQLErrorCodeSQLExceptionTranslatorTests {
translator = new SQLErrorCodeSQLExceptionTranslator() {
@SuppressWarnings("deprecation")
@Override
@Nullable
protected DataAccessException customTranslate(String task, @Nullable String sql, SQLException sqlException) {
protected @Nullable DataAccessException customTranslate(String task, @Nullable String sql, SQLException sqlException) {
assertThat(task).isEqualTo(TASK);
assertThat(sql).isEqualTo(SQL);
return (sqlException == badSqlEx) ? customDex : null;

View File

@@ -18,6 +18,7 @@ package org.springframework.jdbc.support;
import java.sql.SQLException;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.dao.CannotAcquireLockException;
@@ -28,7 +29,6 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

View File

@@ -16,13 +16,13 @@
package org.springframework.jdbc.core
import java.sql.*
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.sql.JDBCType
import java.sql.ResultSet
/**
* Mock object based tests for [JdbcOperations] Kotlin extensions
@@ -54,7 +54,7 @@ class JdbcOperationsExtensionsTests {
fun `queryForObject with nullable RowMapper-like function`() {
every { template.queryForObject(sql, any<RowMapper<Int>>(), 3) } returns null
assertThat(template.queryForObject<Int?>(sql, 3) { _, _ -> null }).isNull()
verify { template.queryForObject(eq(sql), any<RowMapper<Int?>>(), eq(3)) }
verify { template.queryForObject(eq(sql), any<RowMapper<Int>>(), eq(3)) }
}
@Test
@@ -115,9 +115,9 @@ class JdbcOperationsExtensionsTests {
@Test // gh-22682
fun `query with nullable ResultSetExtractor-like function`() {
every { template.query(eq(sql), any<ResultSetExtractor<Int?>>(), eq(3)) } returns null
every { template.query(eq(sql), any<ResultSetExtractor<Int>>(), eq(3)) } returns null
assertThat(template.query<Int?>(sql, 3) { _ -> null }).isNull()
verify { template.query(eq(sql), any<ResultSetExtractor<Int?>>(), eq(3)) }
verify { template.query(eq(sql), any<ResultSetExtractor<Int>>(), eq(3)) }
}
@Suppress("RemoveExplicitTypeArguments")