diff --git a/benchmark/src/main/java/org/jdbi/v3/benchmark/ConstructorBenchmark.java b/benchmark/src/main/java/org/jdbi/v3/benchmark/ConstructorBenchmark.java index 3551e04c97..f0ec84eaea 100644 --- a/benchmark/src/main/java/org/jdbi/v3/benchmark/ConstructorBenchmark.java +++ b/benchmark/src/main/java/org/jdbi/v3/benchmark/ConstructorBenchmark.java @@ -59,7 +59,7 @@ public class ConstructorBenchmark { public static void main(final String[] args) throws RunnerException { final Options options = new OptionsBuilder() - .include(BeanBindingBenchmark.class.getSimpleName()) + .include(ConstructorBenchmark.class.getSimpleName()) .forks(0) .build(); new Runner(options).run(); diff --git a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorInstanceFactory.java b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorInstanceFactory.java index 06c8c3411f..c498348d46 100644 --- a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorInstanceFactory.java +++ b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorInstanceFactory.java @@ -13,38 +13,44 @@ */ package org.jdbi.v3.core.mapper.reflect; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.Arrays; +import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; import org.jdbi.v3.core.internal.exceptions.Unchecked; import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toUnmodifiableList; class ConstructorInstanceFactory extends InstanceFactory { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private final Constructor constructor; - - private final Supplier typeSupplier; + private final List types; + private final MethodHandle constructorHandle; ConstructorInstanceFactory(Constructor constructor) { super(constructor); this.constructor = requireNonNull(constructor, "constructor is null"); - this.typeSupplier = getTypeSupplier(constructor, super::getTypes); + this.types = extractTypes(constructor, super::getTypes); + this.constructorHandle = getConstructorMethodHandle(constructor); } @Override - Type[] getTypes() { - return typeSupplier.get(); + List getTypes() { + return types; } @Override T newInstance(Object... params) { - return Unchecked.function(constructor::newInstance).apply(params); + return (T) Unchecked.function(constructorHandle::invokeWithArguments).apply(params); } @Override @@ -76,13 +82,21 @@ private static boolean isGenericInformationLost(Constructor factory) { return lossDetected; } - private static Supplier getTypeSupplier(Constructor constructor, Supplier defaultSupplier) { + private static List extractTypes(Constructor constructor, Supplier> defaultSupplier) { if (isGenericInformationLost(constructor)) { - return () -> getFields(constructor) + return getFields(constructor) .map(Field::getGenericType) - .toArray(Type[]::new); + .collect(toUnmodifiableList()); + } + return defaultSupplier.get(); + } + + private static MethodHandle getConstructorMethodHandle(Constructor constructor) { + try { + return LOOKUP.unreflectConstructor(constructor).asFixedArity(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); } - return defaultSupplier; } private static Stream getFields(Constructor constructor) { diff --git a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorMapper.java b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorMapper.java index 07c6b5e708..60d8464e17 100644 --- a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorMapper.java +++ b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/ConstructorMapper.java @@ -207,14 +207,14 @@ private Optional> createSpecializedRowMapper(StatementContext ctx, List unmatchedColumns) { final int count = factory.getParameterCount(); final Parameter[] parameters = factory.getParameters(); - final Type[] types = factory.getTypes(); + final List types = factory.getTypes(); boolean matchedColumns = false; final List unmatchedParameters = new ArrayList<>(); final List paramData = new ArrayList<>(); for (int i = 0; i < count; i++) { final Parameter parameter = parameters[i]; - final Type parameterType = types[i]; + final Type parameterType = types.get(i); boolean nullable = isNullable(parameter); Nested nested = parameter.getAnnotation(Nested.class); if (nested == null) { diff --git a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/InstanceFactory.java b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/InstanceFactory.java index 2aee37ae77..bac11a4793 100644 --- a/core/src/main/java/org/jdbi/v3/core/mapper/reflect/InstanceFactory.java +++ b/core/src/main/java/org/jdbi/v3/core/mapper/reflect/InstanceFactory.java @@ -18,12 +18,14 @@ import java.lang.reflect.Parameter; import java.lang.reflect.Type; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.stream.Stream; import jakarta.annotation.Nullable; import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toUnmodifiableList; abstract class InstanceFactory { private final Executable executable; @@ -44,10 +46,10 @@ Parameter[] getParameters() { return executable.getParameters(); } - Type[] getTypes() { + List getTypes() { return Arrays.stream(getParameters()) .map(Parameter::getParameterizedType) - .toArray(Type[]::new); + .collect(toUnmodifiableList()); } @Nullable