HHH-17987 Improve allocation rate of ReflectHelper#setterMethodOrNull

This commit is contained in:
Sanne Grinovero 2024-04-20 22:04:07 +01:00 committed by Sanne Grinovero
parent cf5d09cc76
commit 6f8545f1a5
1 changed files with 63 additions and 7 deletions

View File

@ -544,6 +544,9 @@ public final class ReflectHelper {
Method getter = null;
for ( int i = 0; getter == null && i < interfaces.length; ++i ) {
final Class<?> anInterface = interfaces[i];
if ( shouldSkipInterfaceCheck( anInterface ) ) {
continue;
}
getter = getGetterOrNull( anInterface, propertyName );
if ( getter == null ) {
// if no getter found yet, check all implemented interfaces of interface
@ -689,6 +692,23 @@ public final class ReflectHelper {
}
public static Method setterMethodOrNull(final Class<?> containerClass, final String propertyName, final Class<?> propertyType) {
//Computes the most likely setter name - there might be fallback choices to try, but we try this one first
//to try not overwhelming the system with swallowed exceptions.
final String likelyMethodName = likelySetterMethodNameForProperty( propertyName );
//First let's test the most obvious solution: a public method having exactly the expected name and type;
//this has the benefit of including parent types and interfaces w/o extensively bothering the reflection api
//which is very allocation intensive - this is noticeable on bootstrap costs on large models.
try {
final Method setter = containerClass.getMethod( likelyMethodName, propertyType );
ensureAccessibility( setter );
return setter;
}
catch ( NoSuchMethodException e ) {
//No luck: we'll need to run the more expensive but thorough process
}
Class<?> checkClass = containerClass;
Method setter = null;
@ -698,11 +718,11 @@ public final class ReflectHelper {
break;
}
setter = setterOrNull( checkClass, propertyName, propertyType );
setter = setterOrNull( checkClass, propertyName, propertyType, likelyMethodName );
// if no setter found yet, check all implemented interfaces
if ( setter == null ) {
setter = setterOrNull( checkClass.getInterfaces(), propertyName, propertyType );
setter = setterOrNull( checkClass.getInterfaces(), propertyName, propertyType, likelyMethodName );
}
else {
ensureAccessibility( setter );
@ -742,6 +762,9 @@ public final class ReflectHelper {
Method setter = null;
for ( int i = 0; setter == null && i < interfaces.length; ++i ) {
final Class<?> anInterface = interfaces[i];
if ( shouldSkipInterfaceCheck( anInterface ) ) {
continue;
}
setter = setterOrNullBySetterName( anInterface, setterName, propertyType );
if ( setter == null ) {
// if no setter found yet, check all implemented interfaces of interface
@ -751,6 +774,21 @@ public final class ReflectHelper {
return setter;
}
private static boolean shouldSkipInterfaceCheck(final Class anInterface) {
final String interfaceName = anInterface.getName();
//Skip checking any interface that we've added ourself via bytecode enhancement:
//there's many of those and it's pointless to look there.
if ( interfaceName.startsWith( "org.hibernate.engine." ) ) {
return true;
}
//Also skip jakarta.persistence prefixed interfaces, as otherwise we'll be scanning
//among mapping annotations as well:
if ( interfaceName.startsWith( "jakarta.persistence." ) ) {
return true;
}
return false;
}
private static Method setterOrNullBySetterName(Class<?> theClass, String setterName, Class<?> propertyType) {
Method potentialSetter = null;
@ -782,22 +820,30 @@ public final class ReflectHelper {
return setter;
}
private static Method setterOrNull(Class<?>[] interfaces, String propertyName, Class<?> propertyType) {
private static Method setterOrNull(Class<?>[] interfaces, String propertyName, Class<?> propertyType, String likelyMethodName) {
Method setter = null;
for ( int i = 0; setter == null && i < interfaces.length; ++i ) {
final Class<?> anInterface = interfaces[i];
setter = setterOrNull( anInterface, propertyName, propertyType );
if ( shouldSkipInterfaceCheck( anInterface ) ) {
continue;
}
setter = setterOrNull( anInterface, propertyName, propertyType, likelyMethodName );
if ( setter == null ) {
// if no setter found yet, check all implemented interfaces of interface
setter = setterOrNull( anInterface.getInterfaces(), propertyName, propertyType );
setter = setterOrNull( anInterface.getInterfaces(), propertyName, propertyType, likelyMethodName );
}
}
return setter;
}
private static Method setterOrNull(Class<?> theClass, String propertyName, Class<?> propertyType) {
private static Method setterOrNull(Class<?> theClass, String propertyName, Class propertyType, String likelyMethodName) {
try {
return theClass.getDeclaredMethod( likelyMethodName, propertyType );
}
catch ( NoSuchMethodException e ) {
//Ignore, so we try the old method for best compatibility (even though it's less efficient) next:
}
Method potentialSetter = null;
for ( Method method : theClass.getDeclaredMethods() ) {
final String methodName = method.getName();
if ( method.getParameterCount() == 1 && methodName.startsWith( "set" ) ) {
@ -815,6 +861,16 @@ public final class ReflectHelper {
return potentialSetter;
}
private static String likelySetterMethodNameForProperty(final String propertyName) {
final char firstCharacter = propertyName.charAt( 0 );
if ( Character.isLowerCase( firstCharacter ) ) {
return "set" + Character.toUpperCase( firstCharacter ) + propertyName.substring( 1 );
}
else {
return "set" + propertyName;
}
}
/**
* Similar to {@link #getterMethodOrNull}, except that here we are just looking for the
* corresponding getter for a field (defined as field access) if one exists.