📄 abstractautowirecapablebeanfactory.java
字号:
/**
* 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)
throws BeansException {
String[] propertyNames = unsatisfiedNonSimpleProperties(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) {
String autowiredBeanName = (String) matchingBeans.keySet().iterator().next();
Object autowiredBean = matchingBeans.values().iterator().next();
pvs.addPropertyValue(propertyName, autowiredBean);
if (mergedBeanDefinition.isSingleton()) {
registerDependentBean(autowiredBeanName, beanName);
}
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
propertyName + "' to bean named '" + autowiredBeanName + "'");
}
}
else if (matchingBeans != null && matchingBeans.size() > 1) {
throw new UnsatisfiedDependencyException(
mergedBeanDefinition.getResourceDescription(), 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");
}
}
}
}
/**
* Return an array of non-simple bean properties that are unsatisfied.
* These are probably unsatisfied references to other beans in the
* factory. Does not include simple properties like primitives or Strings.
* @param mergedBeanDefinition the bean definition the bean was created with
* @param bw the BeanWrapper the bean was created with
* @return an array of bean property names
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
protected String[] unsatisfiedNonSimpleProperties(RootBeanDefinition mergedBeanDefinition, BeanWrapper bw) {
Set result = new TreeSet();
PropertyValues pvs = mergedBeanDefinition.getPropertyValues();
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (int i = 0; i < pds.length; i++) {
if (pds[i].getWriteMethod() != null && !isExcludedFromDependencyCheck(pds[i]) &&
!pvs.contains(pds[i].getName()) && !BeanUtils.isSimpleProperty(pds[i].getPropertyType())) {
result.add(pds[i].getName());
}
}
return StringUtils.toStringArray(result);
}
/**
* 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 the name of the bean
* @param mergedBeanDefinition the bean definition the bean was created with
* @param bw the BeanWrapper the bean was created with
* @param pvs the property values to be applied to the bean
* @see #isExcludedFromDependencyCheck(java.beans.PropertyDescriptor)
*/
protected void checkDependencies(
String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw, PropertyValues pvs)
throws UnsatisfiedDependencyException {
int dependencyCheck = mergedBeanDefinition.getDependencyCheck();
if (dependencyCheck == RootBeanDefinition.DEPENDENCY_CHECK_NONE) {
return;
}
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (int i = 0; i < pds.length; i++) {
if (pds[i].getWriteMethod() != null && !isExcludedFromDependencyCheck(pds[i]) &&
!pvs.contains(pds[i].getName())) {
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(
mergedBeanDefinition.getResourceDescription(), beanName, pds[i].getName(),
"set this property value or disable dependency checking for this bean");
}
}
}
}
/**
* Determine whether the given bean property is excluded from dependency checks.
* <p>This implementation excludes properties defined by CGLIB and
* properties whose type matches an ignored dependency type or which
* are defined by an ignored dependency interface.
* @param pd the PropertyDescriptor of the bean property
* @return whether the bean property is excluded
* @see AutowireUtils#isExcludedFromDependencyCheck(java.beans.PropertyDescriptor)
* @see #ignoreDependencyType(Class)
* @see #ignoreDependencyInterface(Class)
* @see AutowireUtils#isExcludedFromDependencyCheck
* @see AutowireUtils#isSetterDefinedInInterface
*/
protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
getIgnoredDependencyTypes().contains(pd.getPropertyType()) ||
AutowireUtils.isSetterDefinedInInterface(pd, getIgnoredDependencyInterfaces()));
}
/**
* 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
*/
private void applyPropertyValues(
String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw, PropertyValues pvs)
throws BeansException {
if (pvs == null) {
return;
}
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this, beanName, mergedBeanDefinition);
// Create a deep copy, resolving any references for values.
MutablePropertyValues deepCopy = new MutablePropertyValues();
PropertyValue[] pvArray = pvs.getPropertyValues();
for (int i = 0; i < pvArray.length; i++) {
PropertyValue pv = pvArray[i];
Object resolvedValue =
valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue());
deepCopy.addPropertyValue(pvArray[i].getName(), resolvedValue);
}
// Set our (possibly massaged) deep copy.
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.
throw new BeanCreationException(
mergedBeanDefinition.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
/**
* Give a bean a chance to react now all its properties are set,
* and a chance to know about its owning bean factory (this object).
* This means checking whether the bean implements InitializingBean or defines
* a custom init method, and invoking the necessary callback(s) if it does.
* @param beanName the bean has in the factory. Used for debug output.
* @param bean new bean instance we may need to initialize
* @param mergedBeanDefinition the bean definition that the bean was created with
* (can also be <code>null</code>, if given an existing bean instance)
* @throws Throwable if thrown by init methods or by the invocation process
* @see #invokeCustomInitMethod
*/
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mergedBeanDefinition)
throws Throwable {
if (bean instanceof InitializingBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
((InitializingBean) bean).afterPropertiesSet();
}
if (mergedBeanDefinition != null && mergedBeanDefinition.getInitMethodName() != null) {
invokeCustomInitMethod(
beanName, bean, mergedBeanDefinition.getInitMethodName(), mergedBeanDefinition.isEnforceInitMethod());
}
}
/**
* Invoke the specified custom init method on the given bean.
* Called by invokeInitMethods.
* <p>Can be overridden in subclasses for custom resolution of init
* methods with arguments.
* @param beanName the bean has in the factory. Used for debug output.
* @param bean new bean instance we may need to initialize
* @param initMethodName the name of the custom init method
* @param enforceInitMethod indicates whether the defined init method needs to exist
* @see #invokeInitMethods
*/
protected void invokeCustomInitMethod(
String beanName, Object bean, String initMethodName, boolean enforceInitMethod) throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom init method '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
if (initMethod == null) {
if (enforceInitMethod) {
throw new NoSuchMethodException("Couldn't find an init method named '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
else {
// Ignore non-existent default lifecycle methods.
return;
}
}
if (!Modifier.isPublic(initMethod.getModifiers())) {
initMethod.setAccessible(true);
}
try {
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
//---------------------------------------------------------------------
// Abstract method to be implemented by subclasses
//---------------------------------------------------------------------
/**
* Find bean instances that match the required type. Called by autowiring.
* If a subclass cannot obtain information about bean names by type,
* a corresponding exception should be thrown.
* @param requiredType the type of the beans to look up
* @return a Map of bean names and bean instances that match the required type,
* or <code>null</code> if none found
* @throws BeansException in case of errors
* @see #autowireByType
* @see #autowireConstructor
*/
protected abstract Map findMatchingBeans(Class requiredType) throws BeansException;
//---------------------------------------------------------------------
// Private inner class for internal use
//---------------------------------------------------------------------
private static class ArgumentsHolder {
public Object rawArguments[];
public Object arguments[];
public ArgumentsHolder(int size) {
this.rawArguments = new Object[size];
this.arguments = new Object[size];
}
public ArgumentsHolder(Object[] args) {
this.rawArguments = args;
this.arguments = args;
}
public int getTypeDifferenceWeight(Class[] argTypes) {
// If valid arguments found, determine type difference weight.
// Try type difference weight on both the converted arguments and
// the raw arguments. If the raw weight is better, use it.
// Decrease raw weight by 1024 to prefer it over equal converted weight.
int typeDiffWeight = AutowireUtils.getTypeDifferenceWeight(argTypes, this.arguments);
int rawTypeDiffWeight = AutowireUtils.getTypeDifferenceWeight(argTypes, this.rawArguments) - 1024;
return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -