ReflectionUtils.java 30.6 KB
Newer Older
1
/*
J
Juergen Hoeller 已提交
2
 * Copyright 2002-2019 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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.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;
24
import java.lang.reflect.UndeclaredThrowableException;
25 26 27 28
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
29
import java.util.Map;
30

31 32
import org.springframework.lang.Nullable;

33 34 35
/**
 * Simple utility class for working with the reflection API and handling
 * reflection exceptions.
J
Juergen Hoeller 已提交
36 37 38
 *
 * <p>Only intended for internal use.
 *
39 40 41 42 43
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @author Rod Johnson
 * @author Costin Leau
 * @author Sam Brannen
44
 * @author Chris Beams
45 46 47 48
 * @since 1.2.2
 */
public abstract class ReflectionUtils {

49
	/**
50 51 52
	 * Pre-built MethodFilter that matches all non-bridge methods.
	 * @since 3.0
	 * @deprecated as of 5.0.11, in favor of a custom {@link MethodFilter}
53
	 */
54 55 56
	@Deprecated
	public static final MethodFilter NON_BRIDGED_METHODS =
			(method -> !method.isBridge());
57

58 59 60 61 62 63
	/**
	 * 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 =
J
Juergen Hoeller 已提交
64
			(method -> !method.isBridge() && !method.isSynthetic() && method.getDeclaringClass() != Object.class);
65

P
Phillip Webb 已提交
66 67 68 69
	/**
	 * Pre-built FieldFilter that matches all non-static, non-final fields.
	 */
	public static final FieldFilter COPYABLE_FIELDS =
J
Juergen Hoeller 已提交
70
			(field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())));
P
Phillip Webb 已提交
71 72 73


	/**
74 75
	 * Naming prefix for CGLIB-renamed methods.
	 * @see #isCglibRenamedMethod
P
Phillip Webb 已提交
76
	 */
77
	private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
P
Phillip Webb 已提交
78

J
Juergen Hoeller 已提交
79
	private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
P
Phillip Webb 已提交
80

J
Juergen Hoeller 已提交
81
	private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
P
Phillip Webb 已提交
82

J
Juergen Hoeller 已提交
83

84
	/**
85 86
	 * Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods
	 * from Java 8 based interfaces, allowing for fast iteration.
87
	 */
88
	private static final Map<Class<?>, Method[]> declaredMethodsCache = new ConcurrentReferenceHashMap<>(256);
89

90 91 92
	/**
	 * Cache for {@link Class#getDeclaredFields()}, allowing for fast iteration.
	 */
93
	private static final Map<Class<?>, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<>(256);
94

J
Juergen Hoeller 已提交
95

J
Juergen Hoeller 已提交
96
	// Exception handling
97 98

	/**
99 100
	 * Handle the given reflection exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
J
Juergen Hoeller 已提交
101
	 * <p>Throws the underlying RuntimeException or Error in case of an
102
	 * InvocationTargetException with such a root cause. Throws an
103 104
	 * IllegalStateException with an appropriate message or
	 * UndeclaredThrowableException otherwise.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	 * @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: " + ex.getMessage());
		}
		if (ex instanceof InvocationTargetException) {
			handleInvocationTargetException((InvocationTargetException) ex);
		}
		if (ex instanceof RuntimeException) {
			throw (RuntimeException) ex;
		}
120
		throw new UndeclaredThrowableException(ex);
121 122 123
	}

	/**
124 125
	 * Handle the given invocation target exception. Should only be called if no
	 * checked exception is expected to be thrown by the target method.
J
Juergen Hoeller 已提交
126
	 * <p>Throws the underlying RuntimeException or Error in case of such a root
127
	 * cause. Throws an UndeclaredThrowableException otherwise.
128 129 130 131 132 133 134 135
	 * @param ex the invocation target exception to handle
	 */
	public static void handleInvocationTargetException(InvocationTargetException ex) {
		rethrowRuntimeException(ex.getTargetException());
	}

	/**
	 * Rethrow the given {@link Throwable exception}, which is presumably the
J
Juergen Hoeller 已提交
136 137 138
	 * <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.
139 140 141
	 * <p>Rethrows the underlying exception cast to a {@link RuntimeException} or
	 * {@link Error} if appropriate; otherwise, throws an
	 * {@link UndeclaredThrowableException}.
142 143 144 145 146 147 148 149 150 151
	 * @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;
		}
152
		throw new UndeclaredThrowableException(ex);
153 154 155 156
	}

	/**
	 * Rethrow the given {@link Throwable exception}, which is presumably the
J
Juergen Hoeller 已提交
157 158 159
	 * <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.
J
Juergen Hoeller 已提交
160
	 * <p>Rethrows the underlying exception cast to an {@link Exception} or
161 162
	 * {@link Error} if appropriate; otherwise, throws an
	 * {@link UndeclaredThrowableException}.
163 164 165 166 167 168 169 170 171 172
	 * @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;
		}
173
		throw new UndeclaredThrowableException(ex);
174 175
	}

J
Juergen Hoeller 已提交
176 177

	// Constructor handling
178 179

	/**
J
Juergen Hoeller 已提交
180 181 182 183 184 185
	 * 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
186
	 */
J
Juergen Hoeller 已提交
187 188 189 190 191 192
	public static <T> Constructor<T> accessibleConstructor(Class<T> clazz, Class<?>... parameterTypes)
			throws NoSuchMethodException {

		Constructor<T> ctor = clazz.getDeclaredConstructor(parameterTypes);
		makeAccessible(ctor);
		return ctor;
193 194 195
	}

	/**
J
Juergen Hoeller 已提交
196 197 198 199 200 201
	 * 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
202
	 */
J
Juergen Hoeller 已提交
203 204 205 206 207
	@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);
208 209 210
		}
	}

J
Juergen Hoeller 已提交
211 212

	// Method handling
213 214

	/**
J
Juergen Hoeller 已提交
215 216 217 218 219 220
	 * 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
221
	 */
J
Juergen Hoeller 已提交
222 223 224
	@Nullable
	public static Method findMethod(Class<?> clazz, String name) {
		return findMethod(clazz, name, new Class<?>[0]);
225 226
	}

227
	/**
J
Juergen Hoeller 已提交
228 229 230 231 232 233 234 235
	 * 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
236
	 */
J
Juergen Hoeller 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250
	@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));
			for (Method method : methods) {
				if (name.equals(method.getName()) &&
						(paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
					return method;
				}
			}
			searchType = searchType.getSuperclass();
251
		}
J
Juergen Hoeller 已提交
252
		return null;
253 254
	}

255
	/**
J
Juergen Hoeller 已提交
256 257 258 259 260 261 262
	 * 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[])
263
	 */
J
Juergen Hoeller 已提交
264 265 266
	@Nullable
	public static Object invokeMethod(Method method, @Nullable Object target) {
		return invokeMethod(method, target, new Object[0]);
267 268
	}

269
	/**
J
Juergen Hoeller 已提交
270 271 272 273 274 275 276 277
	 * 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
278
	 */
J
Juergen Hoeller 已提交
279 280 281 282
	@Nullable
	public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) {
		try {
			return method.invoke(target, args);
283
		}
J
Juergen Hoeller 已提交
284 285 286 287
		catch (Exception ex) {
			handleReflectionException(ex);
		}
		throw new IllegalStateException("Should never get here");
288 289 290
	}

	/**
J
Juergen Hoeller 已提交
291 292 293 294 295 296 297 298
	 * Invoke the specified JDBC API {@link Method} against the supplied target
	 * object with no arguments.
	 * @param method the method to invoke
	 * @param target the target object to invoke the method on
	 * @return the invocation result, if any
	 * @throws SQLException the JDBC API SQLException to rethrow (if any)
	 * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[])
	 * @deprecated as of 5.0.11, in favor of custom SQLException handling
299
	 */
J
Juergen Hoeller 已提交
300 301 302 303
	@Deprecated
	@Nullable
	public static Object invokeJdbcMethod(Method method, @Nullable Object target) throws SQLException {
		return invokeJdbcMethod(method, target, new Object[0]);
304 305 306
	}

	/**
J
Juergen Hoeller 已提交
307 308 309 310 311 312 313 314 315
	 * Invoke the specified JDBC API {@link Method} against the supplied target
	 * object with the supplied arguments.
	 * @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
	 * @throws SQLException the JDBC API SQLException to rethrow (if any)
	 * @see #invokeMethod(java.lang.reflect.Method, Object, Object[])
	 * @deprecated as of 5.0.11, in favor of custom SQLException handling
316
	 */
