Skip to content

Commit dc7f434

Browse files
committed
Add nullability annotations to module/spring-boot-jdbc
See gh-46587
1 parent 40d0560 commit dc7f434

40 files changed

+325
-182
lines changed

module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceBuilder.java

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
import oracle.ucp.jdbc.PoolDataSourceImpl;
3636
import org.apache.commons.dbcp2.BasicDataSource;
3737
import org.h2.jdbcx.JdbcDataSource;
38+
import org.jspecify.annotations.Nullable;
3839
import org.postgresql.ds.PGSimpleDataSource;
3940
import org.vibur.dbcp.ViburDBCPDataSource;
4041

4142
import org.springframework.beans.BeanUtils;
4243
import org.springframework.core.ResolvableType;
4344
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
45+
import org.springframework.lang.Contract;
4446
import org.springframework.util.Assert;
4547
import org.springframework.util.ClassUtils;
4648
import org.springframework.util.ReflectionUtils;
@@ -89,15 +91,15 @@
8991
*/
9092
public final class DataSourceBuilder<T extends DataSource> {
9193

92-
private final ClassLoader classLoader;
94+
private final @Nullable ClassLoader classLoader;
9395

9496
private final Map<DataSourceProperty, String> values = new HashMap<>();
9597

96-
private Class<T> type;
98+
private @Nullable Class<T> type;
9799

98-
private final DataSource deriveFrom;
100+
private final @Nullable DataSource deriveFrom;
99101

100-
private DataSourceBuilder(ClassLoader classLoader) {
102+
private DataSourceBuilder(@Nullable ClassLoader classLoader) {
101103
this.classLoader = classLoader;
102104
this.deriveFrom = null;
103105
}
@@ -117,7 +119,7 @@ private DataSourceBuilder(T deriveFrom) {
117119
* @return this builder
118120
*/
119121
@SuppressWarnings("unchecked")
120-
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
122+
public <D extends DataSource> DataSourceBuilder<D> type(@Nullable Class<D> type) {
121123
this.type = (Class<T>) type;
122124
return (DataSourceBuilder<D>) this;
123125
}
@@ -147,7 +149,7 @@ public DataSourceBuilder<T> driverClassName(String driverClassName) {
147149
* @param username the user name
148150
* @return this builder
149151
*/
150-
public DataSourceBuilder<T> username(String username) {
152+
public DataSourceBuilder<T> username(@Nullable String username) {
151153
set(DataSourceProperty.USERNAME, username);
152154
return this;
153155
}
@@ -157,12 +159,12 @@ public DataSourceBuilder<T> username(String username) {
157159
* @param password the password
158160
* @return this builder
159161
*/
160-
public DataSourceBuilder<T> password(String password) {
162+
public DataSourceBuilder<T> password(@Nullable String password) {
161163
set(DataSourceProperty.PASSWORD, password);
162164
return this;
163165
}
164166

165-
private void set(DataSourceProperty property, String value) {
167+
private void set(DataSourceProperty property, @Nullable String value) {
166168
this.values.put(property, value);
167169
}
168170

@@ -178,7 +180,8 @@ public T build() {
178180
Set<DataSourceProperty> applied = new HashSet<>();
179181
for (DataSourceProperty property : DataSourceProperty.values()) {
180182
String value = this.values.get(property);
181-
if (value == null && deriveFromProperties != null && properties.canSet(property)) {
183+
if (value == null && deriveFromProperties != null && this.deriveFrom != null
184+
&& properties.canSet(property)) {
182185
value = deriveFromProperties.get(this.deriveFrom, property);
183186
}
184187
if (value != null) {
@@ -193,14 +196,14 @@ public T build() {
193196
DatabaseDriver driver = DatabaseDriver.fromJdbcUrl(url);
194197
String driverClassName = driver.getDriverClassName();
195198
if (driverClassName != null) {
196-
properties.set(dataSource, DataSourceProperty.DRIVER_CLASS_NAME, driver.getDriverClassName());
199+
properties.set(dataSource, DataSourceProperty.DRIVER_CLASS_NAME, driverClassName);
197200
}
198201
}
199202
return dataSource;
200203
}
201204

202205
@SuppressWarnings("unchecked")
203-
private DataSourceProperties<DataSource> getDeriveFromProperties() {
206+
private @Nullable DataSourceProperties<DataSource> getDeriveFromProperties() {
204207
if (this.deriveFrom == null) {
205208
return null;
206209
}
@@ -220,7 +223,7 @@ public static DataSourceBuilder<?> create() {
220223
* @param classLoader the classloader used to discover preferred settings
221224
* @return a new {@link DataSource} builder instance
222225
*/
223-
public static DataSourceBuilder<?> create(ClassLoader classLoader) {
226+
public static DataSourceBuilder<?> create(@Nullable ClassLoader classLoader) {
224227
return new DataSourceBuilder<>(classLoader);
225228
}
226229

@@ -258,7 +261,7 @@ private static DataSource unwrap(DataSource dataSource) {
258261
* @param classLoader the classloader used to discover preferred settings
259262
* @return the preferred {@link DataSource} type
260263
*/
261-
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
264+
public static @Nullable Class<? extends DataSource> findType(@Nullable ClassLoader classLoader) {
262265
MappedDataSourceProperties<?> mappings = MappedDataSourceProperties.forType(classLoader, null);
263266
return (mappings != null) ? mappings.getDataSourceInstanceType() : null;
264267
}
@@ -294,15 +297,15 @@ public String toString() {
294297
return this.names[0];
295298
}
296299

297-
Method findSetter(Class<?> type) {
300+
@Nullable Method findSetter(Class<?> type) {
298301
return findMethod("set", type, String.class);
299302
}
300303

301-
Method findGetter(Class<?> type) {
304+
@Nullable Method findGetter(Class<?> type) {
302305
return findMethod("get", type);
303306
}
304307

305-
private Method findMethod(String prefix, Class<?> type, Class<?>... paramTypes) {
308+
private @Nullable Method findMethod(String prefix, Class<?> type, Class<?>... paramTypes) {
306309
for (String name : this.names) {
307310
String candidate = prefix + StringUtils.capitalize(name);
308311
Method method = ReflectionUtils.findMethod(type, candidate, paramTypes);
@@ -323,11 +326,16 @@ private interface DataSourceProperties<T extends DataSource> {
323326

324327
void set(T dataSource, DataSourceProperty property, String value);
325328

326-
String get(T dataSource, DataSourceProperty property);
329+
@Nullable String get(T dataSource, DataSourceProperty property);
327330

328-
static <T extends DataSource> DataSourceProperties<T> forType(ClassLoader classLoader, Class<T> type) {
331+
static <T extends DataSource> DataSourceProperties<T> forType(@Nullable ClassLoader classLoader,
332+
@Nullable Class<T> type) {
329333
MappedDataSourceProperties<T> mapped = MappedDataSourceProperties.forType(classLoader, type);
330-
return (mapped != null) ? mapped : new ReflectionDataSourceProperties<>(type);
334+
if (mapped != null) {
335+
return mapped;
336+
}
337+
Assert.state(type != null, "No supported DataSource type found");
338+
return new ReflectionDataSourceProperties<>(type);
331339
}
332340

333341
}
@@ -338,22 +346,29 @@ private static class MappedDataSourceProperties<T extends DataSource> implements
338346

339347
private final Class<T> dataSourceType;
340348

341-
@SuppressWarnings("unchecked")
342349
MappedDataSourceProperties() {
343-
this.dataSourceType = (Class<T>) ResolvableType.forClass(MappedDataSourceProperties.class, getClass())
350+
this.dataSourceType = getGeneric();
351+
}
352+
353+
@SuppressWarnings("unchecked")
354+
private Class<T> getGeneric() {
355+
Class<T> generic = (Class<T>) ResolvableType.forClass(MappedDataSourceProperties.class, getClass())
344356
.resolveGeneric();
357+
Assert.state(generic != null, "'generic' must not be null");
358+
return generic;
345359
}
346360

347361
@Override
348362
public Class<? extends T> getDataSourceInstanceType() {
349363
return this.dataSourceType;
350364
}
351365

352-
protected void add(DataSourceProperty property, Getter<T, String> getter, Setter<T, String> setter) {
366+
protected void add(DataSourceProperty property, @Nullable Getter<T, String> getter, Setter<T, String> setter) {
353367
add(property, String.class, getter, setter);
354368
}
355369

356-
protected <V> void add(DataSourceProperty property, Class<V> type, Getter<T, V> getter, Setter<T, V> setter) {
370+
protected <V> void add(DataSourceProperty property, Class<V> type, @Nullable Getter<T, V> getter,
371+
Setter<T, V> setter) {
357372
this.mappedProperties.put(property, new MappedDataSourceProperty<>(property, type, getter, setter));
358373
}
359374

@@ -371,31 +386,32 @@ public void set(T dataSource, DataSourceProperty property, String value) {
371386
}
372387

373388
@Override
374-
public String get(T dataSource, DataSourceProperty property) {
389+
public @Nullable String get(T dataSource, DataSourceProperty property) {
375390
MappedDataSourceProperty<T, ?> mappedProperty = getMapping(property);
376391
if (mappedProperty != null) {
377392
return mappedProperty.get(dataSource);
378393
}
379394
return null;
380395
}
381396

382-
private MappedDataSourceProperty<T, ?> getMapping(DataSourceProperty property) {
397+
private @Nullable MappedDataSourceProperty<T, ?> getMapping(DataSourceProperty property) {
383398
MappedDataSourceProperty<T, ?> mappedProperty = this.mappedProperties.get(property);
384399
UnsupportedDataSourcePropertyException.throwIf(!property.isOptional() && mappedProperty == null,
385400
() -> "No mapping found for " + property);
386401
return mappedProperty;
387402
}
388403

389-
static <T extends DataSource> MappedDataSourceProperties<T> forType(ClassLoader classLoader, Class<T> type) {
404+
static <T extends DataSource> @Nullable MappedDataSourceProperties<T> forType(@Nullable ClassLoader classLoader,
405+
@Nullable Class<T> type) {
390406
MappedDataSourceProperties<T> pooled = lookupPooled(classLoader, type);
391407
if (type == null || pooled != null) {
392408
return pooled;
393409
}
394410
return lookupBasic(classLoader, type);
395411
}
396412

397-
private static <T extends DataSource> MappedDataSourceProperties<T> lookupPooled(ClassLoader classLoader,
398-
Class<T> type) {
413+
private static <T extends DataSource> @Nullable MappedDataSourceProperties<T> lookupPooled(
414+
@Nullable ClassLoader classLoader, @Nullable Class<T> type) {
399415
MappedDataSourceProperties<T> result = null;
400416
result = lookup(classLoader, type, result, "com.zaxxer.hikari.HikariDataSource",
401417
HikariDataSourceProperties::new);
@@ -412,8 +428,8 @@ private static <T extends DataSource> MappedDataSourceProperties<T> lookupPooled
412428
return result;
413429
}
414430

415-
private static <T extends DataSource> MappedDataSourceProperties<T> lookupBasic(ClassLoader classLoader,
416-
Class<T> dataSourceType) {
431+
private static <T extends DataSource> @Nullable MappedDataSourceProperties<T> lookupBasic(
432+
@Nullable ClassLoader classLoader, Class<T> dataSourceType) {
417433
MappedDataSourceProperties<T> result = null;
418434
result = lookup(classLoader, dataSourceType, result,
419435
"org.springframework.jdbc.datasource.SimpleDriverDataSource", SimpleDataSourceProperties::new);
@@ -427,8 +443,9 @@ private static <T extends DataSource> MappedDataSourceProperties<T> lookupBasic(
427443
}
428444

429445
@SuppressWarnings("unchecked")
430-
private static <T extends DataSource> MappedDataSourceProperties<T> lookup(ClassLoader classLoader,
431-
Class<T> dataSourceType, MappedDataSourceProperties<T> existing, String dataSourceClassName,
446+
private static <T extends DataSource> @Nullable MappedDataSourceProperties<T> lookup(
447+
@Nullable ClassLoader classLoader, @Nullable Class<T> dataSourceType,
448+
@Nullable MappedDataSourceProperties<T> existing, String dataSourceClassName,
432449
Supplier<MappedDataSourceProperties<?>> propertyMappingsSupplier, String... requiredClassNames) {
433450
if (existing != null || !allPresent(classLoader, dataSourceClassName, requiredClassNames)) {
434451
return existing;
@@ -439,7 +456,7 @@ private static <T extends DataSource> MappedDataSourceProperties<T> lookup(Class
439456
? (MappedDataSourceProperties<T>) propertyMappings : null;
440457
}
441458

442-
private static boolean allPresent(ClassLoader classLoader, String dataSourceClassName,
459+
private static boolean allPresent(@Nullable ClassLoader classLoader, String dataSourceClassName,
443460
String[] requiredClassNames) {
444461
boolean result = ClassUtils.isPresent(dataSourceClassName, classLoader);
445462
for (String requiredClassName : requiredClassNames) {
@@ -456,11 +473,12 @@ private static class MappedDataSourceProperty<T extends DataSource, V> {
456473

457474
private final Class<V> type;
458475

459-
private final Getter<T, V> getter;
476+
private final @Nullable Getter<T, V> getter;
460477

461-
private final Setter<T, V> setter;
478+
private final @Nullable Setter<T, V> setter;
462479

463-
MappedDataSourceProperty(DataSourceProperty property, Class<V> type, Getter<T, V> getter, Setter<T, V> setter) {
480+
MappedDataSourceProperty(DataSourceProperty property, Class<V> type, @Nullable Getter<T, V> getter,
481+
@Nullable Setter<T, V> setter) {
464482
this.property = property;
465483
this.type = type;
466484
this.getter = getter;
@@ -481,7 +499,7 @@ void set(T dataSource, String value) {
481499
}
482500
}
483501

484-
String get(T dataSource) {
502+
@Nullable String get(T dataSource) {
485503
try {
486504
if (this.getter == null) {
487505
UnsupportedDataSourcePropertyException.throwIf(!this.property.isOptional(),
@@ -506,7 +524,8 @@ private V convertFromString(String value) {
506524
throw new IllegalStateException("Unsupported value type " + this.type);
507525
}
508526

509-
private String convertToString(V value) {
527+
@Contract("!null -> !null")
528+
private @Nullable String convertToString(@Nullable V value) {
510529
if (value == null) {
511530
return null;
512531
}
@@ -530,7 +549,6 @@ private static class ReflectionDataSourceProperties<T extends DataSource> implem
530549
private final Class<T> dataSourceType;
531550

532551
ReflectionDataSourceProperties(Class<T> dataSourceType) {
533-
Assert.state(dataSourceType != null, "No supported DataSource type found");
534552
Map<DataSourceProperty, Method> getters = new HashMap<>();
535553
Map<DataSourceProperty, Method> setters = new HashMap<>();
536554
for (DataSourceProperty property : DataSourceProperty.values()) {
@@ -542,7 +560,8 @@ private static class ReflectionDataSourceProperties<T extends DataSource> implem
542560
this.setters = Collections.unmodifiableMap(setters);
543561
}
544562

545-
private void putIfNotNull(Map<DataSourceProperty, Method> map, DataSourceProperty property, Method method) {
563+
private void putIfNotNull(Map<DataSourceProperty, Method> map, DataSourceProperty property,
564+
@Nullable Method method) {
546565
if (method != null) {
547566
map.put(property, method);
548567
}
@@ -567,15 +586,15 @@ public void set(T dataSource, DataSourceProperty property, String value) {
567586
}
568587

569588
@Override
570-
public String get(T dataSource, DataSourceProperty property) {
589+
public @Nullable String get(T dataSource, DataSourceProperty property) {
571590
Method method = getMethod(property, this.getters);
572591
if (method != null) {
573592
return (String) ReflectionUtils.invokeMethod(method, dataSource);
574593
}
575594
return null;
576595
}
577596

578-
private Method getMethod(DataSourceProperty property, Map<DataSourceProperty, Method> methods) {
597+
private @Nullable Method getMethod(DataSourceProperty property, Map<DataSourceProperty, Method> methods) {
579598
Method method = methods.get(property);
580599
if (method == null) {
581600
UnsupportedDataSourcePropertyException.throwIf(!property.isOptional(),
@@ -591,7 +610,7 @@ private Method getMethod(DataSourceProperty property, Map<DataSourceProperty, Me
591610
@FunctionalInterface
592611
private interface Getter<T, V> {
593612

594-
V get(T instance) throws SQLException;
613+
@Nullable V get(T instance) throws SQLException;
595614

596615
}
597616

module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceBuilderRuntimeHints.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
import javax.sql.DataSource;
2424

25+
import org.jspecify.annotations.Nullable;
26+
2527
import org.springframework.aot.hint.MemberCategory;
2628
import org.springframework.aot.hint.RuntimeHints;
2729
import org.springframework.aot.hint.RuntimeHintsRegistrar;
@@ -51,7 +53,7 @@ class DataSourceBuilderRuntimeHints implements RuntimeHintsRegistrar {
5153
}
5254

5355
@Override
54-
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
56+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
5557
for (String typeName : TYPE_NAMES) {
5658
hints.reflection()
5759
.registerTypeIfPresent(classLoader, typeName,

module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import javax.sql.DataSource;
2222

23+
import org.jspecify.annotations.Nullable;
24+
2325
import org.springframework.aop.framework.AopProxyUtils;
2426
import org.springframework.aop.support.AopUtils;
2527
import org.springframework.jdbc.datasource.DelegatingDataSource;
@@ -53,7 +55,8 @@ private DataSourceUnwrapper() {
5355
* @since 2.3.8
5456
* @see Wrapper#unwrap(Class)
5557
*/
56-
public static <I, T extends I> T unwrap(DataSource dataSource, Class<I> unwrapInterface, Class<T> target) {
58+
public static <I, T extends I> @Nullable T unwrap(DataSource dataSource, Class<I> unwrapInterface,
59+
Class<T> target) {
5760
if (target.isInstance(dataSource)) {
5861
return target.cast(dataSource);
5962
}
@@ -86,11 +89,11 @@ public static <I, T extends I> T unwrap(DataSource dataSource, Class<I> unwrapIn
8689
* @param <T> the target type
8790
* @return an object that implements the target type or {@code null}
8891
*/
89-
public static <T> T unwrap(DataSource dataSource, Class<T> target) {
92+
public static <T> @Nullable T unwrap(DataSource dataSource, Class<T> target) {
9093
return unwrap(dataSource, target, target);
9194
}
9295

93-
private static <S> S safeUnwrap(Wrapper wrapper, Class<S> target) {
96+
private static <S> @Nullable S safeUnwrap(Wrapper wrapper, Class<S> target) {
9497
try {
9598
if (target.isInterface() && wrapper.isWrapperFor(target)) {
9699
return wrapper.unwrap(target);
@@ -104,7 +107,7 @@ private static <S> S safeUnwrap(Wrapper wrapper, Class<S> target) {
104107

105108
private static final class DelegatingDataSourceUnwrapper {
106109

107-
private static DataSource getTargetDataSource(DataSource dataSource) {
110+
private static @Nullable DataSource getTargetDataSource(DataSource dataSource) {
108111
if (dataSource instanceof DelegatingDataSource delegatingDataSource) {
109112
return delegatingDataSource.getTargetDataSource();
110113
}

0 commit comments

Comments
 (0)