📄 abstractautowirecapablebeanfactory.java
字号:
}
}
}
return bean;
}
/**
* Instantiate the bean using a named factory method. The method may be static, if the
* mergedBeanDefinition parameter specifies a class, rather than a factoryBean, or
* an instance variable on a factory object itself configured using Dependency Injection.
* <p>Implementation requires iterating over the static or instance methods with the
* name specified in the RootBeanDefinition (the method may be overloaded) and trying
* to match with the parameters. We don't have the types attached to constructor args,
* so trial and error is the only way to go here. The args array may contain argument
* values passed in programmatically via the overloaded getBean() method.
*/
protected BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) throws BeansException {
ConstructorArgumentValues cargs = mergedBeanDefinition.getConstructorArgumentValues();
ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues();
int expectedArgCount = 0;
// We don't have arguments passed in programmatically, so we need to resolve the
// arguments specified in the constructor arguments held in the bean definition.
if (args == null) {
expectedArgCount = cargs.getArgumentCount();
resolveConstructorArguments(beanName, mergedBeanDefinition, cargs, resolvedValues);
}
else {
// If we have constructor args, don't need to resolve them.
expectedArgCount = args.length;
}
BeanWrapperImpl bw = new BeanWrapperImpl();
initBeanWrapper(bw);
boolean isStatic = true;
Class factoryClass = null;
if (mergedBeanDefinition.getFactoryBeanName() != null) {
// It's an instance method on the factory bean's class.
factoryClass = getBean(mergedBeanDefinition.getFactoryBeanName()).getClass();
isStatic = false;
}
else {
// It's a static factory method on the bean class.
factoryClass = mergedBeanDefinition.getBeanClass();
}
// try all methods with this name to see if they match constructor arguments
for (int i = 0; i < factoryClass.getMethods().length; i++) {
Method factoryMethod = factoryClass.getMethods()[i];
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
factoryMethod.getName().equals(mergedBeanDefinition.getFactoryMethodName()) &&
factoryMethod.getParameterTypes().length == expectedArgCount) {
Class[] argTypes = factoryMethod.getParameterTypes();
try {
// try to create the required arguments
if (args == null) {
args = createArgumentArray(beanName, mergedBeanDefinition, resolvedValues, bw, argTypes);
}
}
catch (Exception ex) {
// If we failed to match this method, swallow the exception and keep trying new overloaded
// factory methods...
continue;
}
// If we get here, we found a factory method
Object beanInstance =
this.instantiationStrategy.instantiate(mergedBeanDefinition, beanName, this, factoryMethod, args);
// TODO: If we got to here, we could cache the resolved Method in the RootBeanDefinition for
// efficiency on future creation, but that would need to be synchronized.
bw.setWrappedInstance(beanInstance);
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' instantiated via factory method [" + factoryMethod + "]");
}
return bw;
}
} // for each method
// If we get here, we didn't match any method.
throw new BeanDefinitionStoreException(
"Cannot find matching factory method '" + mergedBeanDefinition.getFactoryMethodName() +
"' on class " + factoryClass);
}
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
* <p>This corresponds to constructor injection: In this mode, a Spring
* bean factory is able to host components that expect constructor-based
* dependency resolution.
* @param beanName name of the bean to autowire by type
* @param mergedBeanDefinition bean definition to update through autowiring
* @return BeanWrapper for the new instance
*/
protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mergedBeanDefinition)
throws BeansException {
ConstructorArgumentValues cargs = mergedBeanDefinition.getConstructorArgumentValues();
ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues();
BeanWrapperImpl bw = new BeanWrapperImpl();
initBeanWrapper(bw);
int minNrOfArgs = 0;
if (cargs != null) {
minNrOfArgs = resolveConstructorArguments(beanName, mergedBeanDefinition, cargs, resolvedValues);
}
Constructor[] constructors = mergedBeanDefinition.getBeanClass().getDeclaredConstructors();
Arrays.sort(constructors, new Comparator() {
public int compare(Object o1, Object o2) {
Constructor c1 = (Constructor) o1;
Constructor c2 = (Constructor) o2;
boolean p1 = Modifier.isPublic(c1.getModifiers());
boolean p2 = Modifier.isPublic(c2.getModifiers());
if (p1 != p2) {
return (p1 ? -1 : 1);
}
int c1pl = c1.getParameterTypes().length;
int c2pl = c2.getParameterTypes().length;
return (new Integer(c1pl)).compareTo(new Integer(c2pl)) * -1;
}
});
Constructor constructorToUse = null;
Object[] argsToUse = null;
int minTypeDiffWeight = Integer.MAX_VALUE;
for (int i = 0; i < constructors.length; i++) {
try {
Constructor constructor = constructors[i];
if (constructor.getParameterTypes().length < minNrOfArgs) {
throw new BeanCreationException(mergedBeanDefinition.getResourceDescription(), beanName,
minNrOfArgs + " constructor arguments specified but no matching constructor found in bean '" +
beanName + "' (hint: specify index arguments for simple parameters to avoid type ambiguities)");
}
Class[] argTypes = constructor.getParameterTypes();
Object[] args = createArgumentArray(beanName, mergedBeanDefinition, resolvedValues, bw, argTypes);
int typeDiffWeight = getTypeDifferenceWeight(argTypes, args);
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = constructor;
argsToUse = args;
minTypeDiffWeight = typeDiffWeight;
}
}
catch (BeansException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring constructor [" + constructors[i] + "] of bean '" + beanName +
"': could not satisfy dependencies. Detail: " + ex.getMessage());
}
if (i == constructors.length - 1 && constructorToUse == null) {
// all constructors tried
throw ex;
}
else {
// swallow and try next constructor
}
}
}
if (constructorToUse == null) {
throw new BeanCreationException(
mergedBeanDefinition.getResourceDescription(), beanName, "Could not resolve matching constructor");
}
Object beanInstance = this.instantiationStrategy.instantiate(
mergedBeanDefinition, beanName, this, constructorToUse, argsToUse);
bw.setWrappedInstance(beanInstance);
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' instantiated via constructor [" + constructorToUse + "]");
}
return bw;
}
/**
* Resolve the constructor arguments for this bean into the resolvedValues object.
* This may involve looking up other beans.
* This method is also used for handling invocations of static factory methods.
*/
private int resolveConstructorArguments(
String beanName, RootBeanDefinition mergedBeanDefinition,
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
int minNrOfArgs;
minNrOfArgs = cargs.getArgumentCount();
for (Iterator it = cargs.getIndexedArgumentValues().entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
int index = ((Integer) entry.getKey()).intValue();
if (index < 0) {
throw new BeanCreationException(
mergedBeanDefinition.getResourceDescription(), beanName,
"Invalid constructor argument index: " + index);
}
if (index > minNrOfArgs) {
minNrOfArgs = index + 1;
}
String argName = "constructor argument with index " + index;
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) entry.getValue();
Object resolvedValue =
resolveValueIfNecessary(beanName, mergedBeanDefinition, argName, valueHolder.getValue());
resolvedValues.addIndexedArgumentValue(index, resolvedValue, valueHolder.getType());
}
for (Iterator it = cargs.getGenericArgumentValues().iterator(); it.hasNext();) {
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) it.next();
String argName = "constructor argument";
Object resolvedValue =
resolveValueIfNecessary(beanName, mergedBeanDefinition, argName, valueHolder.getValue());
resolvedValues.addGenericArgumentValue(resolvedValue, valueHolder.getType());
}
return minNrOfArgs;
}
/**
* Create an array of arguments to invoke a Constructor or static factory method,
* given the resolved constructor arguments values.
*/
private Object[] createArgumentArray(
String beanName, RootBeanDefinition mergedBeanDefinition,
ConstructorArgumentValues resolvedValues, BeanWrapperImpl bw, Class[] argTypes) {
Object[] args = new Object[argTypes.length];
Set usedValueHolders = new HashSet(argTypes.length);
for (int j = 0; j < argTypes.length; j++) {
ConstructorArgumentValues.ValueHolder valueHolder = resolvedValues.getArgumentValue(j, argTypes[j]);
if (valueHolder != null && !usedValueHolders.contains(valueHolder)) {
// Do not consider the same value definition multiple times!
usedValueHolders.add(valueHolder);
// Synchronize if custom editors are registered.
// Necessary because PropertyEditors are not thread-safe.
if (!getCustomEditors().isEmpty()) {
synchronized (getCustomEditors()) {
args[j] = bw.doTypeConversionIfNecessary(valueHolder.getValue(), argTypes[j]);
}
}
else {
args[j] = bw.doTypeConversionIfNecessary(valueHolder.getValue(), argTypes[j]);
}
}
else {
if (mergedBeanDefinition.getResolvedAutowireMode() != RootBeanDefinition.AUTOWIRE_CONSTRUCTOR) {
throw new UnsatisfiedDependencyException(
mergedBeanDefinition.getResourceDescription(), beanName, j, argTypes[j],
"Did you specify the correct bean references as generic constructor arguments?");
}
Map matchingBeans = findMatchingBeans(argTypes[j]);
if (matchingBeans == null || matchingBeans.size() != 1) {
throw new UnsatisfiedDependencyException(
mergedBeanDefinition.getResourceDescription(), beanName, j, argTypes[j],
"There are " + matchingBeans.size() + " beans of type [" + argTypes[j] +
"] for autowiring constructor. There should have been 1 to be able to " +
"autowire constructor of bean '" + beanName + "'.");
}
String autowiredBeanName = (String) matchingBeans.keySet().iterator().next();
Object autowiredBean = matchingBeans.values().iterator().next();
args[j] = autowiredBean;
if (mergedBeanDefinition.isSingleton()) {
registerDependentBean(autowiredBeanName, beanName);
}
if (logger.isInfoEnabled()) {
logger.info("Autowiring by type from bean name '" + beanName +
"' via constructor to bean named '" + matchingBeans.keySet().iterator().next() + "'");
}
}
}
return args;
}
/**
* Determine a weight that represents the class hierarchy difference between types and
* arguments. A direct match, i.e. type Integer -> arg of class Integer, does not increase
* the result - all direct matches means weight 0. A match between type Object and arg of
* class Integer would increase the weight by 2, due to the superclass 2 steps up in the
* hierarchy (i.e. Object) being the last one that still matches the required type Object.
* Type Number and class Integer would increase the weight by 1 accordingly, due to the
* superclass 1 step up the hierarchy (i.e. Number) still matching the required type Number.
* Therefore, with an arg of type Integer, a constructor (Integer) would be preferred to a
* constructor (Number) which would in turn be preferred to a constructor (Object).
* All argument weights get accumulated.
* @param argTypes the argument types to match
* @param args the arguments to match
* @return the accumulated weight for all arguments
*/
private int getTypeDifferenceWeight(Class[] argTypes, Object[] args) {
int result = 0;
for (int i = 0; i < argTypes.length; i++) {
if (!BeanUtils.isAssignable(argTypes[i], args[i])) {
return Integer.MAX_VALUE;
}
if (args[i] != null) {
Class superClass = args[i].getClass().getSuperclass();
while (superClass != null) {
if (argTypes[i].isAssignableFrom(superClass)) {
result++;
superClass = superClass.getSuperclass();
}
else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -