DATACMNS-1710 - Avoid potentially expensive constructor parameter creation where possible.

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.
This commit is contained in:
Christoph Strobl
2020-04-27 12:50:19 +02:00
committed by Mark Paluch
parent 03d5498c22
commit 03832c36a4

View File

@@ -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<T, P extends PersistentProperty<
<T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(TypeInformation<T> type,
@Nullable PersistentEntity<T, P> entity) {
boolean noArgConstructorFound = false;
int numberOfArgConstructors = 0;
Class<?> rawOwningType = type.getType();
PreferredConstructor<T, P> constructor = null;
List<Constructor<?>> candidates = new ArrayList<>();
Constructor<?> noArg = null;
for (Constructor<?> candidate : rawOwningType.getDeclaredConstructors()) {
PreferredConstructor<T, P> 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<T, P extends PersistentProperty<
Class<?> 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(() -> {