From 03832c36a441cd3c31287d323419738e61e2a38c Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 27 Apr 2020 12:50:19 +0200 Subject: [PATCH] DATACMNS-1710 - Avoid potentially expensive constructor parameter creation where possible. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder constructor filtering with the goal to delay/avoid potentially expensive PersistenceConstructor creation with parameter lookup as much as possible. The lookup is (in a default setup) only done once per domain type, but depending on the number of entities to inspect the change can help save some cycles for larger domains. Before: Benchmark Mode Cnt Score Error Units EntityMetadataBenchmark.alwaysNew thrpt 10 224318,163 ± 42542,453 ops/s After: Benchmark Mode Cnt Score Error Units EtityMetadataBenchmark.alwaysNew thrpt 10 420053,505 ± 9288,093 ops/s public class DomainType { private String id; private @Id String theId; private String firstname, lastname; private Integer age; public DomainType(String id, String theId, String firstname, String lastname, Integer age) { this.id = id; this.theId = theId; this.firstname = firstname; this.lastname = lastname; this.age = age; } @PersistenceConstructor public DomainType(String firstname, String lastname) { this.firstname = firstname; this.lastname = lastname; } } Original pull request: #440. --- .../model/PreferredConstructorDiscoverer.java | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java b/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java index a70effd80..57169dc2d 100644 --- a/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java +++ b/src/main/java/org/springframework/data/mapping/model/PreferredConstructorDiscoverer.java @@ -22,11 +22,13 @@ import kotlin.reflect.jvm.ReflectJvmMapping; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PreferredConstructor; @@ -102,42 +104,33 @@ public interface PreferredConstructorDiscoverer> PreferredConstructor discover(TypeInformation type, @Nullable PersistentEntity entity) { - boolean noArgConstructorFound = false; - int numberOfArgConstructors = 0; Class rawOwningType = type.getType(); - PreferredConstructor constructor = null; + List> candidates = new ArrayList<>(); + Constructor noArg = null; for (Constructor candidate : rawOwningType.getDeclaredConstructors()) { - PreferredConstructor preferredConstructor = buildPreferredConstructor(candidate, type, entity); - // Synthetic constructors should not be considered - if (preferredConstructor.getConstructor().isSynthetic()) { + if (candidate.isSynthetic()) { continue; } - // Explicitly defined constructor trumps all - if (preferredConstructor.isExplicitlyAnnotated()) { - return preferredConstructor; + if(candidate.isAnnotationPresent(PersistenceConstructor.class)) { + return buildPreferredConstructor(candidate, type, entity); } - // No-arg constructor trumps custom ones - if (constructor == null || preferredConstructor.isNoArgConstructor()) { - constructor = preferredConstructor; - } - - if (preferredConstructor.isNoArgConstructor()) { - noArgConstructorFound = true; + if(candidate.getParameterCount() == 0) { + noArg = candidate; } else { - numberOfArgConstructors++; + candidates.add(candidate); } } - if (!noArgConstructorFound && numberOfArgConstructors > 1) { - constructor = null; + if(noArg != null) { + return buildPreferredConstructor(noArg, type, entity); } - return constructor; + return candidates.size() > 1 || candidates.isEmpty() ? null : buildPreferredConstructor(candidates.iterator().next(), type, entity); } }, @@ -158,9 +151,9 @@ public interface PreferredConstructorDiscoverer rawOwningType = type.getType(); return Arrays.stream(rawOwningType.getDeclaredConstructors()) // + .filter(it -> !it.isSynthetic()) // Synthetic constructors should not be considered + .filter(it -> it.isAnnotationPresent(PersistenceConstructor.class)) // Explicitly defined constructor trumps all .map(it -> buildPreferredConstructor(it, type, entity)) // - .filter(it -> !it.getConstructor().isSynthetic()) // Synthetic constructors should not be considered - .filter(PreferredConstructor::isExplicitlyAnnotated) // Explicitly defined constructor trumps all .findFirst() // .orElseGet(() -> {