/*
* Copyright 2002-2020 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
*
* https://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.ConstructorProperties;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
import java.time.temporal.Temporal;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.full.KClasses;
import kotlin.reflect.jvm.KCallablesJvm;
import kotlin.reflect.jvm.ReflectJvmMapping;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
Static convenience methods for JavaBeans: for instantiating beans,
checking bean property types, copying bean properties, etc.
Mainly for internal use within the framework, but to some degree also
useful for application classes. Consider
Apache Commons BeanUtils,
BULL - Bean Utils Light Library,
or similar third-party frameworks for more comprehensive bean utilities.
Author: Rod Johnson, Juergen Hoeller, Rob Harrop, Sam Brannen, Sebastien Deleuze
/**
* Static convenience methods for JavaBeans: for instantiating beans,
* checking bean property types, copying bean properties, etc.
*
* <p>Mainly for internal use within the framework, but to some degree also
* useful for application classes. Consider
* <a href="https://commons.apache.org/proper/commons-beanutils/">Apache Commons BeanUtils</a>,
* <a href="https://hotelsdotcom.github.io/bull/">BULL - Bean Utils Light Library</a>,
* or similar third-party frameworks for more comprehensive bean utilities.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
* @author Sebastien Deleuze
*/
public abstract class BeanUtils {
private static final Log logger = LogFactory.getLog(BeanUtils.class);
private static final ParameterNameDiscoverer parameterNameDiscoverer =
new DefaultParameterNameDiscoverer();
private static final Set<Class<?>> unknownEditorTypes =
Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64));
private static final Map<Class<?>, Object> DEFAULT_TYPE_VALUES;
static {
Map<Class<?>, Object> values = new HashMap<>();
values.put(boolean.class, false);
values.put(byte.class, (byte) 0);
values.put(short.class, (short) 0);
values.put(int.class, 0);
values.put(long.class, (long) 0);
DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
}
Convenience method to instantiate a class using its no-arg constructor.
Params: - clazz – class to instantiate
Throws: - BeanInstantiationException – if the bean cannot be instantiated
See Also: Returns: the new instance Deprecated: as of Spring 5.0, following the deprecation of Class.newInstance()
in JDK 9
/**
* Convenience method to instantiate a class using its no-arg constructor.
* @param clazz class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
* @deprecated as of Spring 5.0, following the deprecation of
* {@link Class#newInstance()} in JDK 9
* @see Class#newInstance()
*/
@Deprecated
public static <T> T instantiate(Class<T> 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 clazz.newInstance();
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex);
}
}
Instantiate a class using its 'primary' constructor (for Kotlin classes,
potentially having default arguments declared) or its default constructor
(for regular Java classes, expecting a standard no-arg setup).
Note that this method tries to set the constructor accessible
if given a non-accessible (that is, non-public) constructor.
Params: - clazz – the class to instantiate
Throws: - BeanInstantiationException – if the bean cannot be instantiated. The cause may notably indicate a
NoSuchMethodException
if no primary/default constructor was found, a NoClassDefFoundError
or other LinkageError
in case of an unresolvable class definition (e.g. due to a missing dependency at runtime), or an exception thrown from the constructor invocation itself.
See Also: Returns: the new instance
/**
* Instantiate a class using its 'primary' constructor (for Kotlin classes,
* potentially having default arguments declared) or its default constructor
* (for regular Java classes, expecting a standard no-arg setup).
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz the class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated.
* The cause may notably indicate a {@link NoSuchMethodException} if no
* primary/default constructor was found, a {@link NoClassDefFoundError}
* or other {@link LinkageError} in case of an unresolvable class definition
* (e.g. due to a missing dependency at runtime), or an exception thrown
* from the constructor invocation itself.
* @see Constructor#newInstance
*/
public static <T> T instantiateClass(Class<T> 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());
}
catch (NoSuchMethodException ex) {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor != null) {
return instantiateClass(ctor);
}
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
catch (LinkageError err) {
throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
}
}
Instantiate a class using its no-arg constructor and return the new instance
as the specified assignable type.
Useful in cases where the type of the class to instantiate (clazz) is not
available, but the type desired (assignableTo) is known.
Note that this method tries to set the constructor accessible if given a
non-accessible (that is, non-public) constructor.
Params: - clazz – class to instantiate
- assignableTo – type that clazz must be assignableTo
Throws: - BeanInstantiationException – if the bean cannot be instantiated
See Also: Returns: the new instance
/**
* Instantiate a class using its no-arg constructor and return the new instance
* as the specified assignable type.
* <p>Useful in cases where the type of the class to instantiate (clazz) is not
* available, but the type desired (assignableTo) is known.
* <p>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
* @param assignableTo type that clazz must be assignableTo
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
* @see Constructor#newInstance
*/
@SuppressWarnings("unchecked")
public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo) throws BeanInstantiationException {
Assert.isAssignable(assignableTo, clazz);
return (T) instantiateClass(clazz);
}
Convenience method to instantiate a class using the given constructor.
Note that this method tries to set the constructor accessible if given a
non-accessible (that is, non-public) constructor, and supports Kotlin classes
with optional parameters and default values.
Params: - ctor – the constructor to instantiate
- args – the constructor arguments to apply (use
null
for an unspecified parameter, Kotlin optional parameters and Java primitive types are supported)
Throws: - BeanInstantiationException – if the bean cannot be instantiated
See Also: Returns: the new instance
/**
* Convenience method to instantiate a class using the given constructor.
* <p>Note that this method tries to set the constructor accessible if given a
* non-accessible (that is, non-public) constructor, and supports Kotlin classes
* with optional parameters and default values.
* @param ctor the constructor to instantiate
* @param args the constructor arguments to apply (use {@code null} for an unspecified
* parameter, Kotlin optional parameters and Java primitive types are supported)
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
* @see Constructor#newInstance
*/
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
Class<?>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
return ctor.newInstance(argsWithDefaultValues);
}
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
Return a resolvable constructor for the provided class, either a primary constructor
or single public constructor or simply a default constructor. Callers have to be
prepared to resolve arguments for the returned constructor's parameters, if any.
Params: - clazz – the class to check
See Also: Since: 5.3
/**
* Return a resolvable constructor for the provided class, either a primary constructor
* or single public constructor or simply a default constructor. Callers have to be
* prepared to resolve arguments for the returned constructor's parameters, if any.
* @param clazz the class to check
* @since 5.3
* @see #findPrimaryConstructor
*/
@SuppressWarnings("unchecked")
public static <T> Constructor<T> getResolvableConstructor(Class<T> clazz) {
Constructor<T> ctor = findPrimaryConstructor(clazz);
if (ctor == null) {
Constructor<?>[] ctors = clazz.getConstructors();
if (ctors.length == 1) {
ctor = (Constructor<T>) ctors[0];
}
else {
try {
ctor = clazz.getDeclaredConstructor();
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("No primary or single public constructor found for " +
clazz + " - and no default constructor found either");
}
}
}
return ctor;
}
Return the primary constructor of the provided class. For Kotlin classes, this returns the Java constructor corresponding to the Kotlin primary constructor (as defined in the Kotlin specification). Otherwise, in particular for non-Kotlin classes, this simply returns null
. Params: - clazz – the class to check
See Also: Since: 5.0
/**
* Return the primary constructor of the provided class. For Kotlin classes, this
* returns the Java constructor corresponding to the Kotlin primary constructor
* (as defined in the Kotlin specification). Otherwise, in particular for non-Kotlin
* classes, this simply returns {@code null}.
* @param clazz the class to check
* @since 5.0
* @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">Kotlin docs</a>
*/
@Nullable
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
Assert.notNull(clazz, "Class must not be null");
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) {
Constructor<T> kotlinPrimaryConstructor = KotlinDelegate.findPrimaryConstructor(clazz);
if (kotlinPrimaryConstructor != null) {
return kotlinPrimaryConstructor;
}
}
return null;
}
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.
Params: - clazz – the class to check
- methodName – the name of the method to find
- paramTypes – the parameter types of the method to find
See Also: Returns: the Method object, or null
if not found
/**
* 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.
* <p>Checks {@code Class.getMethod} first, falling back to
* {@code 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 {@code null} if not found
* @see Class#getMethod
* @see #findDeclaredMethod
*/
@Nullable
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.
Params: - clazz – the class to check
- methodName – the name of the method to find
- paramTypes – the parameter types of the method to find
See Also: Returns: the Method object, or null
if not found
/**
* 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.
* <p>Checks {@code 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 {@code null} if not found
* @see Class#getDeclaredMethod
*/
@Nullable
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 for finding public methods without issues even in environments with restricted Java security settings.
Params: - clazz – the class to check
- methodName – the name of the method to find
Throws: - IllegalArgumentException – if methods of the given name were found but
could not be resolved to a unique method with minimal parameters
See Also: Returns: the Method object, or null
if not found
/**
* 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.
* <p>Checks {@code Class.getMethods} first, falling back to
* {@code findDeclaredMethodWithMinimalParameters}. This allows for finding 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 {@code 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 Class#getMethods
* @see #findDeclaredMethodWithMinimalParameters
*/
@Nullable
public static Method findMethodWithMinimalParameters(Class<?> clazz, String methodName)
throws IllegalArgumentException {
Method targetMethod = findMethodWithMinimalParameters(clazz.getMethods(), methodName);
if (targetMethod == null) {
targetMethod = 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.
Params: - clazz – the class to check
- methodName – the name of the method to find
Throws: - IllegalArgumentException – if methods of the given name were found but
could not be resolved to a unique method with minimal parameters
See Also: Returns: the Method object, or null
if not found
/**
* 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.
* <p>Checks {@code 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 {@code 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 Class#getDeclaredMethods
*/
@Nullable
public static Method findDeclaredMethodWithMinimalParameters(Class<?> clazz, String methodName)
throws IllegalArgumentException {
Method targetMethod = findMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
if (targetMethod == null && clazz.getSuperclass() != null) {
targetMethod = 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.
Params: - methods – the methods to check
- methodName – the name of the method to find
Throws: - IllegalArgumentException – if methods of the given name were found but
could not be resolved to a unique method with minimal parameters
Returns: the Method object, or null
if not found
/**
* 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 {@code 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
*/
@Nullable
public static Method findMethodWithMinimalParameters(Method[] methods, String methodName)
throws IllegalArgumentException {
Method targetMethod = null;
int numMethodsFoundWithCurrentMinimumArgs = 0;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
int numParams = method.getParameterCount();
if (targetMethod == null || numParams < targetMethod.getParameterCount()) {
targetMethod = method;
numMethodsFoundWithCurrentMinimumArgs = 1;
}
else if (!method.isBridge() && targetMethod.getParameterCount() == numParams) {
if (targetMethod.isBridge()) {
// Prefer regular method over bridge...
targetMethod = method;
}
else {
// 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.
Params: - signature – the method signature as String representation
- clazz – the class to resolve the method signature against
See Also: Returns: the resolved Method
/**
* Parse a method signature in the form {@code methodName[([arg_list])]},
* where {@code arg_list} is an optional, comma-separated list of fully-qualified
* type names, and attempts to resolve that signature against the supplied {@code Class}.
* <p>When not supplying an argument list ({@code 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.
* <p>Note then that {@code methodName} and {@code methodName()} are <strong>not</strong>
* resolved in the same way. The signature {@code methodName} means the method called
* {@code methodName} with the least number of arguments, whereas {@code methodName()}
* means the method called {@code methodName} with exactly 0 arguments.
* <p>If no method can be found, then {@code 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
*/
@Nullable
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 startParen = signature.indexOf('(');
int endParen = signature.indexOf(')');
if (startParen > -1 && endParen == -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected closing ')' for args list");
}
else if (startParen == -1 && endParen > -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected opening '(' for args list");
}
else if (startParen == -1) {
return findMethodWithMinimalParameters(clazz, signature);
}
else {
String methodName = signature.substring(0, startParen);
String[] parameterTypeNames =
StringUtils.commaDelimitedListToStringArray(signature.substring(startParen + 1, endParen));
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 PropertyDescriptor
s of a given class. Params: - clazz – the Class to retrieve the PropertyDescriptors for
Throws: - BeansException – if PropertyDescriptor look fails
Returns: an array of PropertyDescriptors
for the given class
/**
* Retrieve the JavaBeans {@code PropertyDescriptor}s of a given class.
* @param clazz the Class to retrieve the PropertyDescriptors for
* @return an array of {@code PropertyDescriptors} for the given class
* @throws BeansException if PropertyDescriptor look fails
*/
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptors();
}
Retrieve the JavaBeans PropertyDescriptors
for the given property. Params: - clazz – the Class to retrieve the PropertyDescriptor for
- propertyName – the name of the property
Throws: - BeansException – if PropertyDescriptor lookup fails
Returns: the corresponding PropertyDescriptor, or null
if none
/**
* Retrieve the JavaBeans {@code 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 {@code null} if none
* @throws BeansException if PropertyDescriptor lookup fails
*/
@Nullable
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) throws BeansException {
return CachedIntrospectionResults.forClass(clazz).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. Params: - method – the method to find a corresponding PropertyDescriptor for,
introspecting its declaring class
Throws: - BeansException – if PropertyDescriptor lookup fails
Returns: the corresponding PropertyDescriptor, or null
if none
/**
* Find a JavaBeans {@code 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,
* introspecting its declaring class
* @return the corresponding PropertyDescriptor, or {@code null} if none
* @throws BeansException if PropertyDescriptor lookup fails
*/
@Nullable
public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException {
return findPropertyForMethod(method, method.getDeclaringClass());
}
Find a JavaBeans PropertyDescriptor
for the given method, with the method either being the read method or the write method for that bean property. Params: - method – the method to find a corresponding PropertyDescriptor for
- clazz – the (most specific) class to introspect for descriptors
Throws: - BeansException – if PropertyDescriptor lookup fails
Returns: the corresponding PropertyDescriptor, or null
if none Since: 3.2.13
/**
* Find a JavaBeans {@code 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
* @param clazz the (most specific) class to introspect for descriptors
* @return the corresponding PropertyDescriptor, or {@code null} if none
* @throws BeansException if PropertyDescriptor lookup fails
* @since 3.2.13
*/
@Nullable
public static PropertyDescriptor findPropertyForMethod(Method method, Class<?> clazz) throws BeansException {
Assert.notNull(method, "Method must not be null");
PropertyDescriptor[] pds = getPropertyDescriptors(clazz);
for (PropertyDescriptor pd : pds) {
if (method.equals(pd.getReadMethod()) || method.equals(pd.getWriteMethod())) {
return pd;
}
}
return null;
}
Find a JavaBeans PropertyEditor following the 'Editor' suffix convention
(e.g. "mypackage.MyDomainClass" -> "mypackage.MyDomainClassEditor").
Compatible to the standard JavaBeans convention as implemented by PropertyEditorManager
but isolated from the latter's registered default editors for primitive types.
Params: - targetType – the type to find an editor for
Returns: the corresponding editor, or null
if none found
/**
* Find a JavaBeans PropertyEditor following the 'Editor' suffix convention
* (e.g. "mypackage.MyDomainClass" -> "mypackage.MyDomainClassEditor").
* <p>Compatible to the standard JavaBeans convention as implemented by
* {@link java.beans.PropertyEditorManager} but isolated from the latter's
* registered default editors for primitive types.
* @param targetType the type to find an editor for
* @return the corresponding editor, or {@code null} if none found
*/
@Nullable
public static PropertyEditor findEditorByConvention(@Nullable Class<?> targetType) {
if (targetType == null || targetType.isArray() || unknownEditorTypes.contains(targetType)) {
return null;
}
ClassLoader cl = targetType.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getSystemClassLoader();
if (cl == null) {
return null;
}
}
catch (Throwable ex) {
// e.g. AccessControlException on Google App Engine
if (logger.isDebugEnabled()) {
logger.debug("Could not access system ClassLoader: " + ex);
}
return null;
}
}
String targetTypeName = targetType.getName();
String editorName = targetTypeName + "Editor";
try {
Class<?> editorClass = cl.loadClass(editorName);
if (editorClass != null) {
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
if (logger.isInfoEnabled()) {
logger.info("Editor class [" + editorName +
"] does not implement [java.beans.PropertyEditor] interface");
}
unknownEditorTypes.add(targetType);
return null;
}
return (PropertyEditor) instantiateClass(editorClass);
}
// Misbehaving ClassLoader returned null instead of ClassNotFoundException
// - fall back to unknown editor type registration below
}
catch (ClassNotFoundException ex) {
// Ignore - fall back to unknown editor type registration below
}
if (logger.isTraceEnabled()) {
logger.trace("No property editor [" + editorName + "] found for type " +
targetTypeName + " according to 'Editor' suffix convention");
}
unknownEditorTypes.add(targetType);
return null;
}
Determine the bean property type for the given property from the
given classes/interfaces, if possible.
Params: - propertyName – the name of the bean property
- beanClasses – the classes to check against
Returns: the property type, or Object.class
as fallback
/**
* 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 {@code Object.class} as fallback
*/
public static Class<?> findPropertyType(String propertyName, @Nullable Class<?>... beanClasses) {
if (beanClasses != null) {
for (Class<?> beanClass : beanClasses) {
PropertyDescriptor pd = getPropertyDescriptor(beanClass, propertyName);
if (pd != null) {
return pd.getPropertyType();
}
}
}
return Object.class;
}
Obtain a new MethodParameter object for the write method of the
specified property.
Params: - pd – the PropertyDescriptor for the property
Returns: a corresponding MethodParameter object
/**
* Obtain a new MethodParameter object for the write method of the
* specified property.
* @param pd the PropertyDescriptor for the property
* @return a corresponding MethodParameter object
*/
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
if (pd instanceof GenericTypeAwarePropertyDescriptor) {
return new MethodParameter(((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodParameter());
}
else {
Method writeMethod = pd.getWriteMethod();
Assert.state(writeMethod != null, "No write method available");
return new MethodParameter(writeMethod, 0);
}
}
Determine required parameter names for the given constructor, considering the JavaBeans ConstructorProperties
annotation as well as Spring's DefaultParameterNameDiscoverer
. Params: - ctor – the constructor to find parameter names for
Throws: - IllegalStateException – if the parameter names are not resolvable
See Also: Returns: the parameter names (matching the constructor's parameter count) Since: 5.3
/**
* Determine required parameter names for the given constructor,
* considering the JavaBeans {@link ConstructorProperties} annotation
* as well as Spring's {@link DefaultParameterNameDiscoverer}.
* @param ctor the constructor to find parameter names for
* @return the parameter names (matching the constructor's parameter count)
* @throws IllegalStateException if the parameter names are not resolvable
* @since 5.3
* @see ConstructorProperties
* @see DefaultParameterNameDiscoverer
*/
public static String[] getParameterNames(Constructor<?> ctor) {
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
Assert.state(paramNames.length == ctor.getParameterCount(),
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
return paramNames;
}
Check if the given type represents a "simple" property: a simple value
type or an array of simple value types.
See isSimpleValueType(Class<?>)
for the definition of simple
value type.
Used to determine properties to check for a "simple" dependency-check.
Params: - type – the type to check
See Also: Returns: whether the given type represents a "simple" property
/**
* Check if the given type represents a "simple" property: a simple value
* type or an array of simple value types.
* <p>See {@link #isSimpleValueType(Class)} for the definition of <em>simple
* value type</em>.
* <p>Used to determine properties to check for a "simple" dependency-check.
* @param type the type to check
* @return whether the given type represents a "simple" property
* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
* @see #isSimpleValueType(Class)
*/
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
Check if the given type represents a "simple" value type: a primitive or
primitive wrapper, an enum, a String or other CharSequence, a Number, a
Date, a Temporal, a URI, a URL, a Locale, or a Class.
Void
and void
are not considered simple value types.
Params: - type – the type to check
See Also: Returns: whether the given type represents a "simple" value type
/**
* Check if the given type represents a "simple" value type: a primitive or
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
* <p>{@code Void} and {@code void} are not considered simple value types.
* @param type the type to check
* @return whether the given type represents a "simple" value type
* @see #isSimpleProperty(Class)
*/
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
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.
Params: - source – the source bean
- target – the target bean
Throws: - BeansException – if the copying failed
See Also:
/**
* Copy the property values of the given source bean into the target bean.
* <p>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.
* <p>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, (String[]) 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.
Params: - source – the source bean
- target – the target bean
- editable – the class (or interface) to restrict property setting to
Throws: - BeansException – if the copying failed
See Also:
/**
* 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).
* <p>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.
* <p>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, (String[]) 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.
Params: - source – the source bean
- target – the target bean
- ignoreProperties – array of property names to ignore
Throws: - BeansException – if the copying failed
See Also:
/**
* Copy the property values of the given source bean into the given target bean,
* ignoring the given "ignoreProperties".
* <p>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.
* <p>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.
As of Spring Framework 5.3, this method honors generic type information
when matching properties in the source and target objects.
Params: - source – the source bean
- target – the target bean
- editable – the class (or interface) to restrict property setting to
- ignoreProperties – array of property names to ignore
Throws: - BeansException – if the copying failed
See Also:
/**
* Copy the property values of the given source bean into the given target bean.
* <p>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.
* <p>As of Spring Framework 5.3, this method honors generic type information
* when matching properties in the source and target objects.
* @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, @Nullable Class<?> editable,
@Nullable 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<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
if (targetResolvableType.isAssignableFrom(sourceResolvableType)) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
}
Inner class to avoid a hard dependency on Kotlin at runtime.
/**
* Inner class to avoid a hard dependency on Kotlin at runtime.
*/
private static class KotlinDelegate {
Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
Params: - clazz – the
Class
of the Kotlin class
See Also:
/**
* Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
* @param clazz the {@link Class} of the Kotlin class
* @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">
* https://kotlinlang.org/docs/reference/classes.html#constructors</a>
*/
@Nullable
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
try {
KFunction<T> primaryCtor = KClasses.getPrimaryConstructor(JvmClassMappingKt.getKotlinClass(clazz));
if (primaryCtor == null) {
return null;
}
Constructor<T> constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
if (constructor == null) {
throw new IllegalStateException(
"Failed to find Java constructor for Kotlin primary constructor: " + clazz.getName());
}
return constructor;
}
catch (UnsupportedOperationException ex) {
return null;
}
}
Instantiate a Kotlin class using the provided constructor.
Params: - ctor – the constructor of the Kotlin class to instantiate
- args – the constructor arguments to apply (use
null
for unspecified parameter if needed)
/**
* Instantiate a Kotlin class using the provided constructor.
* @param ctor the constructor of the Kotlin class to instantiate
* @param args the constructor arguments to apply
* (use {@code null} for unspecified parameter if needed)
*/
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
KFunction<T> kotlinConstructor = ReflectJvmMapping.getKotlinFunction(ctor);
if (kotlinConstructor == null) {
return ctor.newInstance(args);
}
if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))) {
KCallablesJvm.setAccessible(kotlinConstructor, true);
}
List<KParameter> parameters = kotlinConstructor.getParameters();
Map<KParameter, Object> argParameters = CollectionUtils.newHashMap(parameters.size());
Assert.isTrue(args.length <= parameters.size(),
"Number of provided arguments should be less of equals than number of constructor parameters");
for (int i = 0 ; i < args.length ; i++) {
if (!(parameters.get(i).isOptional() && args[i] == null)) {
argParameters.put(parameters.get(i), args[i]);
}
}
return kotlinConstructor.callBy(argParameters);
}
}
}