J
Juergen Hoeller 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
	@Deprecated
	@Nullable
	public static Object invokeJdbcMethod(Method method, @Nullable Object target, @Nullable Object... args)
			throws SQLException {
		try {
			return method.invoke(target, args);
		}
		catch (IllegalAccessException ex) {
			handleReflectionException(ex);
		}
		catch (InvocationTargetException ex) {
			if (ex.getTargetException() instanceof SQLException) {
				throw (SQLException) ex.getTargetException();
			}
			handleInvocationTargetException(ex);
332
		}
J
Juergen Hoeller 已提交
333
		throw new IllegalStateException("Should never get here");
334 335
	}

336
	/**
J
Juergen Hoeller 已提交
337 338 339 340 341 342 343
	 * 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
344
	 */
J
Juergen Hoeller 已提交
345 346 347 348 349 350 351 352 353
	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;
354 355
	}

356 357 358 359 360 361
	/**
	 * 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
362
	 * @throws IllegalStateException if introspection fails
P
Phillip Webb 已提交
363
	 * @since 4.2
364 365 366 367 368 369 370 371 372 373 374 375 376 377
	 * @see #doWithMethods
	 */
	public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
		Method[] methods = getDeclaredMethods(clazz);
		for (Method method : methods) {
			try {
				mc.doWith(method);
			}
			catch (IllegalAccessException ex) {
				throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
			}
		}
	}

378
	/**
379 380
	 * Perform the given callback operation on all matching methods of the given
	 * class and superclasses.
J
Juergen Hoeller 已提交
381
	 * <p>The same named method occurring on subclass and superclass will appear
382
	 * twice, unless excluded by a {@link MethodFilter}.
383
	 * @param clazz the class to introspect
384
	 * @param mc the callback to invoke for each method
385
	 * @throws IllegalStateException if introspection fails
386 387
	 * @see #doWithMethods(Class, MethodCallback, MethodFilter)
	 */
388
	public static void doWithMethods(Class<?> clazz, MethodCallback mc) {
389
		doWithMethods(clazz, mc, null);
390 391 392
	}

	/**
393
	 * Perform the given callback operation on all matching methods of the given
394
	 * class and superclasses (or given interface and super-interfaces).
J
Juergen Hoeller 已提交
395
	 * <p>The same named method occurring on subclass and superclass will appear
396
	 * twice, unless excluded by the specified {@link MethodFilter}.
397
	 * @param clazz the class to introspect
398 399
	 * @param mc the callback to invoke for each method
	 * @param mf the filter that determines the methods to apply the callback to
400
	 * @throws IllegalStateException if introspection fails
401
	 */
402
	public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
403
		// Keep backing up the inheritance hierarchy.
404
		Method[] methods = getDeclaredMethods(clazz);
405 406 407 408 409 410 411 412
		for (Method method : methods) {
			if (mf != null && !mf.matches(method)) {
				continue;
			}
			try {
				mc.doWith(method);
			}
			catch (IllegalAccessException ex) {
413
				throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
414 415 416 417 418 419 420 421
			}
		}
		if (clazz.getSuperclass() != null) {
			doWithMethods(clazz.getSuperclass(), mc, mf);
		}
		else if (clazz.isInterface()) {
			for (Class<?> superIfc : clazz.getInterfaces()) {
				doWithMethods(superIfc, mc, mf);
422
			}
423
		}
424 425 426
	}

	/**
427 428 429
	 * Get all declared methods on the leaf class and all superclasses.
	 * Leaf class methods are included first.
	 * @param leafClass the class to introspect
430
	 * @throws IllegalStateException if introspection fails
431
	 */
432
	public static Method[] getAllDeclaredMethods(Class<?> leafClass) {
433
		final List<Method> methods = new ArrayList<>(32);
434
		doWithMethods(leafClass, methods::add);
J
Juergen Hoeller 已提交
435
		return methods.toArray(EMPTY_METHOD_ARRAY);
436 437
	}

438
	/**
439 440 441 442
	 * 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
443
	 * @throws IllegalStateException if introspection fails
444
	 */
445
	public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
446
		final List<Method> methods = new ArrayList<>(32);
447 448 449 450 451 452 453 454 455 456
		doWithMethods(leafClass, method -> {
			boolean knownSignature = false;
			Method methodBeingOverriddenWithCovariantReturnType = null;
			for (Method existingMethod : methods) {
				if (method.getName().equals(existingMethod.getName()) &&
						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;
457
					}
458 459 460 461
					else {
						knownSignature = true;
					}
					break;
462 463
				}
			}
464 465 466 467 468 469
			if (methodBeingOverriddenWithCovariantReturnType != null) {
				methods.remove(methodBeingOverriddenWithCovariantReturnType);
			}
			if (!knownSignature && !isCglibRenamedMethod(method)) {
				methods.add(method);
			}
470
		});
J
Juergen Hoeller 已提交
471
		return methods.toArray(EMPTY_METHOD_ARRAY);
472 473
	}

474
	/**
475
	 * This variant retrieves {@link Class#getDeclaredMethods()} from a local cache
476
	 * in order to avoid the JVM's SecurityManager check and defensive array copying.
477 478 479 480
	 * 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
481
	 * @throws IllegalStateException if introspection fails
482
	 * @see Class#getDeclaredMethods()
483 484
	 */
	private static Method[] getDeclaredMethods(Class<?> clazz) {
485
		Assert.notNull(clazz, "Class must not be null");
486 487
		Method[] result = declaredMethodsCache.get(clazz);
		if (result == null) {
488 489 490 491 492 493 494 495 496 497 498 499 500 501
			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;
502
				}
J
Juergen Hoeller 已提交
503
				declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));
504
			}
505
			catch (Throwable ex) {
506
				throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
507
						"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
508
			}
509 510 511 512
		}
		return result;
	}

513
	@Nullable
514 515 516 517 518 519
	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) {
S
stsypanov 已提交
520
						result = new ArrayList<>();
521 522 523 524 525 526 527 528
					}
					result.add(ifcMethod);
				}
			}
		}
		return result;
	}

J
Juergen Hoeller 已提交
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
	/**
	 * 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 || !method.getName().equals("equals")) {
			return false;
		}
		Class<?>[] paramTypes = method.getParameterTypes();
		return (paramTypes.length == 1 && paramTypes[0] == Object.class);
	}

	/**
	 * 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.getName().equals("hashCode") && method.getParameterCount() == 0);
	}

	/**
	 * 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.getName().equals("toString") && method.getParameterCount() == 0);
	}

	/**
	 * Determine whether the given method is originally declared by {@link java.lang.Object}.
	 */
	public static boolean isObjectMethod(@Nullable Method method) {
		if (method == null) {
			return false;
		}
		try {
			Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes());
			return true;
		}
		catch (Exception ex) {
			return false;
		}
	}

	/**
	 * 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 {@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 {@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 {@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 {@link Field field object} on the
	 * specified {@link Object target object} to the specified {@code value}.
	 * In accordance with {@link Field#set(Object, Object)} semantics, the new value
	 * is automatically unwrapped 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 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);
			throw new IllegalStateException(
					"Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
		}
	}

	/**
	 * 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(
					"Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
		}
	}

691
	/**
692
	 * Invoke the given callback on all locally declared fields in the given class.
693
	 * @param clazz the target class to analyze
694
	 * @param fc the callback to invoke for each field
695
	 * @throws IllegalStateException if introspection fails
P
Phillip Webb 已提交
696
	 * @since 4.2
697
	 * @see #doWithFields
698
	 */
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
	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.
	 * @param clazz the target class to analyze
	 * @param fc the callback to invoke for each field
715
	 * @throws IllegalStateException if introspection fails
716 717
	 */
	public static void doWithFields(Class<?> clazz, FieldCallback fc) {
718
		doWithFields(clazz, fc, null);
719 720 721
	}

	/**
722 723
	 * Invoke the given callback on all fields in the target class, going up the
	 * class hierarchy to get all declared fields.
724
	 * @param clazz the target class to analyze
725 726
	 * @param fc the callback to invoke for each field
	 * @param ff the filter that determines the fields to apply the callback to
727
	 * @throws IllegalStateException if introspection fails
728
	 */
