All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.beans.BeanUtils Maven / Gradle / Ivy

There is a newer version: 5.3.34
Show newest version
/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;

import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Static convenience methods for JavaBeans: for instantiating beans,
 * checking bean property types, copying bean properties, etc.
 *
 * 

Mainly for use within the framework, but to some degree also * useful for application classes. * * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop */ public abstract class BeanUtils { /** * Convenience method to instantiate a class using its no-arg constructor. * As this method doesn't try to load classes by name, it should avoid * class-loading issues. *

Note that this method tries to set the constructor accessible * if given a non-accessible (that is, non-public) constructor. * @param clazz class to instantiate * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated */ public static Object instantiateClass(Class clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return instantiateClass(clazz.getDeclaredConstructor((Class[]) null), null); } catch (NoSuchMethodException ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } /** * Convenience method to instantiate a class using the given constructor. * As this method doesn't try to load classes by name, it should avoid * class-loading issues. *

Note that this method tries to set the constructor accessible * if given a non-accessible (that is, non-public) constructor. * @param ctor the constructor to instantiate * @param args the constructor arguments to apply * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated */ public static Object instantiateClass(Constructor ctor, Object[] args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); try { if (!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) { ctor.setAccessible(true); } return ctor.newInstance(args); } catch (InstantiationException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Has the class definition changed? Is the constructor accessible?", ex); } catch (IllegalArgumentException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Illegal arguments for constructor", ex); } catch (InvocationTargetException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Constructor threw exception", ex.getTargetException()); } } /** * Find a method with the given method name and the given parameter types, * declared on the given class or one of its superclasses. Prefers public methods, * but will return a protected, package access, or private method too. *

Checks Class.getMethod first, falling back to * findDeclaredMethod. This allows to find public methods * without issues even in environments with restricted Java security settings. * @param clazz the class to check * @param methodName the name of the method to find * @param paramTypes the parameter types of the method to find * @return the Method object, or null if not found * @see java.lang.Class#getMethod * @see #findDeclaredMethod */ public static Method findMethod(Class clazz, String methodName, Class[] paramTypes) { try { return clazz.getMethod(methodName, paramTypes); } catch (NoSuchMethodException ex) { return findDeclaredMethod(clazz, methodName, paramTypes); } } /** * Find a method with the given method name and the given parameter types, * declared on the given class or one of its superclasses. Will return a public, * protected, package access, or private method. *

Checks Class.getDeclaredMethod, cascading upwards to all superclasses. * @param clazz the class to check * @param methodName the name of the method to find * @param paramTypes the parameter types of the method to find * @return the Method object, or null if not found * @see java.lang.Class#getDeclaredMethod */ public static Method findDeclaredMethod(Class clazz, String methodName, Class[] paramTypes) { try { return clazz.getDeclaredMethod(methodName, paramTypes); } catch (NoSuchMethodException ex) { if (clazz.getSuperclass() != null) { return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes); } return null; } } /** * Find a method with the given method name and minimal parameters (best case: none), * declared on the given class or one of its superclasses. Prefers public methods, * but will return a protected, package access, or private method too. *

Checks Class.getMethods first, falling back to * findDeclaredMethodWithMinimalParameters. This allows to find public * methods without issues even in environments with restricted Java security settings. * @param clazz the class to check * @param methodName the name of the method to find * @return the Method object, or null if not found * @throws IllegalArgumentException if methods of the given name were found but * could not be resolved to a unique method with minimal parameters * @see java.lang.Class#getMethods * @see #findDeclaredMethodWithMinimalParameters */ public static Method findMethodWithMinimalParameters(Class clazz, String methodName) throws IllegalArgumentException { Method targetMethod = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName); if (targetMethod == null) { return findDeclaredMethodWithMinimalParameters(clazz, methodName); } return targetMethod; } /** * Find a method with the given method name and minimal parameters (best case: none), * declared on the given class or one of its superclasses. Will return a public, * protected, package access, or private method. *

Checks Class.getDeclaredMethods, cascading upwards to all superclasses. * @param clazz the class to check * @param methodName the name of the method to find * @return the Method object, or null if not found * @throws IllegalArgumentException if methods of the given name were found but * could not be resolved to a unique method with minimal parameters * @see java.lang.Class#getDeclaredMethods */ public static Method findDeclaredMethodWithMinimalParameters(Class clazz, String methodName) throws IllegalArgumentException { Method targetMethod = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName); if (targetMethod == null && clazz.getSuperclass() != null) { return findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName); } return targetMethod; } /** * Find a method with the given method name and minimal parameters (best case: none) * in the given list of methods. * @param methods the methods to check * @param methodName the name of the method to find * @return the Method object, or null if not found * @throws IllegalArgumentException if methods of the given name were found but * could not be resolved to a unique method with minimal parameters */ private static Method doFindMethodWithMinimalParameters(Method[] methods, String methodName) throws IllegalArgumentException { Method targetMethod = null; int numMethodsFoundWithCurrentMinimumArgs = 0; for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(methodName)) { int numParams = methods[i].getParameterTypes().length; if (targetMethod == null || numParams < targetMethod.getParameterTypes().length) { targetMethod = methods[i]; numMethodsFoundWithCurrentMinimumArgs = 1; } else { if (targetMethod.getParameterTypes().length == numParams) { // Additional candidate with same length. numMethodsFoundWithCurrentMinimumArgs++; } } } } if (numMethodsFoundWithCurrentMinimumArgs > 1) { 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."); } return targetMethod; } /** * Parse a method signature in the form methodName[([arg_list])], * where arg_list is an optional, comma-separated list of fully-qualified * type names, and attempts to resolve that signature against the supplied Class. *

When not supplying an argument list (methodName) the method whose name * matches and has the least number of parameters will be returned. When supplying an * argument type list, only the method whose name and argument types match will be returned. *

Note then that methodName and methodName() are not * resolved in the same way. The signature methodName means the method called * methodName with the least number of arguments, whereas methodName() * means the method called methodName with exactly 0 arguments. *

If no method can be found, then null is returned. * @param signature the method signature as String representation * @param clazz the class to resolve the method signature against * @return the resolved Method * @see #findMethod * @see #findMethodWithMinimalParameters */ public static Method resolveSignature(String signature, Class clazz) { Assert.hasText(signature, "'signature' must not be empty"); Assert.notNull(clazz, "Class must not be null"); int firstParen = signature.indexOf("("); int lastParen = signature.indexOf(")"); if (firstParen > -1 && lastParen == -1) { throw new IllegalArgumentException("Invalid method signature '" + signature + "': expected closing ')' for args list"); } else if (lastParen > -1 && firstParen == -1) { throw new IllegalArgumentException("Invalid method signature '" + signature + "': expected opening '(' for args list"); } else if (firstParen == -1 && lastParen == -1) { return findMethodWithMinimalParameters(clazz, signature); } else { String methodName = signature.substring(0, firstParen); String[] parameterTypeNames = StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen)); Class[] parameterTypes = new Class[parameterTypeNames.length]; for (int i = 0; i < parameterTypeNames.length; i++) { String parameterTypeName = parameterTypeNames[i].trim(); try { parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader()); } catch (Throwable ex) { throw new IllegalArgumentException("Invalid method signature: unable to resolve type [" + parameterTypeName + "] for argument " + i + ". Root cause: " + ex); } } return findMethod(clazz, methodName, parameterTypes); } } /** * Retrieve the JavaBeans PropertyDescriptors of a given class. * @param clazz the Class to retrieve the PropertyDescriptors for * @return an array of PropertyDescriptors for the given class * @throws BeansException if PropertyDescriptor look fails */ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws BeansException { CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getBeanInfo().getPropertyDescriptors(); } /** * Retrieve the JavaBeans PropertyDescriptors for the given property. * @param clazz the Class to retrieve the PropertyDescriptor for * @param propertyName the name of the property * @return the corresponding PropertyDescriptor, or null if none * @throws BeansException if PropertyDescriptor lookup fails */ public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) throws BeansException { CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptor(propertyName); } /** * Find a JavaBeans PropertyDescriptor for the given method, * with the method either being the read method or the write method for * that bean property. * @param method the method to find a corresponding PropertyDescriptor for * @return the corresponding PropertyDescriptor, or null if none * @throws BeansException if PropertyDescriptor lookup fails */ public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException { Assert.notNull(method, "Method must not be null"); PropertyDescriptor[] pds = getPropertyDescriptors(method.getDeclaringClass()); for (int i = 0; i < pds.length; i++) { if (method.equals(pds[i].getReadMethod()) || method.equals(pds[i].getWriteMethod())) { return pds[i]; } } return null; } /** * Determine the bean property type for the given property from the * given classes/interfaces, if possible. * @param propertyName the name of the bean property * @param beanClasses the classes to check against * @return the property type, or Object.class as fallback */ public static Class findPropertyType(String propertyName, Class[] beanClasses) { if (beanClasses != null) { for (int i = 0; i < beanClasses.length; i++) { PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(beanClasses[i], propertyName); if (pd != null) { return pd.getPropertyType(); } } } return Object.class; } /** * Check if the given type represents a "simple" property: * a primitive, a String, a Class, or a corresponding array. *

Used to determine properties to check for a "simple" dependency-check. * @param clazz the type to check * @return whether the given type represent a "simple" property * @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE * @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies */ public static boolean isSimpleProperty(Class clazz) { Assert.notNull(clazz, "Class must not be null"); return clazz.isPrimitive() || ClassUtils.isPrimitiveArray(clazz) || ClassUtils.isPrimitiveWrapper(clazz) || ClassUtils.isPrimitiveWrapperArray(clazz) || clazz.equals(String.class) || clazz.equals(String[].class) || clazz.equals(Class.class) || clazz.equals(Class[].class); } /** * Determine if the given target type is assignable from the given value * type, assuming setting by reflection. Considers primitive wrapper * classes as assignable to the corresponding primitive types. * @param targetType the target type * @param valueType the value type that should be assigned to the target type * @return if the target type is assignable from the value type * @deprecated as of Spring 2.0, in favor of ClassUtils.isAssignable * @see org.springframework.util.ClassUtils#isAssignable(Class, Class) */ public static boolean isAssignable(Class targetType, Class valueType) { return ClassUtils.isAssignable(targetType, valueType); } /** * Determine if the given type is assignable from the given value, * assuming setting by reflection. Considers primitive wrapper classes * as assignable to the corresponding primitive types. * @param type the target type * @param value the value that should be assigned to the type * @return if the type is assignable from the value * @deprecated as of Spring 2.0, in favor of ClassUtils.isAssignableValue * @see org.springframework.util.ClassUtils#isAssignableValue(Class, Object) */ public static boolean isAssignable(Class type, Object value) { return ClassUtils.isAssignableValue(type, value); } /** * Copy the property values of the given source bean into the target bean. *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. *

This is just a convenience method. For more complex transfer needs, * consider using a full BeanWrapper. * @param source the source bean * @param target the target bean * @throws BeansException if the copying failed * @see BeanWrapper */ public static void copyProperties(Object source, Object target) throws BeansException { copyProperties(source, target, null, null); } /** * Copy the property values of the given source bean into the given target bean, * only setting properties defined in the given "editable" class (or interface). *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. *

This is just a convenience method. For more complex transfer needs, * consider using a full BeanWrapper. * @param source the source bean * @param target the target bean * @param editable the class (or interface) to restrict property setting to * @throws BeansException if the copying failed * @see BeanWrapper */ public static void copyProperties(Object source, Object target, Class editable) throws BeansException { copyProperties(source, target, editable, null); } /** * Copy the property values of the given source bean into the given target bean, * ignoring the given "ignoreProperties". *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. *

This is just a convenience method. For more complex transfer needs, * consider using a full BeanWrapper. * @param source the source bean * @param target the target bean * @param ignoreProperties array of property names to ignore * @throws BeansException if the copying failed * @see BeanWrapper */ public static void copyProperties(Object source, Object target, String[] ignoreProperties) throws BeansException { copyProperties(source, target, null, ignoreProperties); } /** * Copy the property values of the given source bean into the given target bean. *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. * @param source the source bean * @param target the target bean * @param editable the class (or interface) to restrict property setting to * @param ignoreProperties array of property names to ignore * @throws BeansException if the copying failed * @see BeanWrapper */ private static void copyProperties(Object source, Object target, Class editable, String[] ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null; for (int i = 0; i < targetPds.length; i++) { PropertyDescriptor targetPd = targetPds[i]; if (targetPd.getWriteMethod() != null && (ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null && sourcePd.getReadMethod() != null) { try { Method readMethod = sourcePd.getReadMethod(); if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source, new Object[0]); Method writeMethod = targetPd.getWriteMethod(); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, new Object[] {value}); } catch (Throwable ex) { throw new FatalBeanException("Could not copy properties from source to target", ex); } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy