一、前言
基于spring-beans(4.1.4)的工具类org.springframework.beans.BeanUtils对注入spring对象按照Class实例化instantiateClass、class对象方法名称methodName查找findMethod、属性查找对于class类信息findPropertyType、对象属性复制copyProperties等常用操作,具体如下源码所示。
二、源码说明
package org.springframework.beans;@b@@b@import java.beans.PropertyDescriptor;@b@import java.beans.PropertyEditor;@b@import java.lang.reflect.Constructor;@b@import java.lang.reflect.InvocationTargetException;@b@import java.lang.reflect.Method;@b@import java.lang.reflect.Modifier;@b@import java.net.URI;@b@import java.net.URL;@b@import java.util.Arrays;@b@import java.util.Collections;@b@import java.util.Date;@b@import java.util.List;@b@import java.util.Locale;@b@import java.util.Set;@b@import org.apache.commons.logging.Log;@b@import org.apache.commons.logging.LogFactory;@b@import org.springframework.core.MethodParameter;@b@import org.springframework.util.Assert;@b@import org.springframework.util.ClassUtils;@b@import org.springframework.util.ConcurrentReferenceHashMap;@b@import org.springframework.util.ReflectionUtils;@b@import org.springframework.util.StringUtils;@b@@b@public abstract class BeanUtils@b@{@b@ private static final Log logger = LogFactory.getLog(BeanUtils.class);@b@ private static final Set<Class<?>> unknownEditorTypes = Collections.newSetFromMap(new ConcurrentReferenceHashMap(64));@b@@b@ public static <T> T instantiate(Class<T> clazz)@b@ throws BeanInstantiationException@b@ {@b@ Assert.notNull(clazz, "Class must not be null");@b@ if (clazz.isInterface())@b@ throw new BeanInstantiationException(clazz, "Specified class is an interface");@b@ try@b@ {@b@ return clazz.newInstance();@b@ }@b@ catch (InstantiationException ex) {@b@ throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex);@b@ }@b@ catch (IllegalAccessException ex) {@b@ throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex);@b@ }@b@ }@b@@b@ public static <T> T instantiateClass(Class<T> clazz)@b@ throws BeanInstantiationException@b@ {@b@ Assert.notNull(clazz, "Class must not be null");@b@ if (clazz.isInterface())@b@ throw new BeanInstantiationException(clazz, "Specified class is an interface");@b@ try@b@ {@b@ return instantiateClass(clazz.getDeclaredConstructor(new Class[0]), new Object[0]);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ throw new BeanInstantiationException(clazz, "No default constructor found", ex);@b@ }@b@ }@b@@b@ public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo)@b@ throws BeanInstantiationException@b@ {@b@ Assert.isAssignable(assignableTo, clazz);@b@ return instantiateClass(clazz);@b@ }@b@@b@ public static <T> T instantiateClass(Constructor<T> ctor, Object[] args)@b@ throws BeanInstantiationException@b@ {@b@ Assert.notNull(ctor, "Constructor must not be null");@b@ try {@b@ ReflectionUtils.makeAccessible(ctor);@b@ return ctor.newInstance(args);@b@ }@b@ catch (InstantiationException ex) {@b@ throw new BeanInstantiationException(ctor.getDeclaringClass(), "Is it an abstract class?", ex);@b@ }@b@ catch (IllegalAccessException ex)@b@ {@b@ throw new BeanInstantiationException(ctor.getDeclaringClass(), "Is the constructor accessible?", ex);@b@ }@b@ catch (IllegalArgumentException ex)@b@ {@b@ throw new BeanInstantiationException(ctor.getDeclaringClass(), "Illegal arguments for constructor", ex);@b@ }@b@ catch (InvocationTargetException ex)@b@ {@b@ throw new BeanInstantiationException(ctor.getDeclaringClass(), "Constructor threw exception", ex@b@ .getTargetException());@b@ }@b@ }@b@@b@ public static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes)@b@ {@b@ try@b@ {@b@ return clazz.getMethod(methodName, paramTypes);@b@ } catch (NoSuchMethodException ex) {@b@ }@b@ return findDeclaredMethod(clazz, methodName, paramTypes);@b@ }@b@@b@ public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes)@b@ {@b@ try@b@ {@b@ return clazz.getDeclaredMethod(methodName, paramTypes);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ if (clazz.getSuperclass() != null)@b@ return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);@b@ }@b@ return null;@b@ }@b@@b@ public static Method findMethodWithMinimalParameters(Class<?> clazz, String methodName)@b@ throws IllegalArgumentException@b@ {@b@ Method targetMethod = findMethodWithMinimalParameters(clazz.getMethods(), methodName);@b@ if (targetMethod == null)@b@ targetMethod = findDeclaredMethodWithMinimalParameters(clazz, methodName);@b@@b@ return targetMethod;@b@ }@b@@b@ public static Method findDeclaredMethodWithMinimalParameters(Class<?> clazz, String methodName)@b@ throws IllegalArgumentException@b@ {@b@ Method targetMethod = findMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);@b@ if ((targetMethod == null) && (clazz.getSuperclass() != null))@b@ targetMethod = findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName);@b@@b@ return targetMethod;@b@ }@b@@b@ public static Method findMethodWithMinimalParameters(Method[] methods, String methodName)@b@ throws IllegalArgumentException@b@ {@b@ Method targetMethod = null;@b@ int numMethodsFoundWithCurrentMinimumArgs = 0;@b@ Method[] arrayOfMethod = methods; int i = arrayOfMethod.length; for (int j = 0; j < i; ++j) { Method method = arrayOfMethod[j];@b@ if (method.getName().equals(methodName)) {@b@ int numParams = method.getParameterTypes().length;@b@ if ((targetMethod == null) || (numParams < targetMethod.getParameterTypes().length)) {@b@ targetMethod = method;@b@ numMethodsFoundWithCurrentMinimumArgs = 1;@b@ }@b@ else if (targetMethod.getParameterTypes().length == numParams)@b@ {@b@ ++numMethodsFoundWithCurrentMinimumArgs;@b@ }@b@ }@b@ }@b@@b@ if (numMethodsFoundWithCurrentMinimumArgs > 1) {@b@ throw new IllegalArgumentException("Cannot resolve method '" + methodName + "' to a unique method. Attempted to resolve to overloaded method with " + "the least number of parameters, but there were " + numMethodsFoundWithCurrentMinimumArgs + " candidates.");@b@ }@b@@b@ return targetMethod;@b@ }@b@@b@ public static Method resolveSignature(String signature, Class<?> clazz)@b@ {@b@ Assert.hasText(signature, "'signature' must not be empty");@b@ Assert.notNull(clazz, "Class must not be null");@b@ int firstParen = signature.indexOf("(");@b@ int lastParen = signature.indexOf(")");@b@ if ((firstParen > -1) && (lastParen == -1)) {@b@ throw new IllegalArgumentException("Invalid method signature '" + signature + "': expected closing ')' for args list");@b@ }@b@@b@ if ((lastParen > -1) && (firstParen == -1)) {@b@ throw new IllegalArgumentException("Invalid method signature '" + signature + "': expected opening '(' for args list");@b@ }@b@@b@ if ((firstParen == -1) && (lastParen == -1)) {@b@ return findMethodWithMinimalParameters(clazz, signature);@b@ }@b@@b@ String methodName = signature.substring(0, firstParen);@b@@b@ String[] parameterTypeNames = StringUtils.commaDelimitedListToStringArray(signature@b@ .substring(firstParen + 1, lastParen));@b@@b@ Class[] parameterTypes = new Class[parameterTypeNames.length];@b@ for (int i = 0; i < parameterTypeNames.length; ++i) {@b@ String parameterTypeName = parameterTypeNames[i].trim();@b@ try {@b@ parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader());@b@ }@b@ catch (Throwable ex) {@b@ throw new IllegalArgumentException("Invalid method signature: unable to resolve type [" + parameterTypeName + "] for argument " + i + ". Root cause: " + ex);@b@ }@b@ }@b@@b@ return findMethod(clazz, methodName, parameterTypes);@b@ }@b@@b@ public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz)@b@ throws BeansException@b@ {@b@ CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);@b@ return cr.getPropertyDescriptors();@b@ }@b@@b@ public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName)@b@ throws BeansException@b@ {@b@ CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);@b@ return cr.getPropertyDescriptor(propertyName);@b@ }@b@@b@ public static PropertyDescriptor findPropertyForMethod(Method method)@b@ throws BeansException@b@ {@b@ return findPropertyForMethod(method, method.getDeclaringClass());@b@ }@b@@b@ public static PropertyDescriptor findPropertyForMethod(Method method, Class<?> clazz)@b@ throws BeansException@b@ {@b@ Assert.notNull(method, "Method must not be null");@b@ PropertyDescriptor[] pds = getPropertyDescriptors(clazz);@b@ PropertyDescriptor[] arrayOfPropertyDescriptor1 = pds; int i = arrayOfPropertyDescriptor1.length; for (int j = 0; j < i; ++j) { PropertyDescriptor pd = arrayOfPropertyDescriptor1[j];@b@ if ((method.equals(pd.getReadMethod())) || (method.equals(pd.getWriteMethod())))@b@ return pd;@b@ }@b@@b@ return null;@b@ }@b@@b@ public static PropertyEditor findEditorByConvention(Class<?> targetType)@b@ {@b@ if ((targetType == null) || (targetType.isArray()) || (unknownEditorTypes.contains(targetType)))@b@ return null;@b@@b@ ClassLoader cl = targetType.getClassLoader();@b@ if (cl == null)@b@ try {@b@ cl = ClassLoader.getSystemClassLoader();@b@ if (cl == null)@b@ return null;@b@@b@ }@b@ catch (Throwable ex)@b@ {@b@ if (logger.isDebugEnabled())@b@ logger.debug("Could not access system ClassLoader: " + ex);@b@@b@ return null;@b@ }@b@@b@ String editorName = targetType.getName() + "Editor";@b@ try {@b@ Class editorClass = cl.loadClass(editorName);@b@ if (!(PropertyEditor.class.isAssignableFrom(editorClass))) {@b@ if (logger.isWarnEnabled()) {@b@ logger.warn("Editor class [" + editorName + "] does not implement [java.beans.PropertyEditor] interface");@b@ }@b@@b@ unknownEditorTypes.add(targetType);@b@ return null;@b@ }@b@ return ((PropertyEditor)instantiateClass(editorClass));@b@ }@b@ catch (ClassNotFoundException ex) {@b@ if (logger.isDebugEnabled())@b@ logger.debug("No property editor [" + editorName + "] found for type " + targetType@b@ .getName() + " according to 'Editor' suffix convention");@b@@b@ unknownEditorTypes.add(targetType); }@b@ return null;@b@ }@b@@b@ public static Class<?> findPropertyType(String propertyName, Class<?>[] beanClasses)@b@ {@b@ Class[] arrayOfClass;@b@ int j;@b@ if (beanClasses != null) {@b@ arrayOfClass = beanClasses; int i = arrayOfClass.length; for (j = 0; j < i; ++j) { Class beanClass = arrayOfClass[j];@b@ PropertyDescriptor pd = getPropertyDescriptor(beanClass, propertyName);@b@ if (pd != null)@b@ return pd.getPropertyType();@b@ }@b@ }@b@@b@ return Object.class;@b@ }@b@@b@ public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd)@b@ {@b@ if (pd instanceof GenericTypeAwarePropertyDescriptor) {@b@ return new MethodParameter(((GenericTypeAwarePropertyDescriptor)pd).getWriteMethodParameter());@b@ }@b@@b@ return new MethodParameter(pd.getWriteMethod(), 0);@b@ }@b@@b@ public static boolean isSimpleProperty(Class<?> clazz)@b@ {@b@ Assert.notNull(clazz, "Class must not be null");@b@ return ((isSimpleValueType(clazz)) || ((clazz.isArray()) && (isSimpleValueType(clazz.getComponentType()))));@b@ }@b@@b@ public static boolean isSimpleValueType(Class<?> clazz)@b@ {@b@ return ((ClassUtils.isPrimitiveOrWrapper(clazz)) || (clazz.isEnum()) || @b@ (CharSequence.class@b@ .isAssignableFrom(clazz)) || @b@ (Number.class@b@ .isAssignableFrom(clazz)) || @b@ (Date.class@b@ .isAssignableFrom(clazz)) || @b@ (clazz@b@ .equals(URI.class)) || @b@ (clazz.equals(URL.class)) || @b@ (clazz@b@ .equals(Locale.class)) || @b@ (clazz.equals(Class.class)));@b@ }@b@@b@ public static void copyProperties(Object source, Object target)@b@ throws BeansException@b@ {@b@ copyProperties(source, target, null, (String[])null);@b@ }@b@@b@ public static void copyProperties(Object source, Object target, Class<?> editable)@b@ throws BeansException@b@ {@b@ copyProperties(source, target, editable, (String[])null);@b@ }@b@@b@ public static void copyProperties(Object source, Object target, String[] ignoreProperties)@b@ throws BeansException@b@ {@b@ copyProperties(source, target, null, ignoreProperties);@b@ }@b@@b@ private static void copyProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties)@b@ throws BeansException@b@ {@b@ Assert.notNull(source, "Source must not be null");@b@ Assert.notNull(target, "Target must not be null");@b@@b@ Class actualEditable = target.getClass();@b@ if (editable != null) {@b@ if (!(editable.isInstance(target)))@b@ {@b@ throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable@b@ .getName() + "]");@b@ }@b@ actualEditable = editable;@b@ }@b@ PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);@b@ List ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;@b@@b@ PropertyDescriptor[] arrayOfPropertyDescriptor1 = targetPds; int i = arrayOfPropertyDescriptor1.length; for (int j = 0; j < i; ++j) { PropertyDescriptor targetPd = arrayOfPropertyDescriptor1[j];@b@ Method writeMethod = targetPd.getWriteMethod();@b@ if ((writeMethod != null) && (((ignoreList == null) || (!(ignoreList.contains(targetPd.getName())))))) {@b@ PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());@b@ if (sourcePd != null) {@b@ Method readMethod = sourcePd.getReadMethod();@b@ if ((readMethod != null) && @b@ (ClassUtils.isAssignable(writeMethod@b@ .getParameterTypes()[0], readMethod.getReturnType())))@b@ try {@b@ if (!(Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())))@b@ readMethod.setAccessible(true);@b@@b@ Object value = readMethod.invoke(source, new Object[0]);@b@ if (!(Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())))@b@ writeMethod.setAccessible(true);@b@@b@ writeMethod.invoke(target, new Object[] { value });@b@ }@b@ catch (Throwable ex)@b@ {@b@ throw new FatalBeanException("Could not copy property '" + targetPd@b@ .getName() + "' from source to target", ex);@b@ }@b@ }@b@ }@b@ }@b@ }@b@}