729
	public static void doWithFields(Class<?> clazz, FieldCallback fc, @Nullable FieldFilter ff) {
730
		// Keep backing up the inheritance hierarchy.
731
		Class<?> targetClass = clazz;
732
		do {
733
			Field[] fields = getDeclaredFields(targetClass);
J
Juergen Hoeller 已提交
734 735
			for (Field field : fields) {
				if (ff != null && !ff.matches(field)) {
736 737 738
					continue;
				}
				try {
J
Juergen Hoeller 已提交
739
					fc.doWith(field);
740 741
				}
				catch (IllegalAccessException ex) {
742
					throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
743 744 745
				}
			}
			targetClass = targetClass.getSuperclass();
746 747
		}
		while (targetClass != null && targetClass != Object.class);
748 749
	}

750 751 752 753 754
	/**
	 * 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
755
	 * @throws IllegalStateException if introspection fails
756 757 758
	 * @see Class#getDeclaredFields()
	 */
	private static Field[] getDeclaredFields(Class<?> clazz) {
759
		Assert.notNull(clazz, "Class must not be null");
760 761
		Field[] result = declaredFieldsCache.get(clazz);
		if (result == null) {
762 763
			try {
				result = clazz.getDeclaredFields();
J
Juergen Hoeller 已提交
764
				declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
765 766
			}
			catch (Throwable ex) {
767
				throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
768 769
						"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
			}
770 771 772 773
		}
		return result;
	}

774 775 776 777
	/**
	 * 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.
778
	 * @throws IllegalStateException if introspection fails
779
	 */
780
	public static void shallowCopyFieldState(final Object src, final Object dest) {
781 782
		Assert.notNull(src, "Source for field copy cannot be null");
		Assert.notNull(dest, "Destination for field copy cannot be null");
783
		if (!src.getClass().isAssignableFrom(dest.getClass())) {
J
Juergen Hoeller 已提交
784 785
			throw new IllegalArgumentException("Destination class [" + dest.getClass().getName() +
					"] must be same or subclass as source class [" + src.getClass().getName() + "]");
786
		}
787 788 789 790
		doWithFields(src.getClass(), field -> {
			makeAccessible(field);
			Object srcValue = field.get(src);
			field.set(dest, srcValue);
791 792 793
		}, COPYABLE_FIELDS);
	}

J
Juergen Hoeller 已提交
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
	/**
	 * 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 {@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

823 824 825 826 827 828 829 830 831
	/**
	 * Clear the internal method/field cache.
	 * @since 4.2.4
	 */
	public static void clearCache() {
		declaredMethodsCache.clear();
		declaredFieldsCache.clear();
	}

832 833 834 835

	/**
	 * Action to take on each method.
	 */
836
	@FunctionalInterface
837
	public interface MethodCallback {
838 839 840 841 842 843 844 845

		/**
		 * Perform an operation using the given method.
		 * @param method the method to operate on
		 */
		void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
	}

J
Juergen Hoeller 已提交
846

847
	/**
S
Sam Brannen 已提交
848
	 * Callback optionally used to filter methods to be operated on by a method callback.
849
	 */
850
	@FunctionalInterface
851
	public interface MethodFilter {
852 853 854 855 856 857 858 859

		/**
		 * Determine whether the given method matches.
		 * @param method the method to check
		 */
		boolean matches(Method method);
	}

860

861 862 863
	/**
	 * Callback interface invoked on each field in the hierarchy.
	 */
864
	@FunctionalInterface
865
	public interface FieldCallback {
866 867 868 869 870 871 872 873

		/**
		 * Perform an operation using the given field.
		 * @param field the field to operate on
		 */
		void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
	}

J
Juergen Hoeller 已提交
874

875
	/**
J
Juergen Hoeller 已提交
876
	 * Callback optionally used to filter fields to be operated on by a field callback.
877
	 */
878
	@FunctionalInterface
879
	public interface FieldFilter {
880 881 882 883 884 885 886 887 888

		/**
		 * Determine whether the given field matches.
		 * @param field the field to check
		 */
		boolean matches(Field field);
	}

}