diff --git a/spring-core/src/main/java/org/springframework/cglib/core/ReflectUtils.java b/spring-core/src/main/java/org/springframework/cglib/core/ReflectUtils.java index 0d05b3157cc..74b1ffe012f 100644 --- a/spring-core/src/main/java/org/springframework/cglib/core/ReflectUtils.java +++ b/spring-core/src/main/java/org/springframework/cglib/core/ReflectUtils.java @@ -437,7 +437,7 @@ public class ReflectUtils { return defineClass(className, b, loader, protectionDomain, null); } - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "serial"}) public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain, Class contextClass) throws Exception { @@ -514,6 +514,16 @@ public class ReflectUtils { MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(contextClass, MethodHandles.lookup()); c = lookup.defineClass(b); } + catch (IllegalAccessException ex) { + throw new CodeGenerationException(ex) { + @Override + public String getMessage() { + return "ClassLoader mismatch for [" + contextClass.getName() + + "]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " + + "for ClassLoader.defineClass to be accessible on " + loader.getClass().getName(); + } + }; + } catch (Throwable ex) { throw new CodeGenerationException(ex); } diff --git a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java index a9321e0a34b..5642bafa5ae 100644 --- a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java +++ b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -180,26 +180,25 @@ public final class CollectionFactory { @SuppressWarnings("unchecked") public static Collection createCollection(Class collectionType, @Nullable Class elementType, int capacity) { Assert.notNull(collectionType, "Collection type must not be null"); - if (collectionType.isInterface()) { - if (Set.class == collectionType || Collection.class == collectionType) { - return new LinkedHashSet<>(capacity); - } - else if (List.class == collectionType) { - return new ArrayList<>(capacity); - } - else if (SortedSet.class == collectionType || NavigableSet.class == collectionType) { - return new TreeSet<>(); - } - else { - throw new IllegalArgumentException("Unsupported Collection interface: " + collectionType.getName()); - } + if (LinkedHashSet.class == collectionType || HashSet.class == collectionType || + Set.class == collectionType || Collection.class == collectionType) { + return new LinkedHashSet<>(capacity); + } + else if (ArrayList.class == collectionType || List.class == collectionType) { + return new ArrayList<>(capacity); + } + else if (LinkedList.class == collectionType) { + return new LinkedList<>(); + } + else if (SortedSet.class == collectionType || NavigableSet.class == collectionType) { + return new TreeSet<>(); } else if (EnumSet.class.isAssignableFrom(collectionType)) { Assert.notNull(elementType, "Cannot create EnumSet for unknown element type"); return EnumSet.noneOf(asEnumType(elementType)); } else { - if (!Collection.class.isAssignableFrom(collectionType)) { + if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) { throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName()); } try { diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 1aa67d2384e..c779b00b9fd 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -221,9 +221,8 @@ public class ResolvableType implements Serializable { /** * Return the underlying source of the resolvable type. Will return a {@link Field}, * {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType} - * was constructed. With the exception of the {@link #NONE} constant, this method will - * never return {@code null}. This method is primarily to provide access to additional - * type information or meta-data that alternative JVM languages may provide. + * was constructed. This method is primarily to provide access to additional type + * information or meta-data that alternative JVM languages may provide. */ public Object getSource() { Object source = (this.typeProvider != null ? this.typeProvider.getSource() : null); @@ -1096,20 +1095,20 @@ public class ResolvableType implements Serializable { * convey generic information but if it implements {@link ResolvableTypeProvider} a * more precise {@link ResolvableType} can be used than the simple one based on * the {@link #forClass(Class) Class instance}. - * @param instance the instance - * @return a {@link ResolvableType} for the specified instance + * @param instance the instance (possibly {@code null}) + * @return a {@link ResolvableType} for the specified instance, + * or {@code NONE} for {@code null} * @since 4.2 * @see ResolvableTypeProvider */ - public static ResolvableType forInstance(Object instance) { - Assert.notNull(instance, "Instance must not be null"); + public static ResolvableType forInstance(@Nullable Object instance) { if (instance instanceof ResolvableTypeProvider resolvableTypeProvider) { ResolvableType type = resolvableTypeProvider.getResolvableType(); if (type != null) { return type; } } - return ResolvableType.forClass(instance.getClass()); + return (instance != null ? forClass(instance.getClass()) : NONE); } /** diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index 71b17e0da02..48641a7c70c 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -146,11 +146,9 @@ class ResolvableTypeTests { assertThat(typeVariable.isAssignableFrom(raw)).isTrue(); } - @Test - void forInstanceMustNotBeNull() throws Exception { - assertThatIllegalArgumentException() - .isThrownBy(() -> ResolvableType.forInstance(null)) - .withMessage("Instance must not be null"); + @Test // gh-28776 + void forInstanceNull() throws Exception { + assertThat(ResolvableType.forInstance(null)).isEqualTo(ResolvableType.NONE); } @Test diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java index 3d7edf0e8e2..71f599b6804 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -26,6 +26,9 @@ import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; @@ -98,6 +101,9 @@ public abstract class StatementCreatorUtils { javaTypeToSqlTypeMap.put(double.class, Types.DOUBLE); javaTypeToSqlTypeMap.put(Double.class, Types.DOUBLE); javaTypeToSqlTypeMap.put(BigDecimal.class, Types.DECIMAL); + javaTypeToSqlTypeMap.put(LocalDate.class, Types.DATE); + javaTypeToSqlTypeMap.put(LocalTime.class, Types.TIME); + javaTypeToSqlTypeMap.put(LocalDateTime.class, Types.TIMESTAMP); javaTypeToSqlTypeMap.put(java.sql.Date.class, Types.DATE); javaTypeToSqlTypeMap.put(java.sql.Time.class, Types.TIME); javaTypeToSqlTypeMap.put(java.sql.Timestamp.class, Types.TIMESTAMP);