revised constructor argument caching for highly concurrent creation scenarios (follow-up to SPR-7423)

This commit is contained in:
Juergen Hoeller 2010-08-18 09:08:55 +00:00
parent 9a088b8128
commit 9857ba077b
5 changed files with 101 additions and 63 deletions

View File

@ -880,8 +880,18 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
} }
// Shortcut when re-creating the same bean... // Shortcut when re-creating the same bean...
if (mbd.resolvedConstructorOrFactoryMethod != null && args == null) { boolean resolved = false;
if (mbd.constructorArgumentsResolved) { boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null); return autowireConstructor(beanName, mbd, null, null);
} }
else { else {

View File

@ -117,14 +117,20 @@ class ConstructorResolver {
argsToUse = explicitArgs; argsToUse = explicitArgs;
} }
else { else {
constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod; Object[] argsToResolve = null;
if (constructorToUse != null) { synchronized (mbd.constructorArgumentLock) {
// Found a cached constructor... constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod;
argsToUse = mbd.resolvedConstructorArguments; if (constructorToUse != null && mbd.constructorArgumentsResolved) {
if (argsToUse == null) { // Found a cached constructor...
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse); argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
} }
} }
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
} }
if (constructorToUse == null) { if (constructorToUse == null) {
@ -254,8 +260,7 @@ class ConstructorResolver {
} }
if (explicitArgs == null) { if (explicitArgs == null) {
mbd.resolvedConstructorOrFactoryMethod = constructorToUse; argsHolderToUse.storeCache(mbd, constructorToUse);
argsHolderToUse.storeCache(mbd);
} }
} }
@ -312,7 +317,9 @@ class ConstructorResolver {
} }
} }
} }
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
}
} }
/** /**
@ -371,14 +378,20 @@ class ConstructorResolver {
argsToUse = explicitArgs; argsToUse = explicitArgs;
} }
else { else {
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod; Object[] argsToResolve = null;
if (factoryMethodToUse != null) { synchronized (mbd.constructorArgumentLock) {
// Found a cached factory method... factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
argsToUse = mbd.resolvedConstructorArguments; if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
if (argsToUse == null && mbd.preparedConstructorArguments != null) { // Found a cached factory method...
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse); argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
} }
} }
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
}
} }
if (factoryMethodToUse == null || argsToUse == null) { if (factoryMethodToUse == null || argsToUse == null) {
@ -536,8 +549,7 @@ class ConstructorResolver {
} }
if (explicitArgs == null) { if (explicitArgs == null) {
mbd.resolvedConstructorOrFactoryMethod = factoryMethodToUse; argsHolderToUse.storeCache(mbd, factoryMethodToUse);
argsHolderToUse.storeCache(mbd);
} }
} }
@ -734,11 +746,10 @@ class ConstructorResolver {
* Resolve the prepared arguments stored in the given bean definition. * Resolve the prepared arguments stored in the given bean definition.
*/ */
private Object[] resolvePreparedArguments( private Object[] resolvePreparedArguments(
String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor) { String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) {
Class[] paramTypes = (methodOrCtor instanceof Method ? Class[] paramTypes = (methodOrCtor instanceof Method ?
((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes());
Object[] argsToResolve = mbd.preparedConstructorArguments;
TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
this.beanFactory.getCustomTypeConverter() : bw); this.beanFactory.getCustomTypeConverter() : bw);
BeanDefinitionValueResolver valueResolver = BeanDefinitionValueResolver valueResolver =
@ -789,11 +800,11 @@ class ConstructorResolver {
*/ */
private static class ArgumentsHolder { private static class ArgumentsHolder {
public Object rawArguments[]; public final Object rawArguments[];
public Object arguments[]; public final Object arguments[];
public Object preparedArguments[]; public final Object preparedArguments[];
public boolean resolveNecessary = false; public boolean resolveNecessary = false;
@ -833,14 +844,17 @@ class ConstructorResolver {
return Integer.MAX_VALUE - 1024; return Integer.MAX_VALUE - 1024;
} }
public void storeCache(RootBeanDefinition mbd) { public void storeCache(RootBeanDefinition mbd, Object constructorOrFactoryMethod) {
if (this.resolveNecessary) { synchronized (mbd.constructorArgumentLock) {
mbd.preparedConstructorArguments = this.preparedArguments; mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;
mbd.constructorArgumentsResolved = true;
if (this.resolveNecessary) {
mbd.preparedConstructorArguments = this.preparedArguments;
}
else {
mbd.resolvedConstructorArguments = this.arguments;
}
} }
else {
mbd.resolvedConstructorArguments = this.arguments;
}
mbd.constructorArgumentsResolved = true;
} }
} }

View File

@ -504,8 +504,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
*/ */
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) { protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
resolveBeanClass(mbd, beanName); resolveBeanClass(mbd, beanName);
if (mbd.isFactoryMethodUnique && mbd.resolvedConstructorOrFactoryMethod == null) { if (mbd.isFactoryMethodUnique) {
new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd); boolean resolve;
synchronized (mbd.constructorArgumentLock) {
resolve = (mbd.resolvedConstructorOrFactoryMethod == null);
}
if (resolve) {
new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
}
} }
return getAutowireCandidateResolver().isAutowireCandidate( return getAutowireCandidateResolver().isAutowireCandidate(
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor); new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2010 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.
@ -59,16 +59,18 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
boolean isFactoryMethodUnique; boolean isFactoryMethodUnique;
/** Package-visible field for caching the resolved constructor or factory method */ /** Package-visible field for caching the resolved constructor or factory method */
volatile Object resolvedConstructorOrFactoryMethod; Object resolvedConstructorOrFactoryMethod;
/** Package-visible field for caching fully resolved constructor arguments */
volatile Object[] resolvedConstructorArguments;
/** Package-visible field for caching partly prepared constructor arguments */
volatile Object[] preparedConstructorArguments;
/** Package-visible field that marks the constructor arguments as resolved */ /** Package-visible field that marks the constructor arguments as resolved */
volatile boolean constructorArgumentsResolved = false; boolean constructorArgumentsResolved = false;
/** Package-visible field for caching fully resolved constructor arguments */
Object[] resolvedConstructorArguments;
/** Package-visible field for caching partly prepared constructor arguments */
Object[] preparedConstructorArguments;
final Object constructorArgumentLock = new Object();
/** Package-visible field that indicates a before-instantiation post-processor having kicked in */ /** Package-visible field that indicates a before-instantiation post-processor having kicked in */
volatile Boolean beforeInstantiationResolved; volatile Boolean beforeInstantiationResolved;
@ -78,6 +80,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
final Object postProcessingLock = new Object(); final Object postProcessingLock = new Object();
/** /**
* Create a new RootBeanDefinition, to be configured through its bean * Create a new RootBeanDefinition, to be configured through its bean
* properties and configuration methods. * properties and configuration methods.
@ -264,8 +267,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
* @return the factory method, or <code>null</code> if not found or not resolved yet * @return the factory method, or <code>null</code> if not found or not resolved yet
*/ */
public Method getResolvedFactoryMethod() { public Method getResolvedFactoryMethod() {
Object candidate = this.resolvedConstructorOrFactoryMethod; synchronized (this.constructorArgumentLock) {
return (candidate instanceof Method ? (Method) candidate : null); Object candidate = this.resolvedConstructorOrFactoryMethod;
return (candidate instanceof Method ? (Method) candidate : null);
}
} }

View File

@ -45,27 +45,30 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides. // Don't override the class with CGLIB if no overrides.
if (beanDefinition.getMethodOverrides().isEmpty()) { if (beanDefinition.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod; Constructor<?> constructorToUse;
if (constructorToUse == null) { synchronized (beanDefinition.constructorArgumentLock) {
final Class clazz = beanDefinition.getBeanClass(); constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
if (clazz.isInterface()) { if (constructorToUse == null) {
throw new BeanInstantiationException(clazz, "Specified class is an interface"); final Class clazz = beanDefinition.getBeanClass();
} if (clazz.isInterface()) {
try { throw new BeanInstantiationException(clazz, "Specified class is an interface");
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
} }
else { try {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null); if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
public Constructor run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
} }
beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
} }
} }
return BeanUtils.instantiateClass(constructorToUse); return BeanUtils.instantiateClass(constructorToUse);