Implement micro performance optimizations

- ClassUtils.isAssignable(): Avoid Map lookup when the type is not a
  primitive.

- AnnotationsScanner: Perform low cost array length check before String
  comparisons.

- BeanFactoryUtils: Use char comparison instead of String comparison.
  The bean factory prefix is '&', so we can use a char comparison
  instead of more heavyweight String.startsWith("&").

- AbstractBeanFactory.getMergedBeanDefinition(): Perform the low cost
  check first. Map lookup, while cheap, is still more expensive than
  instanceof.

Closes gh-34717

Signed-off-by: Olivier Bourgain <olivierbourgain02@gmail.com>
This commit is contained in:
Olivier Bourgain 2025-04-03 15:13:36 +02:00 committed by Sam Brannen
parent ee804ee8fb
commit 0f2308e85f
4 changed files with 14 additions and 5 deletions

View File

@ -63,6 +63,13 @@ public abstract class BeanFactoryUtils {
*/
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
private static final char FACTORY_BEAN_PREFIX = BeanFactory.FACTORY_BEAN_PREFIX.charAt(0);
/**
* Return whether the given name is a factory dereference
@ -72,7 +79,7 @@ public abstract class BeanFactoryUtils {
* @see BeanFactory#FACTORY_BEAN_PREFIX
*/
public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return (name != null && !name.isEmpty() && name.charAt(0) == FACTORY_BEAN_PREFIX);
}
/**
@ -84,14 +91,14 @@ public abstract class BeanFactoryUtils {
*/
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
if (!isFactoryDereference(name)) {
return name;
}
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
while (isFactoryDereference(beanName));
return beanName;
});
}

View File

@ -1153,7 +1153,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
String beanName = transformedBeanName(name);
// Efficiently check whether bean definition exists in this factory.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory parent) {
if (getParentBeanFactory() instanceof ConfigurableBeanFactory parent && !containsBeanDefinition(beanName)) {
return parent.getMergedBeanDefinition(beanName);
}
// Resolve merged bean definition locally.

View File

@ -355,6 +355,7 @@ abstract class AnnotationsScanner {
private static boolean isOverride(Method rootMethod, Method candidateMethod) {
return (!Modifier.isPrivate(candidateMethod.getModifiers()) &&
candidateMethod.getParameterCount() == rootMethod.getParameterCount() &&
candidateMethod.getName().equals(rootMethod.getName()) &&
hasSameParameterTypes(rootMethod, candidateMethod));
}

View File

@ -637,10 +637,11 @@ public abstract class ClassUtils {
Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
return (lhsType == resolvedPrimitive);
}
else {
else if (rhsType.isPrimitive()) {
Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
return (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper));
}
return false;
}
/**