revised constructor argument caching for highly concurrent creation scenarios (follow-up to SPR-7423)
This commit is contained in:
parent
9a088b8128
commit
9857ba077b
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue