package org.theonefx.wcframework.ioc; import java.beans.PropertyChangeEvent; import java.beans.PropertyDescriptor; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.theonefx.wcframework.core.ClassWrapper; import org.theonefx.wcframework.core.TypeConverterDelegate; import org.theonefx.wcframework.core.exception.BeansException; import org.theonefx.wcframework.core.inject.InjectByField; import org.theonefx.wcframework.core.inject.InjectBySetter; import org.theonefx.wcframework.core.inject.Injector; import org.theonefx.wcframework.core.propertyeditor.PropertyTypeDescriptor; import org.theonefx.wcframework.core.propertyeditor.TypeDescriptor; import org.theonefx.wcframework.ioc.exception.ConversionException; import org.theonefx.wcframework.ioc.exception.ConversionNotSupportedException; import org.theonefx.wcframework.ioc.exception.ConverterNotFoundException; import org.theonefx.wcframework.ioc.exception.InvalidPropertyException; import org.theonefx.wcframework.ioc.exception.MethodInvocationException; import org.theonefx.wcframework.ioc.exception.NotReadablePropertyException; import org.theonefx.wcframework.ioc.exception.NotWritablePropertyException; import org.theonefx.wcframework.ioc.exception.NullValueInNestedPathException; import org.theonefx.wcframework.ioc.exception.TypeMismatchException; import org.theonefx.wcframework.ioc.val.GenericCollectionTypeResolver; import org.theonefx.wcframework.ioc.val.MethodParameter; import org.theonefx.wcframework.utils.Assert; import org.theonefx.wcframework.utils.BeanUtils; import org.theonefx.wcframework.utils.ObjectUtils; import org.theonefx.wcframework.utils.StringUtils; public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper { private static final Log log = LogFactory.getLog(BeanWrapperImpl.class); private Object object; private TypeConverterDelegate typeConverterDelegate; private AccessControlContext acc; private CachedIntrospectionResults cachedIntrospectionResults; public BeanWrapperImpl() { this.typeConverterDelegate = new TypeConverterDelegate(this); } public BeanWrapperImpl(Object object) { setWrappedInstance(object); } // --------------------------------------------------------------------- // BeanWrapper接口的实现 // --------------------------------------------------------------------- public void setWrappedInstance(Object object) { Assert.notNull(object, "Bean object must not be null"); if (log.isDebugEnabled()) { log.debug("设置BeanWrapperImpl的包装Bean:" + object); } this.object = object; this.typeConverterDelegate = new TypeConverterDelegate(this, object); setIntrospectionClass(object.getClass()); } public final Object getWrappedInstance() { return this.object; } public final Class<?> getWrappedClass() { return (this.object != null ? this.object.getClass() : null); } /** * 设置调用封装的bean实例的方法时使用的安全上下文. 可以设置null. */ public void setSecurityContext(AccessControlContext acc) { this.acc = acc; } /** * 返回调用封装的bean实例的方法时使用的安全上下文. 可能是null. */ public AccessControlContext getSecurityContext() { return this.acc; } /** * 设置内省的Class. * 需要在目标对象改变的时候调用. */ protected void setIntrospectionClass(Class<?> clazz) { if (this.cachedIntrospectionResults != null && !clazz.equals(this.cachedIntrospectionResults.getBeanClass())) { this.cachedIntrospectionResults = null; } } /** * 获得一个延迟初始化封装对象的CachedIntrospectionResults实例 */ private CachedIntrospectionResults getCachedIntrospectionResults() { Assert.state(this.object != null, "BeanWrapper does not hold a bean instance"); if (this.cachedIntrospectionResults == null) { this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass()); } return this.cachedIntrospectionResults; } public PropertyDescriptor[] getPropertyDescriptors() { return getCachedIntrospectionResults().getPropertyDescriptors(); } public PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException { PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName); if (pd == null) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "找不到名称为 '" + propertyName + "' 的属性"); } return pd; } /** * {@link #getPropertyDescriptor}的内部版本: 如果没有找到对应的属性则返回 <code>null</code>而不是抛出异常. * * @param propertyName 需要获得PropertyDescriptor的属性的名称 * @return 指定属性的PropertyDescriptor, 如果找不到则返回<code>null</code> * @throws BeansException 内省失败时抛出此异常 */ protected PropertyDescriptor getPropertyDescriptorInternal(String propertyName) throws BeansException { return getCachedIntrospectionResults().getPropertyDescriptor(propertyName); } public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException { try { return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); } catch (ConverterNotFoundException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); } catch (ConversionException ex) { throw new TypeMismatchException(value, requiredType, ex); } catch (IllegalStateException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); } catch (IllegalArgumentException ex) { throw new TypeMismatchException(value, requiredType, ex); } } private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType, TypeDescriptor td) throws TypeMismatchException { try { return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td); } catch (ConverterNotFoundException ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue); throw new ConversionNotSupportedException(pce, requiredType, ex); } catch (ConversionException ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue); throw new TypeMismatchException(pce, requiredType, ex); } catch (IllegalStateException ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue); throw new ConversionNotSupportedException(pce, requiredType, ex); } catch (IllegalArgumentException ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, newValue); throw new TypeMismatchException(pce, requiredType, ex); } } /** * 将给定的value装换为与指定属性最相近的类型 * @param value 需要被转换的值 * @param propertyName 目标属性的名称 * @return 新的值 * @throws TypeMismatchException 类型转换失败则抛出此异常 */ public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); return convertForProperty(propertyName, null, value, pd); } /** * 属性转换的内部实现 * @param propertyName 属性名称 * @param oldValue 属性原值 * @param newValue 属性新值 * @param pd JavaBean的属性描述 * @return 转换完成后的对象 * @throws TypeMismatchException 类型不匹配时发生此异常,即无法完成转换 */ private Object convertForProperty(String propertyName, Object oldValue, Object newValue, PropertyDescriptor pd) throws TypeMismatchException { TypeDescriptor descriptor = null; Class<?> propertyType = null; if (pd != null) { Method method = pd.getWriteMethod(); if (method != null) { Type[] types = method.getGenericParameterTypes(); if (types != null && types.length > 0) { propertyType = method.getParameterTypes()[0]; } descriptor = new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd)); } } if (descriptor == null) { ClassWrapper<?> wrapper = ClassWrapper.getWrapper(getWrappedClass()); Field field = wrapper.getField(propertyName); propertyType = field.getType(); descriptor = new TypeDescriptor(field); } return convertIfNecessary(propertyName, oldValue, newValue, propertyType, descriptor); } // --------------------------------------------------------------------- // PropertyAccessor接口的实现 // --------------------------------------------------------------------- @Override public Object getPropertyValue(String propertyName) throws BeansException { PropertyTokenHolder tokens = getPropertyNameTokens(propertyName); return getPropertyValue(tokens); } /** * 解析给定的属性名称,并转换为对应的属性名称标记 * @param propertyName 需要解析的属性名称 * @return 代表解析出来的属性的标记 */ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { PropertyTokenHolder tokens = new PropertyTokenHolder(); String actualName = null; List<String> keys = new ArrayList<String>(2); int searchIndex = 0; while (searchIndex != -1) { int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex); searchIndex = -1; if (keyStart != -1) { int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length()); if (keyEnd != -1) { if (actualName == null) { actualName = propertyName.substring(0, keyStart); } String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd); if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) { key = key.substring(1, key.length() - 1); } keys.add(key); searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length(); } } } tokens.actualName = (actualName != null ? actualName : propertyName); tokens.canonicalName = tokens.actualName; if (!keys.isEmpty()) { tokens.canonicalName += PROPERTY_KEY_PREFIX + StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + PROPERTY_KEY_SUFFIX; tokens.keys = StringUtils.toStringArray(keys); } // 例如: // actualName = propertya // canonicalName = propertya[x][y] // keys = [x,y] return tokens; } /** * 根据属性名称标记获取属性值 * @param tokens 属性名称标记 * @return 属性值 * @throws BeansException 可能在获取过程中发生的异常 */ private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException { // 规范名称 String propertyName = tokens.canonicalName; // 真是属性名 String actualName = tokens.actualName; // 根据真实属性名获取PropertyDescriptor PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); // 该属性必须有PropertyDescriptor 同时还得有读取方法 if (pd == null || pd.getReadMethod() == null) { throw new NotReadablePropertyException(getWrappedClass(), propertyName); } final Method readMethod = pd.getReadMethod(); try { // 调用readMethod读取属性值 if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()) && !readMethod.isAccessible()) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { readMethod.setAccessible(true); return null; } }); } else { readMethod.setAccessible(true); } } Object value; if (System.getSecurityManager() != null) { try { value = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return readMethod.invoke(object, (Object[]) null); } }, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { value = readMethod.invoke(object, (Object[]) null); } // 此时已经获取了object中属性值,处理规范名称与属性名称不一致的情况(也就是说,需要获取的是一个集合/Map中的某一个值而非集合/Map本身) if (tokens.keys != null) { if (value == null) { throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "属性'" + propertyName + "'为null,无法再根据名称中的path获取内部的值"); } // 应用索引号和Map键 for (int i = 0; i < tokens.keys.length; i++) { String key = tokens.keys[i]; if (value == null) { throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "属性'" + propertyName + "'为null,无法再根据名称中的path获取内部的值"); } else if (value.getClass().isArray()) { int index = Integer.parseInt(key); value = Array.get(value, index); } else if (value instanceof List) { int index = Integer.parseInt(key); List<?> list = (List<?>) value; value = list.get(index); } else if (value instanceof Set) { // 由于Set本身没有基于索引号获取内容的方法 // 这里只能使用便利找到对应位置的元素 Set<?> set = (Set<?>) value; int index = Integer.parseInt(key); if (index < 0 || index >= set.size()) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法在大小为 " + set.size() + " 的Set中获取索引号为 " + index + "的元素, 属性名称为 '" + propertyName + "'"); } Iterator<?> it = set.iterator(); for (int j = 0; it.hasNext(); j++) { Object elem = it.next(); if (j == index) { value = elem; break; } } } else if (value instanceof Map) { Map<?, ?> map = (Map<?, ?>) value; Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(readMethod, i + 1); Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, new PropertyTypeDescriptor(pd, new MethodParameter(readMethod, -1), mapKeyType)); value = map.get(convertedMapKey); } else { // 不是数组、不是集合、不是map。。。天知道是什么东西 throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法读取名为 '" + propertyName + "' 值为 '"+ value+"' 的属性,因为他既不是一个数组、也不是一个List或者Set或者Map"); } } } return value; } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "路径 '" + propertyName + "' 的属性索引号超出范围", ex); } catch (NumberFormatException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "属性路径 '" + propertyName + "' 中的索引号无效", ex); } catch (TypeMismatchException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "属性路径 '" + propertyName + "' 中的索引号无效", ex); } catch (InvocationTargetException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "获得名称为 '" + actualName + "' 的属性时发生异常", ex); } catch (Exception ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "尝试获取名称为 '" + actualName + "' 的属性时发生异常", ex); } } @Override public void setPropertyValue(String propertyName, Object value) throws BeansException { PropertyTokenHolder tokens = getPropertyNameTokens(propertyName); setPropertyValue(tokens, new PropertyValue(propertyName, value)); } @Override public void setPropertyValue(PropertyValue pv) throws BeansException { PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens; if (tokens == null) { String propertyName = pv.getName(); tokens = getPropertyNameTokens(propertyName); pv.getOriginalPropertyValue().resolvedTokens = tokens; } setPropertyValue(tokens, pv); } @SuppressWarnings("unchecked") private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException { String propertyName = tokens.canonicalName; String actualName = tokens.actualName; if (tokens.keys != null) { // keys不为空,代表需要设置的属性是一个嵌套属性,需要根据key据需向内部查找 PropertyTokenHolder getterTokens = new PropertyTokenHolder(); getterTokens.canonicalName = tokens.canonicalName; getterTokens.actualName = tokens.actualName; getterTokens.keys = new String[tokens.keys.length - 1]; // 跳过最后一个key,目的是为了获取最后一个key的上层值 System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1); Object propValue; try { // 这个propValue是目标属性的上层属性值 propValue = getPropertyValue(getterTokens); } catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getWrappedClass(), propertyName, "无法设置属性路径为 '" + propertyName + "'的值,因为他的上层不是可读的", ex); } // 设置最后一个key的值 String key = tokens.keys[tokens.keys.length - 1]; // 首先需要保证最后一个key的上层属性值不为null,否则无法设置最有一个key的值 if (propValue == null) { throw new NullValueInNestedPathException(getWrappedClass(), propertyName, "无法设置属性路径为 '" + propertyName + "'的值,因为他的上层属性值为null"); } else if (propValue.getClass().isArray()) { // 是一个数组 PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class<?> requiredType = propValue.getClass().getComponentType(); int arrayIndex = Integer.parseInt(key); Object oldValue = null; try { Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType)); Array.set(propValue, arrayIndex, convertedValue); } catch (IndexOutOfBoundsException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "路径为'" + propertyName + "'的属性其索引号是无效的", ex); } } else if (propValue instanceof List) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class<?> requiredType = GenericCollectionTypeResolver.getCollectionReturnType(pd.getReadMethod(), tokens.keys.length); List<Object> list = (List<Object>) propValue; int index = Integer.parseInt(key); Object oldValue = null; Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), requiredType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), requiredType)); if (index < list.size()) { list.set(index, convertedValue); } else if (index >= list.size()) { for (int i = list.size(); i < index; i++) { try { list.add(null); } catch (NullPointerException ex) { throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法设置索引号为 " + index + ",大小为" + list.size() + ",属性路径为 '" + propertyName + "'的list的元素: 该list不支持null为其元素"); } } list.add(convertedValue); } } else if (propValue instanceof Map) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); Class<?> mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), tokens.keys.length); Class<?> mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(pd.getReadMethod(), tokens.keys.length); Map<Object, Object> map = (Map<Object, Object>) propValue; Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), mapKeyType)); Object oldValue = null; Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(), mapValueType, new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1))); map.put(convertedMapKey, convertedMapValue); } else { throw new InvalidPropertyException(getWrappedClass(), propertyName, "无法设置名为 '" + propertyName + "' 值为 '"+ pv.getValue()+"' 的属性,因为他既不是一个数组、也不是一个List或者Set或者Map"); } } else { // keys为null,代表着actualName与canonicalName相同,也就是说,actualName就是我们需要设置的属性 PropertyDescriptor pd = pv.resolvedDescriptor; if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) { pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName); if (pd != null) { pv.getOriginalPropertyValue().resolvedDescriptor = pd; } } Injector injector = pv.resolvedInjector; if (injector == null) { if (pd == null || pd.getWriteMethod() == null) { // 没有对应的Set方法 injector = new InjectByField(ClassWrapper.getWrapper(getWrappedClass()).getField(pv.getName())); } else { // 有对应的Set方法 Method writeMethod = (pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess() : pd.getWriteMethod()); injector = new InjectBySetter(writeMethod); } pv.getOriginalPropertyValue().resolvedInjector = injector; } Object oldValue = null; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; // 检查是否需要类型转换 // pv.conversionNecessary的默认值是null,也就是说,只要不是false都会进行转换 // 完成了第一次转换以后会对转换前后的值进行对比,以确定pv.conversionNecessary的真正的值 if (!Boolean.FALSE.equals(pv.conversionNecessary)) { if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd); } // 这里是一个原型模式 // 只有在第一次尝试转换以后才能知道以后这个PropertyValue是否还需要转换 pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } injector.inject(object, valueToApply, acc); } catch (TypeMismatchException ex) { throw ex; } catch (Exception ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.object, propertyName, oldValue, pv.getValue()); throw new MethodInvocationException(pce, ex); } } } @Override public String toString() { StringBuilder sb = new StringBuilder(getClass().getName()); if (this.object != null) { sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.object)).append("]"); } else { sb.append(": no wrapped object set"); } return sb.toString(); } // --------------------------------------------------------------------- // 内部使用的内部类 // --------------------------------------------------------------------- private static class PropertyTokenHolder { /** * 规范名称 */ public String canonicalName; /** * 真实名称 */ public String actualName; public String[] keys; } }