Avoid argument resolution overhead for no-arg factory methods
Includes revised InstantiationStrategy nullability for args array. Issue: SPR-17171
This commit is contained in:
parent
777bd0d022
commit
347852e86a
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -79,7 +79,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
|
||||
@Override
|
||||
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...
|
||||
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}.
|
||||
* @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);
|
||||
Object instance;
|
||||
if (ctor == null) {
|
||||
|
|
|
@ -79,6 +79,8 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
class ConstructorResolver {
|
||||
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
|
||||
new NamedThreadLocal<>("Current injection point");
|
||||
|
||||
|
@ -111,8 +113,8 @@ class ConstructorResolver {
|
|||
* or {@code null} if none (-> use constructor argument values from bean definition)
|
||||
* @return a BeanWrapper for the new instance
|
||||
*/
|
||||
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,
|
||||
@Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) {
|
||||
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
|
||||
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
|
||||
|
||||
BeanWrapperImpl bw = new BeanWrapperImpl();
|
||||
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.
|
||||
boolean autowiring = (chosenCtors != null ||
|
||||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
|
@ -157,20 +187,6 @@ class ConstructorResolver {
|
|||
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);
|
||||
int minTypeDiffWeight = Integer.MAX_VALUE;
|
||||
Set<Constructor<?>> ambiguousConstructors = null;
|
||||
|
@ -264,23 +280,23 @@ class ConstructorResolver {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
|
||||
Object beanInstance;
|
||||
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
|
||||
return bw;
|
||||
}
|
||||
|
||||
private Object instantiate(
|
||||
String beanName, RootBeanDefinition mbd, Constructor constructorToUse, Object[] argsToUse) {
|
||||
|
||||
try {
|
||||
InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
|
||||
if (System.getSecurityManager() != null) {
|
||||
final Constructor<?> ctorToUse = constructorToUse;
|
||||
final Object[] argumentsToUse = argsToUse;
|
||||
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
|
||||
strategy.instantiate(mbd, beanName, this.beanFactory, ctorToUse, argumentsToUse),
|
||||
return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
|
||||
strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
|
||||
this.beanFactory.getAccessControlContext());
|
||||
}
|
||||
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) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
|
@ -330,7 +346,7 @@ class ConstructorResolver {
|
|||
* the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag.
|
||||
* 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) {
|
||||
return AccessController.doPrivileged((PrivilegedAction<Method[]>) () ->
|
||||
(mbd.isNonPublicAccessAllowed() ?
|
||||
|
@ -358,7 +374,7 @@ class ConstructorResolver {
|
|||
* @return a BeanWrapper for the new instance
|
||||
*/
|
||||
public BeanWrapper instantiateUsingFactoryMethod(
|
||||
final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {
|
||||
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
|
||||
|
||||
BeanWrapperImpl bw = new BeanWrapperImpl();
|
||||
this.beanFactory.initBeanWrapper(bw);
|
||||
|
@ -421,13 +437,27 @@ class ConstructorResolver {
|
|||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||
|
||||
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
|
||||
List<Method> candidateSet = new ArrayList<>();
|
||||
List<Method> candidateList = new ArrayList<>();
|
||||
for (Method candidate : rawCandidates) {
|
||||
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
|
||||
candidateSet.add(candidate);
|
||||
candidateList.add(candidate);
|
||||
}
|
||||
}
|
||||
Method[] candidates = candidateSet.toArray(new Method[0]);
|
||||
|
||||
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 = candidateList.toArray(new Method[0]);
|
||||
AutowireUtils.sortFactoryMethods(candidates);
|
||||
|
||||
ConstructorArgumentValues resolvedValues = null;
|
||||
|
@ -460,7 +490,7 @@ class ConstructorResolver {
|
|||
if (paramTypes.length >= minNrOfArgs) {
|
||||
ArgumentsHolder argsHolder;
|
||||
|
||||
if (explicitArgs != null){
|
||||
if (explicitArgs != null) {
|
||||
// Explicit arguments given -> arguments length must match exactly.
|
||||
if (paramTypes.length != explicitArgs.length) {
|
||||
continue;
|
||||
|
@ -533,7 +563,7 @@ class ConstructorResolver {
|
|||
argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
|
||||
}
|
||||
}
|
||||
else if (resolvedValues != null){
|
||||
else if (resolvedValues != null) {
|
||||
Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
|
||||
valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
|
||||
valueHolders.addAll(resolvedValues.getGenericArgumentValues());
|
||||
|
@ -571,24 +601,24 @@ class ConstructorResolver {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object beanInstance;
|
||||
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
|
||||
return bw;
|
||||
}
|
||||
|
||||
private Object instantiate(
|
||||
String beanName, RootBeanDefinition mbd, Object factoryBean, Method factoryMethod, Object[] args) {
|
||||
|
||||
try {
|
||||
if (System.getSecurityManager() != null) {
|
||||
final Object fb = factoryBean;
|
||||
final Method factoryMethod = factoryMethodToUse;
|
||||
final Object[] args = argsToUse;
|
||||
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
|
||||
this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, fb, factoryMethod, args),
|
||||
return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
|
||||
this.beanFactory.getInstantiationStrategy().instantiate(
|
||||
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
|
||||
this.beanFactory.getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
|
||||
mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
|
||||
return this.beanFactory.getInstantiationStrategy().instantiate(
|
||||
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
|
||||
}
|
||||
|
||||
bw.setBeanInstance(beanInstance);
|
||||
return bw;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
|
|
|
@ -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");
|
||||
* 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.
|
||||
* @param bd the bean definition
|
||||
* @param beanName the name of the bean when it's created in this context.
|
||||
* The name can be {@code null} if we're autowiring a bean which doesn't
|
||||
* @param beanName the name of the bean when it is created in this context.
|
||||
* The name can be {@code null} if we are autowiring a bean which doesn't
|
||||
* belong to the factory.
|
||||
* @param owner the owning BeanFactory
|
||||
* @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,
|
||||
* creating it via the given constructor.
|
||||
* @param bd the bean definition
|
||||
* @param beanName the name of the bean when it's created in this context.
|
||||
* The name can be {@code null} if we're autowiring a bean which doesn't
|
||||
* @param beanName the name of the bean when it is created in this context.
|
||||
* The name can be {@code null} if we are autowiring a bean which doesn't
|
||||
* belong to the factory.
|
||||
* @param owner the owning BeanFactory
|
||||
* @param ctor the constructor to use
|
||||
|
@ -62,14 +62,14 @@ public interface InstantiationStrategy {
|
|||
* @throws BeansException if the instantiation attempt failed
|
||||
*/
|
||||
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,
|
||||
* creating it via the given factory method.
|
||||
* @param bd the bean definition
|
||||
* @param beanName the name of the bean when it's created in this context.
|
||||
* The name can be {@code null} if we're autowiring a bean which doesn't
|
||||
* @param beanName the name of the bean when it is created in this context.
|
||||
* The name can be {@code null} if we are autowiring a bean which doesn't
|
||||
* belong to the factory.
|
||||
* @param owner the owning BeanFactory
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -104,7 +104,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
|
||||
@Override
|
||||
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 (System.getSecurityManager() != null) {
|
||||
|
@ -114,7 +114,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
return null;
|
||||
});
|
||||
}
|
||||
return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
}
|
||||
else {
|
||||
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
|
||||
|
@ -128,14 +128,14 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
* Instantiation should use the given constructor and parameters.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
@Override
|
||||
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 {
|
||||
if (System.getSecurityManager() != null) {
|
||||
|
|
Loading…
Reference in New Issue