Browse Source

Avoid argument resolution overhead for no-arg factory methods

Includes revised InstantiationStrategy nullability for args array.

Issue: SPR-17171
pull/1919/merge
Juergen Hoeller 7 years ago
parent
commit
347852e86a
  1. 6
      spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
  2. 124
      spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
  3. 18
      spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java
  4. 10
      spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java

6
spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -79,7 +79,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
@Override @Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Constructor<?> ctor, @Nullable Object... args) { @Nullable Constructor<?> ctor, Object... args) {
// Must generate CGLIB subclass... // Must generate CGLIB subclass...
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args); return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
@ -113,7 +113,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
* Ignored if the {@code ctor} parameter is {@code null}. * Ignored if the {@code ctor} parameter is {@code null}.
* @return new instance of the dynamically generated subclass * @return new instance of the dynamically generated subclass
*/ */
public Object instantiate(@Nullable Constructor<?> ctor, @Nullable Object... args) { public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
Class<?> subclass = createEnhancedSubclass(this.beanDefinition); Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance; Object instance;
if (ctor == null) { if (ctor == null) {

124
spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java

@ -79,6 +79,8 @@ import org.springframework.util.StringUtils;
*/ */
class ConstructorResolver { class ConstructorResolver {
private static final Object[] EMPTY_ARGS = new Object[0];
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint = private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
new NamedThreadLocal<>("Current injection point"); new NamedThreadLocal<>("Current injection point");
@ -111,8 +113,8 @@ class ConstructorResolver {
* or {@code null} if none (-> use constructor argument values from bean definition) * or {@code null} if none (-> use constructor argument values from bean definition)
* @return a BeanWrapper for the new instance * @return a BeanWrapper for the new instance
*/ */
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) { @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl(); BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw); this.beanFactory.initBeanWrapper(bw);
@ -141,7 +143,35 @@ class ConstructorResolver {
} }
} }
if (constructorToUse == null) { if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
// Need to resolve the constructor. // Need to resolve the constructor.
boolean autowiring = (chosenCtors != null || boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
@ -157,20 +187,6 @@ class ConstructorResolver {
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
} }
// Take specified constructors, if any.
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
AutowireUtils.sortConstructors(candidates); AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE; int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null; Set<Constructor<?>> ambiguousConstructors = null;
@ -264,23 +280,23 @@ class ConstructorResolver {
} }
} }
try { bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
final InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy(); return bw;
Object beanInstance; }
private Object instantiate(
String beanName, RootBeanDefinition mbd, Constructor constructorToUse, Object[] argsToUse) {
try {
InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
final Constructor<?> ctorToUse = constructorToUse; return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
final Object[] argumentsToUse = argsToUse; strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
strategy.instantiate(mbd, beanName, this.beanFactory, ctorToUse, argumentsToUse),
this.beanFactory.getAccessControlContext()); this.beanFactory.getAccessControlContext());
} }
else { else {
beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse); return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
} }
bw.setBeanInstance(beanInstance);
return bw;
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, throw new BeanCreationException(mbd.getResourceDescription(), beanName,
@ -330,7 +346,7 @@ class ConstructorResolver {
* the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag. * the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag.
* Called as the starting point for factory method determination. * Called as the starting point for factory method determination.
*/ */
private Method[] getCandidateMethods(final Class<?> factoryClass, final RootBeanDefinition mbd) { private Method[] getCandidateMethods(Class<?> factoryClass, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedAction<Method[]>) () -> return AccessController.doPrivileged((PrivilegedAction<Method[]>) () ->
(mbd.isNonPublicAccessAllowed() ? (mbd.isNonPublicAccessAllowed() ?
@ -358,7 +374,7 @@ class ConstructorResolver {
* @return a BeanWrapper for the new instance * @return a BeanWrapper for the new instance
*/ */
public BeanWrapper instantiateUsingFactoryMethod( public BeanWrapper instantiateUsingFactoryMethod(
final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) { String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl(); BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw); this.beanFactory.initBeanWrapper(bw);
@ -421,13 +437,27 @@ class ConstructorResolver {
factoryClass = ClassUtils.getUserClass(factoryClass); factoryClass = ClassUtils.getUserClass(factoryClass);
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd); Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
List<Method> candidateSet = new ArrayList<>(); List<Method> candidateList = new ArrayList<>();
for (Method candidate : rawCandidates) { for (Method candidate : rawCandidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
candidateSet.add(candidate); candidateList.add(candidate);
}
}
if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Method uniqueCandidate = candidateList.get(0);
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
return bw;
} }
} }
Method[] candidates = candidateSet.toArray(new Method[0]);
Method[] candidates = candidateList.toArray(new Method[0]);
AutowireUtils.sortFactoryMethods(candidates); AutowireUtils.sortFactoryMethods(candidates);
ConstructorArgumentValues resolvedValues = null; ConstructorArgumentValues resolvedValues = null;
@ -460,7 +490,7 @@ class ConstructorResolver {
if (paramTypes.length >= minNrOfArgs) { if (paramTypes.length >= minNrOfArgs) {
ArgumentsHolder argsHolder; ArgumentsHolder argsHolder;
if (explicitArgs != null){ if (explicitArgs != null) {
// Explicit arguments given -> arguments length must match exactly. // Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) { if (paramTypes.length != explicitArgs.length) {
continue; continue;
@ -533,7 +563,7 @@ class ConstructorResolver {
argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null"); argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
} }
} }
else if (resolvedValues != null){ else if (resolvedValues != null) {
Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount()); Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values()); valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
valueHolders.addAll(resolvedValues.getGenericArgumentValues()); valueHolders.addAll(resolvedValues.getGenericArgumentValues());
@ -571,24 +601,24 @@ class ConstructorResolver {
} }
} }
try { bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
Object beanInstance; return bw;
}
private Object instantiate(
String beanName, RootBeanDefinition mbd, Object factoryBean, Method factoryMethod, Object[] args) {
try {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
final Object fb = factoryBean; return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
final Method factoryMethod = factoryMethodToUse; this.beanFactory.getInstantiationStrategy().instantiate(
final Object[] args = argsToUse; mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, fb, factoryMethod, args),
this.beanFactory.getAccessControlContext()); this.beanFactory.getAccessControlContext());
} }
else { else {
beanInstance = this.beanFactory.getInstantiationStrategy().instantiate( return this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse); mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
} }
bw.setBeanInstance(beanInstance);
return bw;
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, throw new BeanCreationException(mbd.getResourceDescription(), beanName,

18
spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,8 +38,8 @@ public interface InstantiationStrategy {
/** /**
* Return an instance of the bean with the given name in this factory. * Return an instance of the bean with the given name in this factory.
* @param bd the bean definition * @param bd the bean definition
* @param beanName the name of the bean when it's created in this context. * @param beanName the name of the bean when it is created in this context.
* The name can be {@code null} if we're autowiring a bean which doesn't * The name can be {@code null} if we are autowiring a bean which doesn't
* belong to the factory. * belong to the factory.
* @param owner the owning BeanFactory * @param owner the owning BeanFactory
* @return a bean instance for this bean definition * @return a bean instance for this bean definition
@ -52,8 +52,8 @@ public interface InstantiationStrategy {
* Return an instance of the bean with the given name in this factory, * Return an instance of the bean with the given name in this factory,
* creating it via the given constructor. * creating it via the given constructor.
* @param bd the bean definition * @param bd the bean definition
* @param beanName the name of the bean when it's created in this context. * @param beanName the name of the bean when it is created in this context.
* The name can be {@code null} if we're autowiring a bean which doesn't * The name can be {@code null} if we are autowiring a bean which doesn't
* belong to the factory. * belong to the factory.
* @param owner the owning BeanFactory * @param owner the owning BeanFactory
* @param ctor the constructor to use * @param ctor the constructor to use
@ -62,14 +62,14 @@ public interface InstantiationStrategy {
* @throws BeansException if the instantiation attempt failed * @throws BeansException if the instantiation attempt failed
*/ */
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, @Nullable Object... args) throws BeansException; Constructor<?> ctor, Object... args) throws BeansException;
/** /**
* Return an instance of the bean with the given name in this factory, * Return an instance of the bean with the given name in this factory,
* creating it via the given factory method. * creating it via the given factory method.
* @param bd the bean definition * @param bd the bean definition
* @param beanName the name of the bean when it's created in this context. * @param beanName the name of the bean when it is created in this context.
* The name can be {@code null} if we're autowiring a bean which doesn't * The name can be {@code null} if we are autowiring a bean which doesn't
* belong to the factory. * belong to the factory.
* @param owner the owning BeanFactory * @param owner the owning BeanFactory
* @param factoryBean the factory bean instance to call the factory method on, * @param factoryBean the factory bean instance to call the factory method on,
@ -80,7 +80,7 @@ public interface InstantiationStrategy {
* @throws BeansException if the instantiation attempt failed * @throws BeansException if the instantiation attempt failed
*/ */
Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args) @Nullable Object factoryBean, Method factoryMethod, Object... args)
throws BeansException; throws BeansException;
} }

10
spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -104,7 +104,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
@Override @Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
final Constructor<?> ctor, @Nullable Object... args) { final Constructor<?> ctor, Object... args) {
if (!bd.hasMethodOverrides()) { if (!bd.hasMethodOverrides()) {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
@ -114,7 +114,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
return null; return null;
}); });
} }
return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor)); return BeanUtils.instantiateClass(ctor, args);
} }
else { else {
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args); return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
@ -128,14 +128,14 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
* Instantiation should use the given constructor and parameters. * Instantiation should use the given constructor and parameters.
*/ */
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName,
BeanFactory owner, @Nullable Constructor<?> ctor, @Nullable Object... args) { BeanFactory owner, @Nullable Constructor<?> ctor, Object... args) {
throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy"); throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
} }
@Override @Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, @Nullable Object... args) { @Nullable Object factoryBean, final Method factoryMethod, Object... args) {
try { try {
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {

Loading…
Cancel
Save