001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.util; 018 019import java.beans.Introspector; 020import java.io.Closeable; 021import java.io.Externalizable; 022import java.io.Serializable; 023import java.lang.reflect.Array; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.Method; 026import java.lang.reflect.Modifier; 027import java.lang.reflect.Proxy; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.Enumeration; 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.IdentityHashMap; 035import java.util.Iterator; 036import java.util.LinkedHashSet; 037import java.util.List; 038import java.util.Map; 039import java.util.Optional; 040import java.util.Set; 041import java.util.StringJoiner; 042 043import org.springframework.lang.Nullable; 044 045/** 046 * Miscellaneous {@code java.lang.Class} utility methods. 047 * Mainly for internal use within the framework. 048 * 049 * @author Juergen Hoeller 050 * @author Keith Donald 051 * @author Rob Harrop 052 * @author Sam Brannen 053 * @since 1.1 054 * @see TypeUtils 055 * @see ReflectionUtils 056 */ 057public abstract class ClassUtils { 058 059 /** Suffix for array class names: {@code "[]"}. */ 060 public static final String ARRAY_SUFFIX = "[]"; 061 062 /** Prefix for internal array class names: {@code "["}. */ 063 private static final String INTERNAL_ARRAY_PREFIX = "["; 064 065 /** Prefix for internal non-primitive array class names: {@code "[L"}. */ 066 private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; 067 068 /** A reusable empty class array constant. */ 069 private static final Class<?>[] EMPTY_CLASS_ARRAY = {}; 070 071 /** The package separator character: {@code '.'}. */ 072 private static final char PACKAGE_SEPARATOR = '.'; 073 074 /** The path separator character: {@code '/'}. */ 075 private static final char PATH_SEPARATOR = '/'; 076 077 /** The inner class separator character: {@code '$'}. */ 078 private static final char INNER_CLASS_SEPARATOR = '$'; 079 080 /** The CGLIB class separator: {@code "$$"}. */ 081 public static final String CGLIB_CLASS_SEPARATOR = "$$"; 082 083 /** The ".class" file suffix. */ 084 public static final String CLASS_FILE_SUFFIX = ".class"; 085 086 087 /** 088 * Map with primitive wrapper type as key and corresponding primitive 089 * type as value, for example: Integer.class -> int.class. 090 */ 091 private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new IdentityHashMap<>(8); 092 093 /** 094 * Map with primitive type as key and corresponding wrapper 095 * type as value, for example: int.class -> Integer.class. 096 */ 097 private static final Map<Class<?>, Class<?>> primitiveTypeToWrapperMap = new IdentityHashMap<>(8); 098 099 /** 100 * Map with primitive type name as key and corresponding primitive 101 * type as value, for example: "int" -> "int.class". 102 */ 103 private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap<>(32); 104 105 /** 106 * Map with common Java language class name as key and corresponding Class as value. 107 * Primarily for efficient deserialization of remote invocations. 108 */ 109 private static final Map<String, Class<?>> commonClassCache = new HashMap<>(64); 110 111 /** 112 * Common Java language interfaces which are supposed to be ignored 113 * when searching for 'primary' user-level interfaces. 114 */ 115 private static final Set<Class<?>> javaLanguageInterfaces; 116 117 /** 118 * Cache for equivalent methods on an interface implemented by the declaring class. 119 */ 120 private static final Map<Method, Method> interfaceMethodCache = new ConcurrentReferenceHashMap<>(256); 121 122 123 static { 124 primitiveWrapperTypeMap.put(Boolean.class, boolean.class); 125 primitiveWrapperTypeMap.put(Byte.class, byte.class); 126 primitiveWrapperTypeMap.put(Character.class, char.class); 127 primitiveWrapperTypeMap.put(Double.class, double.class); 128 primitiveWrapperTypeMap.put(Float.class, float.class); 129 primitiveWrapperTypeMap.put(Integer.class, int.class); 130 primitiveWrapperTypeMap.put(Long.class, long.class); 131 primitiveWrapperTypeMap.put(Short.class, short.class); 132 primitiveWrapperTypeMap.put(Void.class, void.class); 133 134 // Map entry iteration is less expensive to initialize than forEach with lambdas 135 for (Map.Entry<Class<?>, Class<?>> entry : primitiveWrapperTypeMap.entrySet()) { 136 primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey()); 137 registerCommonClasses(entry.getKey()); 138 } 139 140 Set<Class<?>> primitiveTypes = new HashSet<>(32); 141 primitiveTypes.addAll(primitiveWrapperTypeMap.values()); 142 Collections.addAll(primitiveTypes, boolean[].class, byte[].class, char[].class, 143 double[].class, float[].class, int[].class, long[].class, short[].class); 144 for (Class<?> primitiveType : primitiveTypes) { 145 primitiveTypeNameMap.put(primitiveType.getName(), primitiveType); 146 } 147 148 registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, 149 Float[].class, Integer[].class, Long[].class, Short[].class); 150 registerCommonClasses(Number.class, Number[].class, String.class, String[].class, 151 Class.class, Class[].class, Object.class, Object[].class); 152 registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, 153 Error.class, StackTraceElement.class, StackTraceElement[].class); 154 registerCommonClasses(Enum.class, Iterable.class, Iterator.class, Enumeration.class, 155 Collection.class, List.class, Set.class, Map.class, Map.Entry.class, Optional.class); 156 157 Class<?>[] javaLanguageInterfaceArray = {Serializable.class, Externalizable.class, 158 Closeable.class, AutoCloseable.class, Cloneable.class, Comparable.class}; 159 registerCommonClasses(javaLanguageInterfaceArray); 160 javaLanguageInterfaces = new HashSet<>(Arrays.asList(javaLanguageInterfaceArray)); 161 } 162 163 164 /** 165 * Register the given common classes with the ClassUtils cache. 166 */ 167 private static void registerCommonClasses(Class<?>... commonClasses) { 168 for (Class<?> clazz : commonClasses) { 169 commonClassCache.put(clazz.getName(), clazz); 170 } 171 } 172 173 /** 174 * Return the default ClassLoader to use: typically the thread context 175 * ClassLoader, if available; the ClassLoader that loaded the ClassUtils 176 * class will be used as fallback. 177 * <p>Call this method if you intend to use the thread context ClassLoader 178 * in a scenario where you clearly prefer a non-null ClassLoader reference: 179 * for example, for class path resource loading (but not necessarily for 180 * {@code Class.forName}, which accepts a {@code null} ClassLoader 181 * reference as well). 182 * @return the default ClassLoader (only {@code null} if even the system 183 * ClassLoader isn't accessible) 184 * @see Thread#getContextClassLoader() 185 * @see ClassLoader#getSystemClassLoader() 186 */ 187 @Nullable 188 public static ClassLoader getDefaultClassLoader() { 189 ClassLoader cl = null; 190 try { 191 cl = Thread.currentThread().getContextClassLoader(); 192 } 193 catch (Throwable ex) { 194 // Cannot access thread context ClassLoader - falling back... 195 } 196 if (cl == null) { 197 // No thread context class loader -> use class loader of this class. 198 cl = ClassUtils.class.getClassLoader(); 199 if (cl == null) { 200 // getClassLoader() returning null indicates the bootstrap ClassLoader 201 try { 202 cl = ClassLoader.getSystemClassLoader(); 203 } 204 catch (Throwable ex) { 205 // Cannot access system ClassLoader - oh well, maybe the caller can live with null... 206 } 207 } 208 } 209 return cl; 210 } 211 212 /** 213 * Override the thread context ClassLoader with the environment's bean ClassLoader 214 * if necessary, i.e. if the bean ClassLoader is not equivalent to the thread 215 * context ClassLoader already. 216 * @param classLoaderToUse the actual ClassLoader to use for the thread context 217 * @return the original thread context ClassLoader, or {@code null} if not overridden 218 */ 219 @Nullable 220 public static ClassLoader overrideThreadContextClassLoader(@Nullable ClassLoader classLoaderToUse) { 221 Thread currentThread = Thread.currentThread(); 222 ClassLoader threadContextClassLoader = currentThread.getContextClassLoader(); 223 if (classLoaderToUse != null && !classLoaderToUse.equals(threadContextClassLoader)) { 224 currentThread.setContextClassLoader(classLoaderToUse); 225 return threadContextClassLoader; 226 } 227 else { 228 return null; 229 } 230 } 231 232 /** 233 * Replacement for {@code Class.forName()} that also returns Class instances 234 * for primitives (e.g. "int") and array class names (e.g. "String[]"). 235 * Furthermore, it is also capable of resolving inner class names in Java source 236 * style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State"). 237 * @param name the name of the Class 238 * @param classLoader the class loader to use 239 * (may be {@code null}, which indicates the default class loader) 240 * @return a class instance for the supplied name 241 * @throws ClassNotFoundException if the class was not found 242 * @throws LinkageError if the class file could not be loaded 243 * @see Class#forName(String, boolean, ClassLoader) 244 */ 245 public static Class<?> forName(String name, @Nullable ClassLoader classLoader) 246 throws ClassNotFoundException, LinkageError { 247 248 Assert.notNull(name, "Name must not be null"); 249 250 Class<?> clazz = resolvePrimitiveClassName(name); 251 if (clazz == null) { 252 clazz = commonClassCache.get(name); 253 } 254 if (clazz != null) { 255 return clazz; 256 } 257 258 // "java.lang.String[]" style arrays 259 if (name.endsWith(ARRAY_SUFFIX)) { 260 String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); 261 Class<?> elementClass = forName(elementClassName, classLoader); 262 return Array.newInstance(elementClass, 0).getClass(); 263 } 264 265 // "[Ljava.lang.String;" style arrays 266 if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { 267 String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); 268 Class<?> elementClass = forName(elementName, classLoader); 269 return Array.newInstance(elementClass, 0).getClass(); 270 } 271 272 // "[[I" or "[[Ljava.lang.String;" style arrays 273 if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { 274 String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); 275 Class<?> elementClass = forName(elementName, classLoader); 276 return Array.newInstance(elementClass, 0).getClass(); 277 } 278 279 ClassLoader clToUse = classLoader; 280 if (clToUse == null) { 281 clToUse = getDefaultClassLoader(); 282 } 283 try { 284 return Class.forName(name, false, clToUse); 285 } 286 catch (ClassNotFoundException ex) { 287 int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); 288 if (lastDotIndex != -1) { 289 String innerClassName = 290 name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1); 291 try { 292 return Class.forName(innerClassName, false, clToUse); 293 } 294 catch (ClassNotFoundException ex2) { 295 // Swallow - let original exception get through 296 } 297 } 298 throw ex; 299 } 300 } 301 302 /** 303 * Resolve the given class name into a Class instance. Supports 304 * primitives (like "int") and array class names (like "String[]"). 305 * <p>This is effectively equivalent to the {@code forName} 306 * method with the same arguments, with the only difference being 307 * the exceptions thrown in case of class loading failure. 308 * @param className the name of the Class 309 * @param classLoader the class loader to use 310 * (may be {@code null}, which indicates the default class loader) 311 * @return a class instance for the supplied name 312 * @throws IllegalArgumentException if the class name was not resolvable 313 * (that is, the class could not be found or the class file could not be loaded) 314 * @throws IllegalStateException if the corresponding class is resolvable but 315 * there was a readability mismatch in the inheritance hierarchy of the class 316 * (typically a missing dependency declaration in a Jigsaw module definition 317 * for a superclass or interface implemented by the class to be loaded here) 318 * @see #forName(String, ClassLoader) 319 */ 320 public static Class<?> resolveClassName(String className, @Nullable ClassLoader classLoader) 321 throws IllegalArgumentException { 322 323 try { 324 return forName(className, classLoader); 325 } 326 catch (IllegalAccessError err) { 327 throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + 328 className + "]: " + err.getMessage(), err); 329 } 330 catch (LinkageError err) { 331 throw new IllegalArgumentException("Unresolvable class definition for class [" + className + "]", err); 332 } 333 catch (ClassNotFoundException ex) { 334 throw new IllegalArgumentException("Could not find class [" + className + "]", ex); 335 } 336 } 337 338 /** 339 * Determine whether the {@link Class} identified by the supplied name is present 340 * and can be loaded. Will return {@code false} if either the class or 341 * one of its dependencies is not present or cannot be loaded. 342 * @param className the name of the class to check 343 * @param classLoader the class loader to use 344 * (may be {@code null} which indicates the default class loader) 345 * @return whether the specified class is present (including all of its 346 * superclasses and interfaces) 347 * @throws IllegalStateException if the corresponding class is resolvable but 348 * there was a readability mismatch in the inheritance hierarchy of the class 349 * (typically a missing dependency declaration in a Jigsaw module definition 350 * for a superclass or interface implemented by the class to be checked here) 351 */ 352 public static boolean isPresent(String className, @Nullable ClassLoader classLoader) { 353 try { 354 forName(className, classLoader); 355 return true; 356 } 357 catch (IllegalAccessError err) { 358 throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + 359 className + "]: " + err.getMessage(), err); 360 } 361 catch (Throwable ex) { 362 // Typically ClassNotFoundException or NoClassDefFoundError... 363 return false; 364 } 365 } 366 367 /** 368 * Check whether the given class is visible in the given ClassLoader. 369 * @param clazz the class to check (typically an interface) 370 * @param classLoader the ClassLoader to check against 371 * (may be {@code null} in which case this method will always return {@code true}) 372 */ 373 public static boolean isVisible(Class<?> clazz, @Nullable ClassLoader classLoader) { 374 if (classLoader == null) { 375 return true; 376 } 377 try { 378 if (clazz.getClassLoader() == classLoader) { 379 return true; 380 } 381 } 382 catch (SecurityException ex) { 383 // Fall through to loadable check below 384 } 385 386 // Visible if same Class can be loaded from given ClassLoader 387 return isLoadable(clazz, classLoader); 388 } 389 390 /** 391 * Check whether the given class is cache-safe in the given context, 392 * i.e. whether it is loaded by the given ClassLoader or a parent of it. 393 * @param clazz the class to analyze 394 * @param classLoader the ClassLoader to potentially cache metadata in 395 * (may be {@code null} which indicates the system class loader) 396 */ 397 public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) { 398 Assert.notNull(clazz, "Class must not be null"); 399 try { 400 ClassLoader target = clazz.getClassLoader(); 401 // Common cases 402 if (target == classLoader || target == null) { 403 return true; 404 } 405 if (classLoader == null) { 406 return false; 407 } 408 // Check for match in ancestors -> positive 409 ClassLoader current = classLoader; 410 while (current != null) { 411 current = current.getParent(); 412 if (current == target) { 413 return true; 414 } 415 } 416 // Check for match in children -> negative 417 while (target != null) { 418 target = target.getParent(); 419 if (target == classLoader) { 420 return false; 421 } 422 } 423 } 424 catch (SecurityException ex) { 425 // Fall through to loadable check below 426 } 427 428 // Fallback for ClassLoaders without parent/child relationship: 429 // safe if same Class can be loaded from given ClassLoader 430 return (classLoader != null && isLoadable(clazz, classLoader)); 431 } 432 433 /** 434 * Check whether the given class is loadable in the given ClassLoader. 435 * @param clazz the class to check (typically an interface) 436 * @param classLoader the ClassLoader to check against 437 * @since 5.0.6 438 */ 439 private static boolean isLoadable(Class<?> clazz, ClassLoader classLoader) { 440 try { 441 return (clazz == classLoader.loadClass(clazz.getName())); 442 // Else: different class with same name found 443 } 444 catch (ClassNotFoundException ex) { 445 // No corresponding class found at all 446 return false; 447 } 448 } 449 450 /** 451 * Resolve the given class name as primitive class, if appropriate, 452 * according to the JVM's naming rules for primitive classes. 453 * <p>Also supports the JVM's internal class names for primitive arrays. 454 * Does <i>not</i> support the "[]" suffix notation for primitive arrays; 455 * this is only supported by {@link #forName(String, ClassLoader)}. 456 * @param name the name of the potentially primitive class 457 * @return the primitive class, or {@code null} if the name does not denote 458 * a primitive class or primitive array class 459 */ 460 @Nullable 461 public static Class<?> resolvePrimitiveClassName(@Nullable String name) { 462 Class<?> result = null; 463 // Most class names will be quite long, considering that they 464 // SHOULD sit in a package, so a length check is worthwhile. 465 if (name != null && name.length() <= 7) { 466 // Could be a primitive - likely. 467 result = primitiveTypeNameMap.get(name); 468 } 469 return result; 470 } 471 472 /** 473 * Check if the given class represents a primitive wrapper, 474 * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, Double, or 475 * Void. 476 * @param clazz the class to check 477 * @return whether the given class is a primitive wrapper class 478 */ 479 public static boolean isPrimitiveWrapper(Class<?> clazz) { 480 Assert.notNull(clazz, "Class must not be null"); 481 return primitiveWrapperTypeMap.containsKey(clazz); 482 } 483 484 /** 485 * Check if the given class represents a primitive (i.e. boolean, byte, 486 * char, short, int, long, float, or double), {@code void}, or a wrapper for 487 * those types (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, 488 * Double, or Void). 489 * @param clazz the class to check 490 * @return {@code true} if the given class represents a primitive, void, or 491 * a wrapper class 492 */ 493 public static boolean isPrimitiveOrWrapper(Class<?> clazz) { 494 Assert.notNull(clazz, "Class must not be null"); 495 return (clazz.isPrimitive() || isPrimitiveWrapper(clazz)); 496 } 497 498 /** 499 * Check if the given class represents an array of primitives, 500 * i.e. boolean, byte, char, short, int, long, float, or double. 501 * @param clazz the class to check 502 * @return whether the given class is a primitive array class 503 */ 504 public static boolean isPrimitiveArray(Class<?> clazz) { 505 Assert.notNull(clazz, "Class must not be null"); 506 return (clazz.isArray() && clazz.getComponentType().isPrimitive()); 507 } 508 509 /** 510 * Check if the given class represents an array of primitive wrappers, 511 * i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double. 512 * @param clazz the class to check 513 * @return whether the given class is a primitive wrapper array class 514 */ 515 public static boolean isPrimitiveWrapperArray(Class<?> clazz) { 516 Assert.notNull(clazz, "Class must not be null"); 517 return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType())); 518 } 519 520 /** 521 * Resolve the given class if it is a primitive class, 522 * returning the corresponding primitive wrapper type instead. 523 * @param clazz the class to check 524 * @return the original class, or a primitive wrapper for the original primitive type 525 */ 526 public static Class<?> resolvePrimitiveIfNecessary(Class<?> clazz) { 527 Assert.notNull(clazz, "Class must not be null"); 528 return (clazz.isPrimitive() && clazz != void.class ? primitiveTypeToWrapperMap.get(clazz) : clazz); 529 } 530 531 /** 532 * Check if the right-hand side type may be assigned to the left-hand side 533 * type, assuming setting by reflection. Considers primitive wrapper 534 * classes as assignable to the corresponding primitive types. 535 * @param lhsType the target type 536 * @param rhsType the value type that should be assigned to the target type 537 * @return if the target type is assignable from the value type 538 * @see TypeUtils#isAssignable(java.lang.reflect.Type, java.lang.reflect.Type) 539 */ 540 public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) { 541 Assert.notNull(lhsType, "Left-hand side type must not be null"); 542 Assert.notNull(rhsType, "Right-hand side type must not be null"); 543 if (lhsType.isAssignableFrom(rhsType)) { 544 return true; 545 } 546 if (lhsType.isPrimitive()) { 547 Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType); 548 return (lhsType == resolvedPrimitive); 549 } 550 else { 551 Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType); 552 return (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)); 553 } 554 } 555 556 /** 557 * Determine if the given type is assignable from the given value, 558 * assuming setting by reflection. Considers primitive wrapper classes 559 * as assignable to the corresponding primitive types. 560 * @param type the target type 561 * @param value the value that should be assigned to the type 562 * @return if the type is assignable from the value 563 */ 564 public static boolean isAssignableValue(Class<?> type, @Nullable Object value) { 565 Assert.notNull(type, "Type must not be null"); 566 return (value != null ? isAssignable(type, value.getClass()) : !type.isPrimitive()); 567 } 568 569 /** 570 * Convert a "/"-based resource path to a "."-based fully qualified class name. 571 * @param resourcePath the resource path pointing to a class 572 * @return the corresponding fully qualified class name 573 */ 574 public static String convertResourcePathToClassName(String resourcePath) { 575 Assert.notNull(resourcePath, "Resource path must not be null"); 576 return resourcePath.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR); 577 } 578 579 /** 580 * Convert a "."-based fully qualified class name to a "/"-based resource path. 581 * @param className the fully qualified class name 582 * @return the corresponding resource path, pointing to the class 583 */ 584 public static String convertClassNameToResourcePath(String className) { 585 Assert.notNull(className, "Class name must not be null"); 586 return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR); 587 } 588 589 /** 590 * Return a path suitable for use with {@code ClassLoader.getResource} 591 * (also suitable for use with {@code Class.getResource} by prepending a 592 * slash ('/') to the return value). Built by taking the package of the specified 593 * class file, converting all dots ('.') to slashes ('/'), adding a trailing slash 594 * if necessary, and concatenating the specified resource name to this. 595 * <br/>As such, this function may be used to build a path suitable for 596 * loading a resource file that is in the same package as a class file, 597 * although {@link org.springframework.core.io.ClassPathResource} is usually 598 * even more convenient. 599 * @param clazz the Class whose package will be used as the base 600 * @param resourceName the resource name to append. A leading slash is optional. 601 * @return the built-up resource path 602 * @see ClassLoader#getResource 603 * @see Class#getResource 604 */ 605 public static String addResourcePathToPackagePath(Class<?> clazz, String resourceName) { 606 Assert.notNull(resourceName, "Resource name must not be null"); 607 if (!resourceName.startsWith("/")) { 608 return classPackageAsResourcePath(clazz) + '/' + resourceName; 609 } 610 return classPackageAsResourcePath(clazz) + resourceName; 611 } 612 613 /** 614 * Given an input class object, return a string which consists of the 615 * class's package name as a pathname, i.e., all dots ('.') are replaced by 616 * slashes ('/'). Neither a leading nor trailing slash is added. The result 617 * could be concatenated with a slash and the name of a resource and fed 618 * directly to {@code ClassLoader.getResource()}. For it to be fed to 619 * {@code Class.getResource} instead, a leading slash would also have 620 * to be prepended to the returned value. 621 * @param clazz the input class. A {@code null} value or the default 622 * (empty) package will result in an empty string ("") being returned. 623 * @return a path which represents the package name 624 * @see ClassLoader#getResource 625 * @see Class#getResource 626 */ 627 public static String classPackageAsResourcePath(@Nullable Class<?> clazz) { 628 if (clazz == null) { 629 return ""; 630 } 631 String className = clazz.getName(); 632 int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR); 633 if (packageEndIndex == -1) { 634 return ""; 635 } 636 String packageName = className.substring(0, packageEndIndex); 637 return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR); 638 } 639 640 /** 641 * Build a String that consists of the names of the classes/interfaces 642 * in the given array. 643 * <p>Basically like {@code AbstractCollection.toString()}, but stripping 644 * the "class "/"interface " prefix before every class name. 645 * @param classes an array of Class objects 646 * @return a String of form "[com.foo.Bar, com.foo.Baz]" 647 * @see java.util.AbstractCollection#toString() 648 */ 649 public static String classNamesToString(Class<?>... classes) { 650 return classNamesToString(Arrays.asList(classes)); 651 } 652 653 /** 654 * Build a String that consists of the names of the classes/interfaces 655 * in the given collection. 656 * <p>Basically like {@code AbstractCollection.toString()}, but stripping 657 * the "class "/"interface " prefix before every class name. 658 * @param classes a Collection of Class objects (may be {@code null}) 659 * @return a String of form "[com.foo.Bar, com.foo.Baz]" 660 * @see java.util.AbstractCollection#toString() 661 */ 662 public static String classNamesToString(@Nullable Collection<Class<?>> classes) { 663 if (CollectionUtils.isEmpty(classes)) { 664 return "[]"; 665 } 666 StringJoiner stringJoiner = new StringJoiner(", ", "[", "]"); 667 for (Class<?> clazz : classes) { 668 stringJoiner.add(clazz.getName()); 669 } 670 return stringJoiner.toString(); 671 } 672 673 /** 674 * Copy the given {@code Collection} into a {@code Class} array. 675 * <p>The {@code Collection} must contain {@code Class} elements only. 676 * @param collection the {@code Collection} to copy 677 * @return the {@code Class} array 678 * @since 3.1 679 * @see StringUtils#toStringArray 680 */ 681 public static Class<?>[] toClassArray(@Nullable Collection<Class<?>> collection) { 682 return (!CollectionUtils.isEmpty(collection) ? collection.toArray(EMPTY_CLASS_ARRAY) : EMPTY_CLASS_ARRAY); 683 } 684 685 /** 686 * Return all interfaces that the given instance implements as an array, 687 * including ones implemented by superclasses. 688 * @param instance the instance to analyze for interfaces 689 * @return all interfaces that the given instance implements as an array 690 */ 691 public static Class<?>[] getAllInterfaces(Object instance) { 692 Assert.notNull(instance, "Instance must not be null"); 693 return getAllInterfacesForClass(instance.getClass()); 694 } 695 696 /** 697 * Return all interfaces that the given class implements as an array, 698 * including ones implemented by superclasses. 699 * <p>If the class itself is an interface, it gets returned as sole interface. 700 * @param clazz the class to analyze for interfaces 701 * @return all interfaces that the given object implements as an array 702 */ 703 public static Class<?>[] getAllInterfacesForClass(Class<?> clazz) { 704 return getAllInterfacesForClass(clazz, null); 705 } 706 707 /** 708 * Return all interfaces that the given class implements as an array, 709 * including ones implemented by superclasses. 710 * <p>If the class itself is an interface, it gets returned as sole interface. 711 * @param clazz the class to analyze for interfaces 712 * @param classLoader the ClassLoader that the interfaces need to be visible in 713 * (may be {@code null} when accepting all declared interfaces) 714 * @return all interfaces that the given object implements as an array 715 */ 716 public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, @Nullable ClassLoader classLoader) { 717 return toClassArray(getAllInterfacesForClassAsSet(clazz, classLoader)); 718 } 719 720 /** 721 * Return all interfaces that the given instance implements as a Set, 722 * including ones implemented by superclasses. 723 * @param instance the instance to analyze for interfaces 724 * @return all interfaces that the given instance implements as a Set 725 */ 726 public static Set<Class<?>> getAllInterfacesAsSet(Object instance) { 727 Assert.notNull(instance, "Instance must not be null"); 728 return getAllInterfacesForClassAsSet(instance.getClass()); 729 } 730 731 /** 732 * Return all interfaces that the given class implements as a Set, 733 * including ones implemented by superclasses. 734 * <p>If the class itself is an interface, it gets returned as sole interface. 735 * @param clazz the class to analyze for interfaces 736 * @return all interfaces that the given object implements as a Set 737 */ 738 public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz) { 739 return getAllInterfacesForClassAsSet(clazz, null); 740 } 741 742 /** 743 * Return all interfaces that the given class implements as a Set, 744 * including ones implemented by superclasses. 745 * <p>If the class itself is an interface, it gets returned as sole interface. 746 * @param clazz the class to analyze for interfaces 747 * @param classLoader the ClassLoader that the interfaces need to be visible in 748 * (may be {@code null} when accepting all declared interfaces) 749 * @return all interfaces that the given object implements as a Set 750 */ 751 public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) { 752 Assert.notNull(clazz, "Class must not be null"); 753 if (clazz.isInterface() && isVisible(clazz, classLoader)) { 754 return Collections.singleton(clazz); 755 } 756 Set<Class<?>> interfaces = new LinkedHashSet<>(); 757 Class<?> current = clazz; 758 while (current != null) { 759 Class<?>[] ifcs = current.getInterfaces(); 760 for (Class<?> ifc : ifcs) { 761 if (isVisible(ifc, classLoader)) { 762 interfaces.add(ifc); 763 } 764 } 765 current = current.getSuperclass(); 766 } 767 return interfaces; 768 } 769 770 /** 771 * Create a composite interface Class for the given interfaces, 772 * implementing the given interfaces in one single Class. 773 * <p>This implementation builds a JDK proxy class for the given interfaces. 774 * @param interfaces the interfaces to merge 775 * @param classLoader the ClassLoader to create the composite Class in 776 * @return the merged interface as Class 777 * @throws IllegalArgumentException if the specified interfaces expose 778 * conflicting method signatures (or a similar constraint is violated) 779 * @see java.lang.reflect.Proxy#getProxyClass 780 */ 781 @SuppressWarnings("deprecation") // on JDK 9 782 public static Class<?> createCompositeInterface(Class<?>[] interfaces, @Nullable ClassLoader classLoader) { 783 Assert.notEmpty(interfaces, "Interface array must not be empty"); 784 return Proxy.getProxyClass(classLoader, interfaces); 785 } 786 787 /** 788 * Determine the common ancestor of the given classes, if any. 789 * @param clazz1 the class to introspect 790 * @param clazz2 the other class to introspect 791 * @return the common ancestor (i.e. common superclass, one interface 792 * extending the other), or {@code null} if none found. If any of the 793 * given classes is {@code null}, the other class will be returned. 794 * @since 3.2.6 795 */ 796 @Nullable 797 public static Class<?> determineCommonAncestor(@Nullable Class<?> clazz1, @Nullable Class<?> clazz2) { 798 if (clazz1 == null) { 799 return clazz2; 800 } 801 if (clazz2 == null) { 802 return clazz1; 803 } 804 if (clazz1.isAssignableFrom(clazz2)) { 805 return clazz1; 806 } 807 if (clazz2.isAssignableFrom(clazz1)) { 808 return clazz2; 809 } 810 Class<?> ancestor = clazz1; 811 do { 812 ancestor = ancestor.getSuperclass(); 813 if (ancestor == null || Object.class == ancestor) { 814 return null; 815 } 816 } 817 while (!ancestor.isAssignableFrom(clazz2)); 818 return ancestor; 819 } 820 821 /** 822 * Determine whether the given interface is a common Java language interface: 823 * {@link Serializable}, {@link Externalizable}, {@link Closeable}, {@link AutoCloseable}, 824 * {@link Cloneable}, {@link Comparable} - all of which can be ignored when looking 825 * for 'primary' user-level interfaces. Common characteristics: no service-level 826 * operations, no bean property methods, no default methods. 827 * @param ifc the interface to check 828 * @since 5.0.3 829 */ 830 public static boolean isJavaLanguageInterface(Class<?> ifc) { 831 return javaLanguageInterfaces.contains(ifc); 832 } 833 834 /** 835 * Determine if the supplied class is an <em>inner class</em>, 836 * i.e. a non-static member of an enclosing class. 837 * @return {@code true} if the supplied class is an inner class 838 * @since 5.0.5 839 * @see Class#isMemberClass() 840 */ 841 public static boolean isInnerClass(Class<?> clazz) { 842 return (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())); 843 } 844 845 /** 846 * Check whether the given object is a CGLIB proxy. 847 * @param object the object to check 848 * @see #isCglibProxyClass(Class) 849 * @see org.springframework.aop.support.AopUtils#isCglibProxy(Object) 850 * @deprecated as of 5.2, in favor of custom (possibly narrower) checks 851 */ 852 @Deprecated 853 public static boolean isCglibProxy(Object object) { 854 return isCglibProxyClass(object.getClass()); 855 } 856 857 /** 858 * Check whether the specified class is a CGLIB-generated class. 859 * @param clazz the class to check 860 * @see #isCglibProxyClassName(String) 861 * @deprecated as of 5.2, in favor of custom (possibly narrower) checks 862 */ 863 @Deprecated 864 public static boolean isCglibProxyClass(@Nullable Class<?> clazz) { 865 return (clazz != null && isCglibProxyClassName(clazz.getName())); 866 } 867 868 /** 869 * Check whether the specified class name is a CGLIB-generated class. 870 * @param className the class name to check 871 * @deprecated as of 5.2, in favor of custom (possibly narrower) checks 872 */ 873 @Deprecated 874 public static boolean isCglibProxyClassName(@Nullable String className) { 875 return (className != null && className.contains(CGLIB_CLASS_SEPARATOR)); 876 } 877 878 /** 879 * Return the user-defined class for the given instance: usually simply 880 * the class of the given instance, but the original class in case of a 881 * CGLIB-generated subclass. 882 * @param instance the instance to check 883 * @return the user-defined class 884 */ 885 public static Class<?> getUserClass(Object instance) { 886 Assert.notNull(instance, "Instance must not be null"); 887 return getUserClass(instance.getClass()); 888 } 889 890 /** 891 * Return the user-defined class for the given class: usually simply the given 892 * class, but the original class in case of a CGLIB-generated subclass. 893 * @param clazz the class to check 894 * @return the user-defined class 895 */ 896 public static Class<?> getUserClass(Class<?> clazz) { 897 if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { 898 Class<?> superclass = clazz.getSuperclass(); 899 if (superclass != null && superclass != Object.class) { 900 return superclass; 901 } 902 } 903 return clazz; 904 } 905 906 /** 907 * Return a descriptive name for the given object's type: usually simply 908 * the class name, but component type class name + "[]" for arrays, 909 * and an appended list of implemented interfaces for JDK proxies. 910 * @param value the value to introspect 911 * @return the qualified name of the class 912 */ 913 @Nullable 914 public static String getDescriptiveType(@Nullable Object value) { 915 if (value == null) { 916 return null; 917 } 918 Class<?> clazz = value.getClass(); 919 if (Proxy.isProxyClass(clazz)) { 920 String prefix = clazz.getName() + " implementing "; 921 StringJoiner result = new StringJoiner(",", prefix, ""); 922 for (Class<?> ifc : clazz.getInterfaces()) { 923 result.add(ifc.getName()); 924 } 925 return result.toString(); 926 } 927 else { 928 return clazz.getTypeName(); 929 } 930 } 931 932 /** 933 * Check whether the given class matches the user-specified type name. 934 * @param clazz the class to check 935 * @param typeName the type name to match 936 */ 937 public static boolean matchesTypeName(Class<?> clazz, @Nullable String typeName) { 938 return (typeName != null && 939 (typeName.equals(clazz.getTypeName()) || typeName.equals(clazz.getSimpleName()))); 940 } 941 942 /** 943 * Get the class name without the qualified package name. 944 * @param className the className to get the short name for 945 * @return the class name of the class without the package name 946 * @throws IllegalArgumentException if the className is empty 947 */ 948 public static String getShortName(String className) { 949 Assert.hasLength(className, "Class name must not be empty"); 950 int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR); 951 int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR); 952 if (nameEndIndex == -1) { 953 nameEndIndex = className.length(); 954 } 955 String shortName = className.substring(lastDotIndex + 1, nameEndIndex); 956 shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR); 957 return shortName; 958 } 959 960 /** 961 * Get the class name without the qualified package name. 962 * @param clazz the class to get the short name for 963 * @return the class name of the class without the package name 964 */ 965 public static String getShortName(Class<?> clazz) { 966 return getShortName(getQualifiedName(clazz)); 967 } 968 969 /** 970 * Return the short string name of a Java class in uncapitalized JavaBeans 971 * property format. Strips the outer class name in case of an inner class. 972 * @param clazz the class 973 * @return the short name rendered in a standard JavaBeans property format 974 * @see java.beans.Introspector#decapitalize(String) 975 */ 976 public static String getShortNameAsProperty(Class<?> clazz) { 977 String shortName = getShortName(clazz); 978 int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR); 979 shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName); 980 return Introspector.decapitalize(shortName); 981 } 982 983 /** 984 * Determine the name of the class file, relative to the containing 985 * package: e.g. "String.class" 986 * @param clazz the class 987 * @return the file name of the ".class" file 988 */ 989 public static String getClassFileName(Class<?> clazz) { 990 Assert.notNull(clazz, "Class must not be null"); 991 String className = clazz.getName(); 992 int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR); 993 return className.substring(lastDotIndex + 1) + CLASS_FILE_SUFFIX; 994 } 995 996 /** 997 * Determine the name of the package of the given class, 998 * e.g. "java.lang" for the {@code java.lang.String} class. 999 * @param clazz the class 1000 * @return the package name, or the empty String if the class 1001 * is defined in the default package 1002 */ 1003 public static String getPackageName(Class<?> clazz) { 1004 Assert.notNull(clazz, "Class must not be null"); 1005 return getPackageName(clazz.getName()); 1006 } 1007 1008 /** 1009 * Determine the name of the package of the given fully-qualified class name, 1010 * e.g. "java.lang" for the {@code java.lang.String} class name. 1011 * @param fqClassName the fully-qualified class name 1012 * @return the package name, or the empty String if the class 1013 * is defined in the default package 1014 */ 1015 public static String getPackageName(String fqClassName) { 1016 Assert.notNull(fqClassName, "Class name must not be null"); 1017 int lastDotIndex = fqClassName.lastIndexOf(PACKAGE_SEPARATOR); 1018 return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : ""); 1019 } 1020 1021 /** 1022 * Return the qualified name of the given class: usually simply 1023 * the class name, but component type class name + "[]" for arrays. 1024 * @param clazz the class 1025 * @return the qualified name of the class 1026 */ 1027 public static String getQualifiedName(Class<?> clazz) { 1028 Assert.notNull(clazz, "Class must not be null"); 1029 return clazz.getTypeName(); 1030 } 1031 1032 /** 1033 * Return the qualified name of the given method, consisting of 1034 * fully qualified interface/class name + "." + method name. 1035 * @param method the method 1036 * @return the qualified name of the method 1037 */ 1038 public static String getQualifiedMethodName(Method method) { 1039 return getQualifiedMethodName(method, null); 1040 } 1041 1042 /** 1043 * Return the qualified name of the given method, consisting of 1044 * fully qualified interface/class name + "." + method name. 1045 * @param method the method 1046 * @param clazz the clazz that the method is being invoked on 1047 * (may be {@code null} to indicate the method's declaring class) 1048 * @return the qualified name of the method 1049 * @since 4.3.4 1050 */ 1051 public static String getQualifiedMethodName(Method method, @Nullable Class<?> clazz) { 1052 Assert.notNull(method, "Method must not be null"); 1053 return (clazz != null ? clazz : method.getDeclaringClass()).getName() + '.' + method.getName(); 1054 } 1055 1056 /** 1057 * Determine whether the given class has a public constructor with the given signature. 1058 * <p>Essentially translates {@code NoSuchMethodException} to "false". 1059 * @param clazz the clazz to analyze 1060 * @param paramTypes the parameter types of the method 1061 * @return whether the class has a corresponding constructor 1062 * @see Class#getConstructor 1063 */ 1064 public static boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes) { 1065 return (getConstructorIfAvailable(clazz, paramTypes) != null); 1066 } 1067 1068 /** 1069 * Determine whether the given class has a public constructor with the given signature, 1070 * and return it if available (else return {@code null}). 1071 * <p>Essentially translates {@code NoSuchMethodException} to {@code null}. 1072 * @param clazz the clazz to analyze 1073 * @param paramTypes the parameter types of the method 1074 * @return the constructor, or {@code null} if not found 1075 * @see Class#getConstructor 1076 */ 1077 @Nullable 1078 public static <T> Constructor<T> getConstructorIfAvailable(Class<T> clazz, Class<?>... paramTypes) { 1079 Assert.notNull(clazz, "Class must not be null"); 1080 try { 1081 return clazz.getConstructor(paramTypes); 1082 } 1083 catch (NoSuchMethodException ex) { 1084 return null; 1085 } 1086 } 1087 1088 /** 1089 * Determine whether the given class has a public method with the given signature. 1090 * @param clazz the clazz to analyze 1091 * @param method the method to look for 1092 * @return whether the class has a corresponding method 1093 * @since 5.2.3 1094 */ 1095 public static boolean hasMethod(Class<?> clazz, Method method) { 1096 Assert.notNull(clazz, "Class must not be null"); 1097 Assert.notNull(method, "Method must not be null"); 1098 if (clazz == method.getDeclaringClass()) { 1099 return true; 1100 } 1101 String methodName = method.getName(); 1102 Class<?>[] paramTypes = method.getParameterTypes(); 1103 return getMethodOrNull(clazz, methodName, paramTypes) != null; 1104 } 1105 1106 /** 1107 * Determine whether the given class has a public method with the given signature. 1108 * <p>Essentially translates {@code NoSuchMethodException} to "false". 1109 * @param clazz the clazz to analyze 1110 * @param methodName the name of the method 1111 * @param paramTypes the parameter types of the method 1112 * @return whether the class has a corresponding method 1113 * @see Class#getMethod 1114 */ 1115 public static boolean hasMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) { 1116 return (getMethodIfAvailable(clazz, methodName, paramTypes) != null); 1117 } 1118 1119 /** 1120 * Determine whether the given class has a public method with the given signature, 1121 * and return it if available (else throws an {@code IllegalStateException}). 1122 * <p>In case of any signature specified, only returns the method if there is a 1123 * unique candidate, i.e. a single public method with the specified name. 1124 * <p>Essentially translates {@code NoSuchMethodException} to {@code IllegalStateException}. 1125 * @param clazz the clazz to analyze 1126 * @param methodName the name of the method 1127 * @param paramTypes the parameter types of the method 1128 * (may be {@code null} to indicate any signature) 1129 * @return the method (never {@code null}) 1130 * @throws IllegalStateException if the method has not been found 1131 * @see Class#getMethod 1132 */ 1133 public static Method getMethod(Class<?> clazz, String methodName, @Nullable Class<?>... paramTypes) { 1134 Assert.notNull(clazz, "Class must not be null"); 1135 Assert.notNull(methodName, "Method name must not be null"); 1136 if (paramTypes != null) { 1137 try { 1138 return clazz.getMethod(methodName, paramTypes); 1139 } 1140 catch (NoSuchMethodException ex) { 1141 throw new IllegalStateException("Expected method not found: " + ex); 1142 } 1143 } 1144 else { 1145 Set<Method> candidates = findMethodCandidatesByName(clazz, methodName); 1146 if (candidates.size() == 1) { 1147 return candidates.iterator().next(); 1148 } 1149 else if (candidates.isEmpty()) { 1150 throw new IllegalStateException("Expected method not found: " + clazz.getName() + '.' + methodName); 1151 } 1152 else { 1153 throw new IllegalStateException("No unique method found: " + clazz.getName() + '.' + methodName); 1154 } 1155 } 1156 } 1157 1158 /** 1159 * Determine whether the given class has a public method with the given signature, 1160 * and return it if available (else return {@code null}). 1161 * <p>In case of any signature specified, only returns the method if there is a 1162 * unique candidate, i.e. a single public method with the specified name. 1163 * <p>Essentially translates {@code NoSuchMethodException} to {@code null}. 1164 * @param clazz the clazz to analyze 1165 * @param methodName the name of the method 1166 * @param paramTypes the parameter types of the method 1167 * (may be {@code null} to indicate any signature) 1168 * @return the method, or {@code null} if not found 1169 * @see Class#getMethod 1170 */ 1171 @Nullable 1172 public static Method getMethodIfAvailable(Class<?> clazz, String methodName, @Nullable Class<?>... paramTypes) { 1173 Assert.notNull(clazz, "Class must not be null"); 1174 Assert.notNull(methodName, "Method name must not be null"); 1175 if (paramTypes != null) { 1176 return getMethodOrNull(clazz, methodName, paramTypes); 1177 } 1178 else { 1179 Set<Method> candidates = findMethodCandidatesByName(clazz, methodName); 1180 if (candidates.size() == 1) { 1181 return candidates.iterator().next(); 1182 } 1183 return null; 1184 } 1185 } 1186 1187 /** 1188 * Return the number of methods with a given name (with any argument types), 1189 * for the given class and/or its superclasses. Includes non-public methods. 1190 * @param clazz the clazz to check 1191 * @param methodName the name of the method 1192 * @return the number of methods with the given name 1193 */ 1194 public static int getMethodCountForName(Class<?> clazz, String methodName) { 1195 Assert.notNull(clazz, "Class must not be null"); 1196 Assert.notNull(methodName, "Method name must not be null"); 1197 int count = 0; 1198 Method[] declaredMethods = clazz.getDeclaredMethods(); 1199 for (Method method : declaredMethods) { 1200 if (methodName.equals(method.getName())) { 1201 count++; 1202 } 1203 } 1204 Class<?>[] ifcs = clazz.getInterfaces(); 1205 for (Class<?> ifc : ifcs) { 1206 count += getMethodCountForName(ifc, methodName); 1207 } 1208 if (clazz.getSuperclass() != null) { 1209 count += getMethodCountForName(clazz.getSuperclass(), methodName); 1210 } 1211 return count; 1212 } 1213 1214 /** 1215 * Does the given class or one of its superclasses at least have one or more 1216 * methods with the supplied name (with any argument types)? 1217 * Includes non-public methods. 1218 * @param clazz the clazz to check 1219 * @param methodName the name of the method 1220 * @return whether there is at least one method with the given name 1221 */ 1222 public static boolean hasAtLeastOneMethodWithName(Class<?> clazz, String methodName) { 1223 Assert.notNull(clazz, "Class must not be null"); 1224 Assert.notNull(methodName, "Method name must not be null"); 1225 Method[] declaredMethods = clazz.getDeclaredMethods(); 1226 for (Method method : declaredMethods) { 1227 if (method.getName().equals(methodName)) { 1228 return true; 1229 } 1230 } 1231 Class<?>[] ifcs = clazz.getInterfaces(); 1232 for (Class<?> ifc : ifcs) { 1233 if (hasAtLeastOneMethodWithName(ifc, methodName)) { 1234 return true; 1235 } 1236 } 1237 return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName)); 1238 } 1239 1240 /** 1241 * Given a method, which may come from an interface, and a target class used 1242 * in the current reflective invocation, find the corresponding target method 1243 * if there is one. E.g. the method may be {@code IFoo.bar()} and the 1244 * target class may be {@code DefaultFoo}. In this case, the method may be 1245 * {@code DefaultFoo.bar()}. This enables attributes on that method to be found. 1246 * <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod}, 1247 * this method does <i>not</i> resolve Java 5 bridge methods automatically. 1248 * Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod} 1249 * if bridge method resolution is desirable (e.g. for obtaining metadata from 1250 * the original method definition). 1251 * <p><b>NOTE:</b> Since Spring 3.1.1, if Java security settings disallow reflective 1252 * access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation 1253 * will fall back to returning the originally provided method. 1254 * @param method the method to be invoked, which may come from an interface 1255 * @param targetClass the target class for the current invocation 1256 * (may be {@code null} or may not even implement the method) 1257 * @return the specific target method, or the original method if the 1258 * {@code targetClass} does not implement it 1259 * @see #getInterfaceMethodIfPossible 1260 */ 1261 public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) { 1262 if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) { 1263 try { 1264 if (Modifier.isPublic(method.getModifiers())) { 1265 try { 1266 return targetClass.getMethod(method.getName(), method.getParameterTypes()); 1267 } 1268 catch (NoSuchMethodException ex) { 1269 return method; 1270 } 1271 } 1272 else { 1273 Method specificMethod = 1274 ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes()); 1275 return (specificMethod != null ? specificMethod : method); 1276 } 1277 } 1278 catch (SecurityException ex) { 1279 // Security settings are disallowing reflective access; fall back to 'method' below. 1280 } 1281 } 1282 return method; 1283 } 1284 1285 /** 1286 * Determine a corresponding interface method for the given method handle, if possible. 1287 * <p>This is particularly useful for arriving at a public exported type on Jigsaw 1288 * which can be reflectively invoked without an illegal access warning. 1289 * @param method the method to be invoked, potentially from an implementation class 1290 * @return the corresponding interface method, or the original method if none found 1291 * @since 5.1 1292 * @see #getMostSpecificMethod 1293 */ 1294 public static Method getInterfaceMethodIfPossible(Method method) { 1295 if (!Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isInterface()) { 1296 return method; 1297 } 1298 return interfaceMethodCache.computeIfAbsent(method, key -> { 1299 Class<?> current = key.getDeclaringClass(); 1300 while (current != null && current != Object.class) { 1301 Class<?>[] ifcs = current.getInterfaces(); 1302 for (Class<?> ifc : ifcs) { 1303 try { 1304 return ifc.getMethod(key.getName(), key.getParameterTypes()); 1305 } 1306 catch (NoSuchMethodException ex) { 1307 // ignore 1308 } 1309 } 1310 current = current.getSuperclass(); 1311 } 1312 return key; 1313 }); 1314 } 1315 1316 /** 1317 * Determine whether the given method is declared by the user or at least pointing to 1318 * a user-declared method. 1319 * <p>Checks {@link Method#isSynthetic()} (for implementation methods) as well as the 1320 * {@code GroovyObject} interface (for interface methods; on an implementation class, 1321 * implementations of the {@code GroovyObject} methods will be marked as synthetic anyway). 1322 * Note that, despite being synthetic, bridge methods ({@link Method#isBridge()}) are considered 1323 * as user-level methods since they are eventually pointing to a user-declared generic method. 1324 * @param method the method to check 1325 * @return {@code true} if the method can be considered as user-declared; [@code false} otherwise 1326 */ 1327 public static boolean isUserLevelMethod(Method method) { 1328 Assert.notNull(method, "Method must not be null"); 1329 return (method.isBridge() || (!method.isSynthetic() && !isGroovyObjectMethod(method))); 1330 } 1331 1332 private static boolean isGroovyObjectMethod(Method method) { 1333 return method.getDeclaringClass().getName().equals("groovy.lang.GroovyObject"); 1334 } 1335 1336 /** 1337 * Determine whether the given method is overridable in the given target class. 1338 * @param method the method to check 1339 * @param targetClass the target class to check against 1340 */ 1341 private static boolean isOverridable(Method method, @Nullable Class<?> targetClass) { 1342 if (Modifier.isPrivate(method.getModifiers())) { 1343 return false; 1344 } 1345 if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) { 1346 return true; 1347 } 1348 return (targetClass == null || 1349 getPackageName(method.getDeclaringClass()).equals(getPackageName(targetClass))); 1350 } 1351 1352 /** 1353 * Return a public static method of a class. 1354 * @param clazz the class which defines the method 1355 * @param methodName the static method name 1356 * @param args the parameter types to the method 1357 * @return the static method, or {@code null} if no static method was found 1358 * @throws IllegalArgumentException if the method name is blank or the clazz is null 1359 */ 1360 @Nullable 1361 public static Method getStaticMethod(Class<?> clazz, String methodName, Class<?>... args) { 1362 Assert.notNull(clazz, "Class must not be null"); 1363 Assert.notNull(methodName, "Method name must not be null"); 1364 try { 1365 Method method = clazz.getMethod(methodName, args); 1366 return Modifier.isStatic(method.getModifiers()) ? method : null; 1367 } 1368 catch (NoSuchMethodException ex) { 1369 return null; 1370 } 1371 } 1372 1373 1374 @Nullable 1375 private static Method getMethodOrNull(Class<?> clazz, String methodName, Class<?>[] paramTypes) { 1376 try { 1377 return clazz.getMethod(methodName, paramTypes); 1378 } 1379 catch (NoSuchMethodException ex) { 1380 return null; 1381 } 1382 } 1383 1384 private static Set<Method> findMethodCandidatesByName(Class<?> clazz, String methodName) { 1385 Set<Method> candidates = new HashSet<>(1); 1386 Method[] methods = clazz.getMethods(); 1387 for (Method method : methods) { 1388 if (methodName.equals(method.getName())) { 1389 candidates.add(method); 1390 } 1391 } 1392 return candidates; 1393 } 1394 1395}