package org.theonefx.wcframework.ioc; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.theonefx.wcframework.core.TypeConverter; import org.theonefx.wcframework.core.exception.BeanCreationException; import org.theonefx.wcframework.core.exception.BeansException; import org.theonefx.wcframework.ioc.val.ManagedArray; import org.theonefx.wcframework.ioc.val.ManagedList; import org.theonefx.wcframework.ioc.val.ManagedMap; import org.theonefx.wcframework.ioc.val.ManagedProperties; import org.theonefx.wcframework.ioc.val.ManagedSet; import org.theonefx.wcframework.ioc.val.TypedStringValue; import org.theonefx.wcframework.utils.ClassUtils; import org.theonefx.wcframework.utils.StringUtils; /** * BeanDefinition的值解析器 * BeanFactory实现过程中的一个工具类,用于将BeanDefinition中的值定义转换成应用于目标对象的真实值。 */ class BeanDefinitionValueResolver { private final AbstractBeanFactory beanFactory; private final String beanName; private final TypeConverter typeConverter; public BeanDefinitionValueResolver(AbstractBeanFactory beanFactory, String beanName, BeanDefinition beanDefinition, TypeConverter typeConverter) { this.beanFactory = beanFactory; this.beanName = beanName; this.typeConverter = typeConverter; } public Object resolveValueIfNecessary(Object argName, Object value) { // We must check each value to see whether it requires a runtime // reference // to another bean to be resolved. if (value instanceof AutoFoundRuntimeBeanReference) { AutoFoundRuntimeBeanReference aref = (AutoFoundRuntimeBeanReference) value; return resolveAutoFoundReference(argName, aref); } else if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); } else if (value instanceof ManagedArray) { // May need to resolve contained runtime references. ManagedArray array = (ManagedArray) value; Class<?> elementType = array.resolvedElementType; if (elementType == null) { String elementTypeName = array.getElementTypeName(); if (StringUtils.isNotBlank(elementTypeName)) { try { elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader()); array.resolvedElementType = elementType; } catch (Throwable ex) { // Improve the message by showing the context. throw new BeanCreationException(this.beanName, "Error resolving array type for " + argName, ex); } } else { elementType = Object.class; } } return resolveManagedArray(argName, (List<?>) value, elementType); } else if (value instanceof ManagedList) { // May need to resolve contained runtime references. return resolveManagedList(argName, (List<?>) value); } else if (value instanceof ManagedSet) { // May need to resolve contained runtime references. return resolveManagedSet(argName, (Set<?>) value); } else if (value instanceof ManagedMap) { // May need to resolve contained runtime references. return resolveManagedMap(argName, (Map<?, ?>) value); } else if (value instanceof ManagedProperties) { Properties original = (Properties) value; Properties copy = new Properties(); for (Map.Entry<?, ?> propEntry : original.entrySet()) { Object propKey = propEntry.getKey(); Object propValue = propEntry.getValue(); if (propKey instanceof TypedStringValue) { propKey = evaluate((TypedStringValue) propKey); } if (propValue instanceof TypedStringValue) { propValue = evaluate((TypedStringValue) propValue); } copy.put(propKey, propValue); } return copy; } else if (value instanceof TypedStringValue) { // Convert value to target type here. TypedStringValue typedStringValue = (TypedStringValue) value; Object valueObject = evaluate(typedStringValue); try { Class<?> resolvedTargetType = resolveTargetType(typedStringValue); if (resolvedTargetType != null) { return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType); } else { return valueObject; } } catch (Throwable ex) { // Improve the message by showing the context. throw new BeanCreationException(this.beanName, "Error converting typed String value for " + argName, ex); } } else { return value; } } /** * Evaluate the given value as an expression, if necessary. * @param value the candidate value (may be an expression) * @return the resolved value */ protected Object evaluate(TypedStringValue value) { return value.getValue(); } /** * Resolve the target type in the given TypedStringValue. * @param value the TypedStringValue to resolve * @return the resolved target type (or <code>null</code> if none specified) * @throws ClassNotFoundException if the specified type cannot be resolved * @see TypedStringValue#resolveTargetType */ protected Class<?> resolveTargetType(TypedStringValue value) throws ClassNotFoundException { if (value.hasTargetType()) { return value.getTargetType(); } return value.resolveTargetType(this.beanFactory.getBeanClassLoader()); } /** * Resolve a reference to another bean in the factory. */ private Object resolveReference(Object argName, RuntimeBeanReference ref) { try { String refName = ref.getBeanId(); Object bean = this.beanFactory.getBean(refName); return bean; } catch (BeansException ex) { throw new BeanCreationException(this.beanName, "Cannot resolve reference to bean '" + ref.getBeanId() + "' while setting " + argName, ex); } } private Object resolveAutoFoundReference(Object argName, AutoFoundRuntimeBeanReference ref) { try { String refName = ref.getFieldName(); if (this.beanFactory.containsBean(refName)) { Object bean = this.beanFactory.getBean(refName); return bean; } else if (this.beanFactory.containsBean(ref.getValueType())) { Object bean = this.beanFactory.getBean(ref.getValueType()); return bean; } else { throw new BeanCreationException(this.beanName, "没有合适的值可被注入,属性:" + argName); } } catch (BeansException ex) { throw new BeanCreationException(this.beanName, "Cannot resolve reference to bean '" + ref.getBeanId() + "' while setting " + argName, ex); } } /** * For each element in the managed array, resolve reference if necessary. */ private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) { Object resolved = Array.newInstance(elementType, ml.size()); for (int i = 0; i < ml.size(); i++) { Array.set(resolved, i, resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } /** * For each element in the managed list, resolve reference if necessary. */ private List<?> resolveManagedList(Object argName, List<?> ml) { List<Object> resolved = new ArrayList<Object>(ml.size()); for (int i = 0; i < ml.size(); i++) { resolved.add( resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i))); } return resolved; } /** * For each element in the managed set, resolve reference if necessary. */ private Set<?> resolveManagedSet(Object argName, Set<?> ms) { Set<Object> resolved = new LinkedHashSet<Object>(ms.size()); int i = 0; for (Object m : ms) { resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m)); i++; } return resolved; } /** * For each element in the managed map, resolve reference if necessary. */ private Map<?,?> resolveManagedMap(Object argName, Map<?, ?> mm) { Map<Object, Object> resolved = new LinkedHashMap<Object, Object>(mm.size()); for (Map.Entry<?,?> entry : mm.entrySet()) { Object resolvedKey = resolveValueIfNecessary(argName, entry.getKey()); Object resolvedValue = resolveValueIfNecessary( new KeyedArgName(argName, entry.getKey()), entry.getValue()); resolved.put(resolvedKey, resolvedValue); } return resolved; } /** * Holder class used for delayed toString building. */ private static class KeyedArgName { private final Object argName; private final Object key; public KeyedArgName(Object argName, Object key) { this.argName = argName; this.key = key; } @Override public String toString() { return this.argName + " with key " + BeanWrapper.PROPERTY_KEY_PREFIX + this.key + BeanWrapper.PROPERTY_KEY_SUFFIX; } } }