📄 abstractautowirecapablebeanfactory.java
字号:
for (int j = 0; j < argTypes.length; j++) {
ConstructorArgumentValues.ValueHolder valueHolder = resolvedValues.getArgumentValue(j, argTypes[j]);
if (valueHolder != null) {
// synchronize if custom editors are registered
// necessary because PropertyEditors are not thread-safe
if (!getCustomEditors().isEmpty()) {
synchronized (this) {
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(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(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 + "'.");
}
args[j] = matchingBeans.values().iterator().next();
logger.info("Autowiring by type from bean name '" + beanName +
"' via constructor to bean named '" + matchingBeans.keySet().iterator().next() + "'");
}
}
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");
}
bw.setWrappedInstance(BeanUtils.instantiateClass(constructorToUse, argsToUse));
logger.info("Bean '" + beanName + "' instantiated via constructor [" + constructorToUse + "]");
return bw;
}
/**
* 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 {
superClass = null;
}
}
}
}
return result;
}
/**
* Populate the bean instance in the given BeanWrapper with the property values
* from the bean definition.
* @param beanName name of the bean
* @param mergedBeanDefinition the bean definition for the bean
* @param bw BeanWrapper with bean instance
*/
protected void populateBean(String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw) {
PropertyValues pvs = mergedBeanDefinition.getPropertyValues();
if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues mpvs = new MutablePropertyValues(pvs);
// add property values based on autowire by name if it's applied
if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mergedBeanDefinition, bw, mpvs);
}
// add property values based on autowire by type if it's applied
if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mergedBeanDefinition, bw, mpvs);
}
pvs = mpvs;
}
dependencyCheck(beanName, mergedBeanDefinition, bw, pvs);
applyPropertyValues(beanName, mergedBeanDefinition, bw, pvs);
}
/**
* Fills in any missing property values with references to
* other beans in this factory if autowire is set to "byName".
* @param beanName name of the bean we're wiring up.
* Useful for debugging messages; not used functionally.
* @param mergedBeanDefinition bean definition to update through autowiring
* @param bw BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByName(String beanName, RootBeanDefinition mergedBeanDefinition,
BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedObjectProperties(mergedBeanDefinition, bw);
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.addPropertyValue(propertyName, bean);
if (logger.isDebugEnabled()) {
logger.debug("Added autowiring by name from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by name: no matching bean found");
}
}
}
}
/**
* Abstract method defining "autowire by type" (bean properties by type) behavior.
* <p>This is like PicoContainer default, in which there must be exactly one bean
* of the property type in the bean factory. This makes bean factories simple to
* configure for small namespaces, but doesn't work as well as standard Spring
* behavior for bigger applications.
* @param beanName name of the bean to autowire by type
* @param mergedBeanDefinition bean definition to update through autowiring
* @param bw BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByType(String beanName, RootBeanDefinition mergedBeanDefinition,
BeanWrapper bw, MutablePropertyValues pvs) {
String[] propertyNames = unsatisfiedObjectProperties(mergedBeanDefinition, bw);
for (int i = 0; i < propertyNames.length; i++) {
String propertyName = propertyNames[i];
// look for a matching type
Class requiredType = bw.getPropertyDescriptor(propertyName).getPropertyType();
Map matchingBeans = findMatchingBeans(requiredType);
if (matchingBeans != null && matchingBeans.size() == 1) {
pvs.addPropertyValue(propertyName, matchingBeans.values().iterator().next());
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' via property '" + propertyName + "' to bean named '" +
matchingBeans.keySet().iterator().next() + "'");
}
}
else if (matchingBeans != null && matchingBeans.size() > 1) {
throw new UnsatisfiedDependencyException(beanName, propertyName,
"There are " + matchingBeans.size() + " beans of type [" + requiredType + "] for autowire by type. " +
"There should have been 1 to be able to autowire property '" + propertyName + "' of bean '" + beanName + "'.");
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not autowiring property '" + propertyName + "' of bean '" + beanName +
"' by type: no matching bean found");
}
}
}
}
/**
* Perform a dependency check that all properties exposed have been set,
* if desired. Dependency checks can be objects (collaborating beans),
* simple (primitives and String), or all (both).
* @param beanName name of the bean
* @throws org.springframework.beans.factory.UnsatisfiedDependencyException
*/
protected void dependencyCheck(String beanName, RootBeanDefinition mergedBeanDefinition,
BeanWrapper bw, PropertyValues pvs)
throws UnsatisfiedDependencyException {
int dependencyCheck = mergedBeanDefinition.getDependencyCheck();
if (dependencyCheck == RootBeanDefinition.DEPENDENCY_CHECK_NONE)
return;
Set ignoreTypes = getIgnoredDependencyTypes();
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (int i = 0; i < pds.length; i++) {
if (pds[i].getWriteMethod() != null &&
!ignoreTypes.contains(pds[i].getPropertyType()) &&
pvs.getPropertyValue(pds[i].getName()) == null) {
boolean isSimple = BeanUtils.isSimpleProperty(pds[i].getPropertyType());
boolean unsatisfied = (dependencyCheck == RootBeanDefinition.DEPENDENCY_CHECK_ALL) ||
(isSimple && dependencyCheck == RootBeanDefinition.DEPENDENCY_CHECK_SIMPLE) ||
(!isSimple && dependencyCheck == RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
if (unsatisfied) {
throw new UnsatisfiedDependencyException(beanName, pds[i].getName(), null);
}
}
}
}
/**
* Return an array of object-type property names that are unsatisfied.
* These are probably unsatisfied references to other beans in the
* factory. Does not include simple properties like primitives or Strings.
* @return an array of object-type property names that are unsatisfied
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
protected String[] unsatisfiedObjectProperties(RootBeanDefinition mergedBeanDefinition, BeanWrapper bw) {
Set result = new TreeSet();
Set ignoreTypes = getIgnoredDependencyTypes();
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (int i = 0; i < pds.length; i++) {
String name = pds[i].getName();
if (pds[i].getWriteMethod() != null &&
!BeanUtils.isSimpleProperty(pds[i].getPropertyType()) &&
!ignoreTypes.contains(pds[i].getPropertyType()) &&
mergedBeanDefinition.getPropertyValues().getPropertyValue(name) == null) {
result.add(name);
}
}
return (String[]) result.toArray(new String[result.size()]);
}
/**
* Apply the given property values, resolving any runtime references
* to other beans in this bean factory.
* Must use deep copy, so we don't permanently modify this property
* @param beanName bean name passed for better exception information
* @param bw BeanWrapper wrapping the target object
* @param pvs new property values
*/
protected void applyPropertyValues(String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw,
PropertyValues pvs) throws BeansException {
if (pvs == null) {
return;
}
MutablePropertyValues deepCopy = new MutablePropertyValues(pvs);
PropertyValue[] pvals = deepCopy.getPropertyValues();
for (int i = 0; i < pvals.length; i++) {
Object value = resolveValueIfNecessary(beanName, mergedBeanDefinition,
pvals[i].getName(), pvals[i].getValue());
PropertyValue pv = new PropertyValue(pvals[i].getName(), value);
// update mutable copy
deepCopy.setPropertyValueAt(pv, i);
}
// set our (possibly massaged) deepCopy
try {
// synchronize if custom editors are registered
// necessary because PropertyEditors are not thread-safe
if (!getCustomEditors().isEmpty()) {
synchronized (this) {
bw.setPropertyValues(deepCopy);
}
}
else {
bw.setPropertyValues(deepCopy);
}
}
catch (BeansException ex) {
// improve the message by showing the context
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -