/*
 * 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.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.lang.Nullable;

Simple utility class for working with the reflection API and handling reflection exceptions.

Only intended for internal use.

Author:Juergen Hoeller, Rob Harrop, Rod Johnson, Costin Leau, Sam Brannen, Chris Beams
Since:1.2.2
/** * Simple utility class for working with the reflection API and handling * reflection exceptions. * * <p>Only intended for internal use. * * @author Juergen Hoeller * @author Rob Harrop * @author Rod Johnson * @author Costin Leau * @author Sam Brannen * @author Chris Beams * @since 1.2.2 */
public abstract class ReflectionUtils {
Pre-built MethodFilter that matches all non-bridge non-synthetic methods which are not declared on java.lang.Object.
Since:3.0.5
/** * Pre-built MethodFilter that matches all non-bridge non-synthetic methods * which are not declared on {@code java.lang.Object}. * @since 3.0.5 */
public static final MethodFilter USER_DECLARED_METHODS = (method -> !method.isBridge() && !method.isSynthetic());
Pre-built FieldFilter that matches all non-static, non-final fields.
/** * Pre-built FieldFilter that matches all non-static, non-final fields. */
public static final FieldFilter COPYABLE_FIELDS = (field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())));
Naming prefix for CGLIB-renamed methods.
See Also:
  • isCglibRenamedMethod
/** * Naming prefix for CGLIB-renamed methods. * @see #isCglibRenamedMethod */
private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$"; private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; private static final Method[] EMPTY_METHOD_ARRAY = new Method[0]; private static final Field[] EMPTY_FIELD_ARRAY = new Field[0]; private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
Cache for Class.getDeclaredMethods() plus equivalent default methods from Java 8 based interfaces, allowing for fast iteration.
/** * Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods * from Java 8 based interfaces, allowing for fast iteration. */
private static final Map<Class<?>, Method[]> declaredMethodsCache = new ConcurrentReferenceHashMap<>(256);
Cache for Class.getDeclaredFields(), allowing for fast iteration.
/** * Cache for {@link Class#getDeclaredFields()}, allowing for fast iteration. */
private static final Map<Class<?>, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<>(256); // Exception handling
Handle the given reflection exception.

Should only be called if no checked exception is expected to be thrown by a target method, or if an error occurs while accessing a method or field.

Throws the underlying RuntimeException or Error in case of an InvocationTargetException with such a root cause. Throws an IllegalStateException with an appropriate message or UndeclaredThrowableException otherwise.

Params:
  • ex – the reflection exception to handle
/** * Handle the given reflection exception. * <p>Should only be called if no checked exception is expected to be thrown * by a target method, or if an error occurs while accessing a method or field. * <p>Throws the underlying RuntimeException or Error in case of an * InvocationTargetException with such a root cause. Throws an * IllegalStateException with an appropriate message or * UndeclaredThrowableException otherwise. * @param ex the reflection exception to handle */
public static void handleReflectionException(Exception ex) { if (ex instanceof NoSuchMethodException) { throw new IllegalStateException("Method not found: " + ex.getMessage()); } if (ex instanceof IllegalAccessException) { throw new IllegalStateException("Could not access method or field: " + ex.getMessage()); } if (ex instanceof InvocationTargetException) { handleInvocationTargetException((InvocationTargetException) ex); } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new UndeclaredThrowableException(ex); }
Handle the given invocation target exception. Should only be called if no checked exception is expected to be thrown by the target method.

Throws the underlying RuntimeException or Error in case of such a root cause. Throws an UndeclaredThrowableException otherwise.

Params:
  • ex – the invocation target exception to handle
/** * Handle the given invocation target exception. Should only be called if no * checked exception is expected to be thrown by the target method. * <p>Throws the underlying RuntimeException or Error in case of such a root * cause. Throws an UndeclaredThrowableException otherwise. * @param ex the invocation target exception to handle */
public static void handleInvocationTargetException(InvocationTargetException ex) { rethrowRuntimeException(ex.getTargetException()); }
Rethrow the given exception, which is presumably the target exception of an InvocationTargetException. Should only be called if no checked exception is expected to be thrown by the target method.

Rethrows the underlying exception cast to a RuntimeException or Error if appropriate; otherwise, throws an UndeclaredThrowableException.

Params:
  • ex – the exception to rethrow
Throws:
/** * Rethrow the given {@link Throwable exception}, which is presumably the * <em>target exception</em> of an {@link InvocationTargetException}. * Should only be called if no checked exception is expected to be thrown * by the target method. * <p>Rethrows the underlying exception cast to a {@link RuntimeException} or * {@link Error} if appropriate; otherwise, throws an * {@link UndeclaredThrowableException}. * @param ex the exception to rethrow * @throws RuntimeException the rethrown exception */
public static void rethrowRuntimeException(Throwable ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); }
Rethrow the given exception, which is presumably the target exception of an InvocationTargetException. Should only be called if no checked exception is expected to be thrown by the target method.

Rethrows the underlying exception cast to an Exception or Error if appropriate; otherwise, throws an UndeclaredThrowableException.

Params:
  • ex – the exception to rethrow
Throws:
  • Exception – the rethrown exception (in case of a checked exception)
/** * Rethrow the given {@link Throwable exception}, which is presumably the * <em>target exception</em> of an {@link InvocationTargetException}. * Should only be called if no checked exception is expected to be thrown * by the target method. * <p>Rethrows the underlying exception cast to an {@link Exception} or * {@link Error} if appropriate; otherwise, throws an * {@link UndeclaredThrowableException}. * @param ex the exception to rethrow * @throws Exception the rethrown exception (in case of a checked exception) */
public static void rethrowException(Throwable ex) throws Exception { if (ex instanceof Exception) { throw (Exception) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); } // Constructor handling
Obtain an accessible constructor for the given class and parameters.
Params:
  • clazz – the clazz to check
  • parameterTypes – the parameter types of the desired constructor
Throws:
Returns:the constructor reference
Since:5.0
/** * Obtain an accessible constructor for the given class and parameters. * @param clazz the clazz to check * @param parameterTypes the parameter types of the desired constructor * @return the constructor reference * @throws NoSuchMethodException if no such constructor exists * @since 5.0 */
public static <T> Constructor<T> accessibleConstructor(Class<T> clazz, Class<?>... parameterTypes) throws NoSuchMethodException { Constructor<T> ctor = clazz.getDeclaredConstructor(parameterTypes); makeAccessible(ctor); return ctor; }
Make the given constructor accessible, explicitly setting it accessible if necessary. The setAccessible(true) method is only called when actually necessary, to avoid unnecessary conflicts with a JVM SecurityManager (if active).
Params:
  • ctor – the constructor to make accessible
See Also:
/** * Make the given constructor accessible, explicitly setting it accessible * if necessary. The {@code setAccessible(true)} method is only called * when actually necessary, to avoid unnecessary conflicts with a JVM * SecurityManager (if active). * @param ctor the constructor to make accessible * @see java.lang.reflect.Constructor#setAccessible */
@SuppressWarnings("deprecation") // on JDK 9 public static void makeAccessible(Constructor<?> ctor) { if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { ctor.setAccessible(true); } } // Method handling
Attempt to find a Method on the supplied class with the supplied name and no parameters. Searches all superclasses up to Object.

Returns null if no Method can be found.

Params:
  • clazz – the class to introspect
  • name – the name of the method
Returns:the Method object, or null if none found
/** * Attempt to find a {@link Method} on the supplied class with the supplied name * and no parameters. Searches all superclasses up to {@code Object}. * <p>Returns {@code null} if no {@link Method} can be found. * @param clazz the class to introspect * @param name the name of the method * @return the Method object, or {@code null} if none found */
@Nullable public static Method findMethod(Class<?> clazz, String name) { return findMethod(clazz, name, EMPTY_CLASS_ARRAY); }
Attempt to find a Method on the supplied class with the supplied name and parameter types. Searches all superclasses up to Object.

Returns null if no Method can be found.

Params:
  • clazz – the class to introspect
  • name – the name of the method
  • paramTypes – the parameter types of the method (may be null to indicate any signature)
Returns:the Method object, or null if none found
/** * Attempt to find a {@link Method} on the supplied class with the supplied name * and parameter types. Searches all superclasses up to {@code Object}. * <p>Returns {@code null} if no {@link Method} can be found. * @param clazz the class to introspect * @param name the name of the method * @param paramTypes the parameter types of the method * (may be {@code null} to indicate any signature) * @return the Method object, or {@code null} if none found */
@Nullable public static Method findMethod(Class<?> clazz, String name, @Nullable Class<?>... paramTypes) { Assert.notNull(clazz, "Class must not be null"); Assert.notNull(name, "Method name must not be null"); Class<?> searchType = clazz; while (searchType != null) { Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType, false)); for (Method method : methods) { if (name.equals(method.getName()) && (paramTypes == null || hasSameParams(method, paramTypes))) { return method; } } searchType = searchType.getSuperclass(); } return null; } private static boolean hasSameParams(Method method, Class<?>[] paramTypes) { return (paramTypes.length == method.getParameterCount() && Arrays.equals(paramTypes, method.getParameterTypes())); }
Invoke the specified Method against the supplied target object with no arguments. The target object can be null when invoking a static Method.

Thrown exceptions are handled via a call to handleReflectionException.

Params:
  • method – the method to invoke
  • target – the target object to invoke the method on
See Also:
Returns:the invocation result, if any
/** * Invoke the specified {@link Method} against the supplied target object with no arguments. * The target object can be {@code null} when invoking a static {@link Method}. * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}. * @param method the method to invoke * @param target the target object to invoke the method on * @return the invocation result, if any * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) */
@Nullable public static Object invokeMethod(Method method, @Nullable Object target) { return invokeMethod(method, target, EMPTY_OBJECT_ARRAY); }
Invoke the specified Method against the supplied target object with the supplied arguments. The target object can be null when invoking a static Method.

Thrown exceptions are handled via a call to handleReflectionException.

Params:
  • method – the method to invoke
  • target – the target object to invoke the method on
  • args – the invocation arguments (may be null)
Returns:the invocation result, if any
/** * Invoke the specified {@link Method} against the supplied target object with the * supplied arguments. The target object can be {@code null} when invoking a * static {@link Method}. * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}. * @param method the method to invoke * @param target the target object to invoke the method on * @param args the invocation arguments (may be {@code null}) * @return the invocation result, if any */
@Nullable public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) { try { return method.invoke(target, args); } catch (Exception ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); }
Determine whether the given method explicitly declares the given exception or one of its superclasses, which means that an exception of that type can be propagated as-is within a reflective invocation.
Params:
  • method – the declaring method
  • exceptionType – the exception to throw
Returns:true if the exception can be thrown as-is; false if it needs to be wrapped
/** * Determine whether the given method explicitly declares the given * exception or one of its superclasses, which means that an exception * of that type can be propagated as-is within a reflective invocation. * @param method the declaring method * @param exceptionType the exception to throw * @return {@code true} if the exception can be thrown as-is; * {@code false} if it needs to be wrapped */
public static boolean declaresException(Method method, Class<?> exceptionType) { Assert.notNull(method, "Method must not be null"); Class<?>[] declaredExceptions = method.getExceptionTypes(); for (Class<?> declaredException : declaredExceptions) { if (declaredException.isAssignableFrom(exceptionType)) { return true; } } return false; }
Perform the given callback operation on all matching methods of the given class, as locally declared or equivalent thereof (such as default methods on Java 8 based interfaces that the given class implements).
Params:
  • clazz – the class to introspect
  • mc – the callback to invoke for each method
Throws:
See Also:
Since:4.2
/** * Perform the given callback operation on all matching methods of the given * class, as locally declared or equivalent thereof (such as default methods * on Java 8 based interfaces that the given class implements). * @param clazz the class to introspect * @param mc the callback to invoke for each method * @throws IllegalStateException if introspection fails * @since 4.2 * @see #doWithMethods */
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) { Method[] methods = getDeclaredMethods(clazz, false); for (Method method : methods) { try { mc.doWith(method); } catch (IllegalAccessException ex) { throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex); } } }
Perform the given callback operation on all matching methods of the given class and superclasses.

The same named method occurring on subclass and superclass will appear twice, unless excluded by a MethodFilter.

Params:
  • clazz – the class to introspect
  • mc – the callback to invoke for each method
Throws:
See Also:
/** * Perform the given callback operation on all matching methods of the given * class and superclasses. * <p>The same named method occurring on subclass and superclass will appear * twice, unless excluded by a {@link MethodFilter}. * @param clazz the class to introspect * @param mc the callback to invoke for each method * @throws IllegalStateException if introspection fails * @see #doWithMethods(Class, MethodCallback, MethodFilter) */
public static void doWithMethods(Class<?> clazz, MethodCallback mc) { doWithMethods(clazz, mc, null); }
Perform the given callback operation on all matching methods of the given class and superclasses (or given interface and super-interfaces).

The same named method occurring on subclass and superclass will appear twice, unless excluded by the specified MethodFilter.

Params:
  • clazz – the class to introspect
  • mc – the callback to invoke for each method
  • mf – the filter that determines the methods to apply the callback to
Throws:
/** * Perform the given callback operation on all matching methods of the given * class and superclasses (or given interface and super-interfaces). * <p>The same named method occurring on subclass and superclass will appear * twice, unless excluded by the specified {@link MethodFilter}. * @param clazz the class to introspect * @param mc the callback to invoke for each method * @param mf the filter that determines the methods to apply the callback to * @throws IllegalStateException if introspection fails */
public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) { // Keep backing up the inheritance hierarchy. Method[] methods = getDeclaredMethods(clazz, false); for (Method method : methods) { if (mf != null && !mf.matches(method)) { continue; } try { mc.doWith(method); } catch (IllegalAccessException ex) { throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex); } } if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) { doWithMethods(clazz.getSuperclass(), mc, mf); } else if (clazz.isInterface()) { for (Class<?> superIfc : clazz.getInterfaces()) { doWithMethods(superIfc, mc, mf); } } }
Get all declared methods on the leaf class and all superclasses. Leaf class methods are included first.
Params:
  • leafClass – the class to introspect
Throws:
/** * Get all declared methods on the leaf class and all superclasses. * Leaf class methods are included first. * @param leafClass the class to introspect * @throws IllegalStateException if introspection fails */
public static Method[] getAllDeclaredMethods(Class<?> leafClass) { final List<Method> methods = new ArrayList<>(32); doWithMethods(leafClass, methods::add); return methods.toArray(EMPTY_METHOD_ARRAY); }
Get the unique set of declared methods on the leaf class and all superclasses. Leaf class methods are included first and while traversing the superclass hierarchy any methods found with signatures matching a method already included are filtered out.
Params:
  • leafClass – the class to introspect
Throws:
/** * Get the unique set of declared methods on the leaf class and all superclasses. * Leaf class methods are included first and while traversing the superclass hierarchy * any methods found with signatures matching a method already included are filtered out. * @param leafClass the class to introspect * @throws IllegalStateException if introspection fails */
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) { return getUniqueDeclaredMethods(leafClass, null); }
Get the unique set of declared methods on the leaf class and all superclasses. Leaf class methods are included first and while traversing the superclass hierarchy any methods found with signatures matching a method already included are filtered out.
Params:
  • leafClass – the class to introspect
  • mf – the filter that determines the methods to take into account
Throws:
Since:5.2
/** * Get the unique set of declared methods on the leaf class and all superclasses. * Leaf class methods are included first and while traversing the superclass hierarchy * any methods found with signatures matching a method already included are filtered out. * @param leafClass the class to introspect * @param mf the filter that determines the methods to take into account * @throws IllegalStateException if introspection fails * @since 5.2 */
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass, @Nullable MethodFilter mf) { final List<Method> methods = new ArrayList<>(32); doWithMethods(leafClass, method -> { boolean knownSignature = false; Method methodBeingOverriddenWithCovariantReturnType = null; for (Method existingMethod : methods) { if (method.getName().equals(existingMethod.getName()) && method.getParameterCount() == existingMethod.getParameterCount() && Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) { // Is this a covariant return type situation? if (existingMethod.getReturnType() != method.getReturnType() && existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) { methodBeingOverriddenWithCovariantReturnType = existingMethod; } else { knownSignature = true; } break; } } if (methodBeingOverriddenWithCovariantReturnType != null) { methods.remove(methodBeingOverriddenWithCovariantReturnType); } if (!knownSignature && !isCglibRenamedMethod(method)) { methods.add(method); } }, mf); return methods.toArray(EMPTY_METHOD_ARRAY); }
Variant of Class.getDeclaredMethods() that uses a local cache in order to avoid the JVM's SecurityManager check and new Method instances. In addition, it also includes Java 8 default methods from locally implemented interfaces, since those are effectively to be treated just like declared methods.
Params:
  • clazz – the class to introspect
Throws:
See Also:
Returns:the cached array of methods
Since:5.2
/** * Variant of {@link Class#getDeclaredMethods()} that uses a local cache in * order to avoid the JVM's SecurityManager check and new Method instances. * In addition, it also includes Java 8 default methods from locally * implemented interfaces, since those are effectively to be treated just * like declared methods. * @param clazz the class to introspect * @return the cached array of methods * @throws IllegalStateException if introspection fails * @since 5.2 * @see Class#getDeclaredMethods() */
public static Method[] getDeclaredMethods(Class<?> clazz) { return getDeclaredMethods(clazz, true); } private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) { Assert.notNull(clazz, "Class must not be null"); Method[] result = declaredMethodsCache.get(clazz); if (result == null) { try { Method[] declaredMethods = clazz.getDeclaredMethods(); List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz); if (defaultMethods != null) { result = new Method[declaredMethods.length + defaultMethods.size()]; System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length); int index = declaredMethods.length; for (Method defaultMethod : defaultMethods) { result[index] = defaultMethod; index++; } } else { result = declaredMethods; } declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result)); } catch (Throwable ex) { throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); } } return (result.length == 0 || !defensive) ? result : result.clone(); } @Nullable private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) { List<Method> result = null; for (Class<?> ifc : clazz.getInterfaces()) { for (Method ifcMethod : ifc.getMethods()) { if (!Modifier.isAbstract(ifcMethod.getModifiers())) { if (result == null) { result = new ArrayList<>(); } result.add(ifcMethod); } } } return result; }
Determine whether the given method is an "equals" method.
See Also:
  • equals.equals(Object)
/** * Determine whether the given method is an "equals" method. * @see java.lang.Object#equals(Object) */
public static boolean isEqualsMethod(@Nullable Method method) { if (method == null) { return false; } if (method.getParameterCount() != 1) { return false; } if (!method.getName().equals("equals")) { return false; } return method.getParameterTypes()[0] == Object.class; }
Determine whether the given method is a "hashCode" method.
See Also:
  • hashCode.hashCode()
/** * Determine whether the given method is a "hashCode" method. * @see java.lang.Object#hashCode() */
public static boolean isHashCodeMethod(@Nullable Method method) { return method != null && method.getParameterCount() == 0 && method.getName().equals("hashCode"); }
Determine whether the given method is a "toString" method.
See Also:
  • toString.toString()
/** * Determine whether the given method is a "toString" method. * @see java.lang.Object#toString() */
public static boolean isToStringMethod(@Nullable Method method) { return (method != null && method.getParameterCount() == 0 && method.getName().equals("toString")); }
Determine whether the given method is originally declared by Object.
/** * Determine whether the given method is originally declared by {@link java.lang.Object}. */
public static boolean isObjectMethod(@Nullable Method method) { return (method != null && (method.getDeclaringClass() == Object.class || isEqualsMethod(method) || isHashCodeMethod(method) || isToStringMethod(method))); }
Determine whether the given method is a CGLIB 'renamed' method, following the pattern "CGLIB$methodName$0".
Params:
  • renamedMethod – the method to check
/** * Determine whether the given method is a CGLIB 'renamed' method, * following the pattern "CGLIB$methodName$0". * @param renamedMethod the method to check */
public static boolean isCglibRenamedMethod(Method renamedMethod) { String name = renamedMethod.getName(); if (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX)) { int i = name.length() - 1; while (i >= 0 && Character.isDigit(name.charAt(i))) { i--; } return (i > CGLIB_RENAMED_METHOD_PREFIX.length() && (i < name.length() - 1) && name.charAt(i) == '$'); } return false; }
Make the given method accessible, explicitly setting it accessible if necessary. The setAccessible(true) method is only called when actually necessary, to avoid unnecessary conflicts with a JVM SecurityManager (if active).
Params:
  • method – the method to make accessible
See Also:
/** * Make the given method accessible, explicitly setting it accessible if * necessary. The {@code setAccessible(true)} method is only called * when actually necessary, to avoid unnecessary conflicts with a JVM * SecurityManager (if active). * @param method the method to make accessible * @see java.lang.reflect.Method#setAccessible */
@SuppressWarnings("deprecation") // on JDK 9 public static void makeAccessible(Method method) { if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { method.setAccessible(true); } } // Field handling
Attempt to find a field on the supplied Class with the supplied name. Searches all superclasses up to Object.
Params:
  • clazz – the class to introspect
  • name – the name of the field
Returns:the corresponding Field object, or null if not found
/** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied {@code name}. Searches all superclasses up to {@link Object}. * @param clazz the class to introspect * @param name the name of the field * @return the corresponding Field object, or {@code null} if not found */
@Nullable public static Field findField(Class<?> clazz, String name) { return findField(clazz, name, null); }
Attempt to find a field on the supplied Class with the supplied name and/or type. Searches all superclasses up to Object.
Params:
  • clazz – the class to introspect
  • name – the name of the field (may be null if type is specified)
  • type – the type of the field (may be null if name is specified)
Returns:the corresponding Field object, or null if not found
/** * Attempt to find a {@link Field field} on the supplied {@link Class} with the * supplied {@code name} and/or {@link Class type}. Searches all superclasses * up to {@link Object}. * @param clazz the class to introspect * @param name the name of the field (may be {@code null} if type is specified) * @param type the type of the field (may be {@code null} if name is specified) * @return the corresponding Field object, or {@code null} if not found */
@Nullable public static Field findField(Class<?> clazz, @Nullable String name, @Nullable Class<?> type) { Assert.notNull(clazz, "Class must not be null"); Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified"); Class<?> searchType = clazz; while (Object.class != searchType && searchType != null) { Field[] fields = getDeclaredFields(searchType); for (Field field : fields) { if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; }
Set the field represented by the supplied field object on the specified target object to the specified value.

In accordance with Field.set(Object, Object) semantics, the new value is automatically unwrapped if the underlying field has a primitive type.

This method does not support setting static final fields.

Thrown exceptions are handled via a call to handleReflectionException(Exception).

Params:
  • field – the field to set
  • target – the target object on which to set the field
  • value – the value to set (may be null)
/** * Set the field represented by the supplied {@linkplain Field field object} on * the specified {@linkplain Object target object} to the specified {@code value}. * <p>In accordance with {@link Field#set(Object, Object)} semantics, the new value * is automatically unwrapped if the underlying field has a primitive type. * <p>This method does not support setting {@code static final} fields. * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * @param field the field to set * @param target the target object on which to set the field * @param value the value to set (may be {@code null}) */
public static void setField(Field field, @Nullable Object target, @Nullable Object value) { try { field.set(target, value); } catch (IllegalAccessException ex) { handleReflectionException(ex); } }
Get the field represented by the supplied field object on the specified target object. In accordance with Field.get(Object) semantics, the returned value is automatically wrapped if the underlying field has a primitive type.

Thrown exceptions are handled via a call to handleReflectionException(Exception).

Params:
  • field – the field to get
  • target – the target object from which to get the field
Returns:the field's current value
/** * Get the field represented by the supplied {@link Field field object} on the * specified {@link Object target object}. In accordance with {@link Field#get(Object)} * semantics, the returned value is automatically wrapped if the underlying field * has a primitive type. * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * @param field the field to get * @param target the target object from which to get the field * @return the field's current value */
@Nullable public static Object getField(Field field, @Nullable Object target) { try { return field.get(target); } catch (IllegalAccessException ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); }
Invoke the given callback on all locally declared fields in the given class.
Params:
  • clazz – the target class to analyze
  • fc – the callback to invoke for each field
Throws:
See Also:
Since:4.2
/** * Invoke the given callback on all locally declared fields in the given class. * @param clazz the target class to analyze * @param fc the callback to invoke for each field * @throws IllegalStateException if introspection fails * @since 4.2 * @see #doWithFields */
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) { for (Field field : getDeclaredFields(clazz)) { try { fc.doWith(field); } catch (IllegalAccessException ex) { throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex); } } }
Invoke the given callback on all fields in the target class, going up the class hierarchy to get all declared fields.
Params:
  • clazz – the target class to analyze
  • fc – the callback to invoke for each field
Throws:
/** * Invoke the given callback on all fields in the target class, going up the * class hierarchy to get all declared fields. * @param clazz the target class to analyze * @param fc the callback to invoke for each field * @throws IllegalStateException if introspection fails */
public static void doWithFields(Class<?> clazz, FieldCallback fc) { doWithFields(clazz, fc, null); }
Invoke the given callback on all fields in the target class, going up the class hierarchy to get all declared fields.
Params:
  • clazz – the target class to analyze
  • fc – the callback to invoke for each field
  • ff – the filter that determines the fields to apply the callback to
Throws:
/** * Invoke the given callback on all fields in the target class, going up the * class hierarchy to get all declared fields. * @param clazz the target class to analyze * @param fc the callback to invoke for each field * @param ff the filter that determines the fields to apply the callback to * @throws IllegalStateException if introspection fails */
public static void doWithFields(Class<?> clazz, FieldCallback fc, @Nullable FieldFilter ff) { // Keep backing up the inheritance hierarchy. Class<?> targetClass = clazz; do { Field[] fields = getDeclaredFields(targetClass); for (Field field : fields) { if (ff != null && !ff.matches(field)) { continue; } try { fc.doWith(field); } catch (IllegalAccessException ex) { throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex); } } targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); }
This variant retrieves Class.getDeclaredFields() from a local cache in order to avoid the JVM's SecurityManager check and defensive array copying.
Params:
  • clazz – the class to introspect
Throws:
See Also:
Returns:the cached array of fields
/** * This variant retrieves {@link Class#getDeclaredFields()} from a local cache * in order to avoid the JVM's SecurityManager check and defensive array copying. * @param clazz the class to introspect * @return the cached array of fields * @throws IllegalStateException if introspection fails * @see Class#getDeclaredFields() */
private static Field[] getDeclaredFields(Class<?> clazz) { Assert.notNull(clazz, "Class must not be null"); Field[] result = declaredFieldsCache.get(clazz); if (result == null) { try { result = clazz.getDeclaredFields(); declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result)); } catch (Throwable ex) { throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex); } } return result; }
Given the source object and the destination, which must be the same class or a subclass, copy all fields, including inherited fields. Designed to work on objects with public no-arg constructors.
Throws:
  • IllegalStateException – if introspection fails
/** * Given the source object and the destination, which must be the same class * or a subclass, copy all fields, including inherited fields. Designed to * work on objects with public no-arg constructors. * @throws IllegalStateException if introspection fails */
public static void shallowCopyFieldState(final Object src, final Object dest) { Assert.notNull(src, "Source for field copy cannot be null"); Assert.notNull(dest, "Destination for field copy cannot be null"); if (!src.getClass().isAssignableFrom(dest.getClass())) { throw new IllegalArgumentException("Destination class [" + dest.getClass().getName() + "] must be same or subclass as source class [" + src.getClass().getName() + "]"); } doWithFields(src.getClass(), field -> { makeAccessible(field); Object srcValue = field.get(src); field.set(dest, srcValue); }, COPYABLE_FIELDS); }
Determine whether the given field is a "public static final" constant.
Params:
  • field – the field to check
/** * Determine whether the given field is a "public static final" constant. * @param field the field to check */
public static boolean isPublicStaticFinal(Field field) { int modifiers = field.getModifiers(); return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)); }
Make the given field accessible, explicitly setting it accessible if necessary. The setAccessible(true) method is only called when actually necessary, to avoid unnecessary conflicts with a JVM SecurityManager (if active).
Params:
  • field – the field to make accessible
See Also:
/** * Make the given field accessible, explicitly setting it accessible if * necessary. The {@code setAccessible(true)} method is only called * when actually necessary, to avoid unnecessary conflicts with a JVM * SecurityManager (if active). * @param field the field to make accessible * @see java.lang.reflect.Field#setAccessible */
@SuppressWarnings("deprecation") // on JDK 9 public static void makeAccessible(Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } } // Cache handling
Clear the internal method/field cache.
Since:4.2.4
/** * Clear the internal method/field cache. * @since 4.2.4 */
public static void clearCache() { declaredMethodsCache.clear(); declaredFieldsCache.clear(); }
Action to take on each method.
/** * Action to take on each method. */
@FunctionalInterface public interface MethodCallback {
Perform an operation using the given method.
Params:
  • method – the method to operate on
/** * Perform an operation using the given method. * @param method the method to operate on */
void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; }
Callback optionally used to filter methods to be operated on by a method callback.
/** * Callback optionally used to filter methods to be operated on by a method callback. */
@FunctionalInterface public interface MethodFilter {
Determine whether the given method matches.
Params:
  • method – the method to check
/** * Determine whether the given method matches. * @param method the method to check */
boolean matches(Method method);
Create a composite filter based on this filter and the provided filter.

If this filter does not match, the next filter will not be applied.

Params:
  • next – the next MethodFilter
Throws:
Returns:a composite MethodFilter
Since:5.3.2
/** * Create a composite filter based on this filter <em>and</em> the provided filter. * <p>If this filter does not match, the next filter will not be applied. * @param next the next {@code MethodFilter} * @return a composite {@code MethodFilter} * @throws IllegalArgumentException if the MethodFilter argument is {@code null} * @since 5.3.2 */
default MethodFilter and(MethodFilter next) { Assert.notNull(next, "Next MethodFilter must not be null"); return method -> matches(method) && next.matches(method); } }
Callback interface invoked on each field in the hierarchy.
/** * Callback interface invoked on each field in the hierarchy. */
@FunctionalInterface public interface FieldCallback {
Perform an operation using the given field.
Params:
  • field – the field to operate on
/** * Perform an operation using the given field. * @param field the field to operate on */
void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; }
Callback optionally used to filter fields to be operated on by a field callback.
/** * Callback optionally used to filter fields to be operated on by a field callback. */
@FunctionalInterface public interface FieldFilter {
Determine whether the given field matches.
Params:
  • field – the field to check
/** * Determine whether the given field matches. * @param field the field to check */
boolean matches(Field field);
Create a composite filter based on this filter and the provided filter.

If this filter does not match, the next filter will not be applied.

Params:
  • next – the next FieldFilter
Throws:
Returns:a composite FieldFilter
Since:5.3.2
/** * Create a composite filter based on this filter <em>and</em> the provided filter. * <p>If this filter does not match, the next filter will not be applied. * @param next the next {@code FieldFilter} * @return a composite {@code FieldFilter} * @throws IllegalArgumentException if the FieldFilter argument is {@code null} * @since 5.3.2 */
default FieldFilter and(FieldFilter next) { Assert.notNull(next, "Next FieldFilter must not be null"); return field -> matches(field) && next.matches(field); } } }