Skip to content

Commit

Permalink
HHH-17987 Improve allocation rate of ReflectHelper#setterMethodOrNull
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed Apr 23, 2024
1 parent cf5d09c commit 6f8545f
Showing 1 changed file with 63 additions and 7 deletions.
Expand Up @@ -544,6 +544,9 @@ private static Method getGetterOrNull(Class<?>[] interfaces, String propertyName
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
Expand Down Expand Up @@ -689,6 +692,23 @@ public static Method getterMethodOrNull(Class<?> containerJavaType, String prope
}

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;

Expand All @@ -698,11 +718,11 @@ public static Method setterMethodOrNull(final Class<?> containerClass, final St
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 );
Expand Down Expand Up @@ -742,6 +762,9 @@ private static Method setterOrNullBySetterName(Class<?>[] interfaces, String set
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
Expand All @@ -751,6 +774,21 @@ private static Method setterOrNullBySetterName(Class<?>[] interfaces, String set
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;

Expand Down Expand Up @@ -782,22 +820,30 @@ public static Method findSetterMethod(final Class<?> containerClass, final Strin
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" ) ) {
Expand All @@ -815,6 +861,16 @@ private static Method setterOrNull(Class<?> theClass, String propertyName, Class
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.
Expand Down

0 comments on commit 6f8545f

Please sign in to comment.