001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.reflect; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Method; 022import java.lang.reflect.Modifier; 023import java.lang.reflect.Type; 024import java.lang.reflect.TypeVariable; 025import java.util.Arrays; 026import java.util.ArrayList; 027import java.util.Iterator; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032 033import org.apache.commons.lang3.ArrayUtils; 034import org.apache.commons.lang3.ClassUtils; 035import org.apache.commons.lang3.ClassUtils.Interfaces; 036import org.apache.commons.lang3.Validate; 037 038/** 039 * <p>Utility reflection methods focused on {@link Method}s, originally from Commons BeanUtils. 040 * Differences from the BeanUtils version may be noted, especially where similar functionality 041 * already existed within Lang. 042 * </p> 043 * 044 * <h3>Known Limitations</h3> 045 * <h4>Accessing Public Methods In A Default Access Superclass</h4> 046 * <p>There is an issue when invoking {@code public} methods contained in a default access superclass on JREs prior to 1.4. 047 * Reflection locates these methods fine and correctly assigns them as {@code public}. 048 * However, an {@link IllegalAccessException} is thrown if the method is invoked.</p> 049 * 050 * <p>{@link MethodUtils} contains a workaround for this situation. 051 * It will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} on this method. 052 * If this call succeeds, then the method can be invoked as normal. 053 * This call will only succeed when the application has sufficient security privileges. 054 * If this call fails then the method may fail.</p> 055 * 056 * @since 2.5 057 * @version $Id: MethodUtils.java 1630277 2014-10-09 04:37:38Z ggregory $ 058 */ 059public class MethodUtils { 060 061 /** 062 * <p>{@link MethodUtils} instances should NOT be constructed in standard programming. 063 * Instead, the class should be used as 064 * {@code MethodUtils.getAccessibleMethod(method)}.</p> 065 * 066 * <p>This constructor is {@code public} to permit tools that require a JavaBean 067 * instance to operate.</p> 068 */ 069 public MethodUtils() { 070 super(); 071 } 072 073 /** 074 * <p>Invokes a named method without parameters.</p> 075 * 076 * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p> 077 * 078 * <p>This is a convenient wrapper for 079 * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}. 080 * </p> 081 * 082 * @param object invoke method on this object 083 * @param methodName get method with this name 084 * @return The value returned by the invoked method 085 * 086 * @throws NoSuchMethodException if there is no such accessible method 087 * @throws InvocationTargetException wraps an exception thrown by the method invoked 088 * @throws IllegalAccessException if the requested method is not accessible via reflection 089 * 090 * @since 3.4 091 */ 092 public static Object invokeMethod(final Object object, final String methodName) throws NoSuchMethodException, 093 IllegalAccessException, InvocationTargetException { 094 return invokeMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null); 095 } 096 097 /** 098 * <p>Invokes a named method whose parameter type matches the object type.</p> 099 * 100 * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p> 101 * 102 * <p>This method supports calls to methods taking primitive parameters 103 * via passing in wrapping classes. So, for example, a {@code Boolean} object 104 * would match a {@code boolean} primitive.</p> 105 * 106 * <p>This is a convenient wrapper for 107 * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}. 108 * </p> 109 * 110 * @param object invoke method on this object 111 * @param methodName get method with this name 112 * @param args use these arguments - treat null as empty array 113 * @return The value returned by the invoked method 114 * 115 * @throws NoSuchMethodException if there is no such accessible method 116 * @throws InvocationTargetException wraps an exception thrown by the method invoked 117 * @throws IllegalAccessException if the requested method is not accessible via reflection 118 */ 119 public static Object invokeMethod(final Object object, final String methodName, 120 Object... args) throws NoSuchMethodException, 121 IllegalAccessException, InvocationTargetException { 122 args = ArrayUtils.nullToEmpty(args); 123 final Class<?>[] parameterTypes = ClassUtils.toClass(args); 124 return invokeMethod(object, methodName, args, parameterTypes); 125 } 126 127 /** 128 * <p>Invokes a named method whose parameter type matches the object type.</p> 129 * 130 * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p> 131 * 132 * <p>This method supports calls to methods taking primitive parameters 133 * via passing in wrapping classes. So, for example, a {@code Boolean} object 134 * would match a {@code boolean} primitive.</p> 135 * 136 * @param object invoke method on this object 137 * @param methodName get method with this name 138 * @param args use these arguments - treat null as empty array 139 * @param parameterTypes match these parameters - treat null as empty array 140 * @return The value returned by the invoked method 141 * 142 * @throws NoSuchMethodException if there is no such accessible method 143 * @throws InvocationTargetException wraps an exception thrown by the method invoked 144 * @throws IllegalAccessException if the requested method is not accessible via reflection 145 */ 146 public static Object invokeMethod(final Object object, final String methodName, 147 Object[] args, Class<?>[] parameterTypes) 148 throws NoSuchMethodException, IllegalAccessException, 149 InvocationTargetException { 150 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 151 args = ArrayUtils.nullToEmpty(args); 152 final Method method = getMatchingAccessibleMethod(object.getClass(), 153 methodName, parameterTypes); 154 if (method == null) { 155 throw new NoSuchMethodException("No such accessible method: " 156 + methodName + "() on object: " 157 + object.getClass().getName()); 158 } 159 return method.invoke(object, args); 160 } 161 162 /** 163 * <p>Invokes a method whose parameter types match exactly the object 164 * types.</p> 165 * 166 * <p>This uses reflection to invoke the method obtained from a call to 167 * {@link #getAccessibleMethod}(Class,String,Class[])}.</p> 168 * 169 * @param object invoke method on this object 170 * @param methodName get method with this name 171 * @return The value returned by the invoked method 172 * 173 * @throws NoSuchMethodException if there is no such accessible method 174 * @throws InvocationTargetException wraps an exception thrown by the 175 * method invoked 176 * @throws IllegalAccessException if the requested method is not accessible 177 * via reflection 178 * 179 * @since 3.4 180 */ 181 public static Object invokeExactMethod(final Object object, final String methodName) throws NoSuchMethodException, 182 IllegalAccessException, InvocationTargetException { 183 return invokeExactMethod(object, methodName, ArrayUtils.EMPTY_OBJECT_ARRAY, null); 184 } 185 186 /** 187 * <p>Invokes a method with no parameters.</p> 188 * 189 * <p>This uses reflection to invoke the method obtained from a call to 190 * {@link #getAccessibleMethod}(Class,String,Class[])}.</p> 191 * 192 * @param object invoke method on this object 193 * @param methodName get method with this name 194 * @param args use these arguments - treat null as empty array 195 * @return The value returned by the invoked method 196 * 197 * @throws NoSuchMethodException if there is no such accessible method 198 * @throws InvocationTargetException wraps an exception thrown by the 199 * method invoked 200 * @throws IllegalAccessException if the requested method is not accessible 201 * via reflection 202 */ 203 public static Object invokeExactMethod(final Object object, final String methodName, 204 Object... args) throws NoSuchMethodException, 205 IllegalAccessException, InvocationTargetException { 206 args = ArrayUtils.nullToEmpty(args); 207 final Class<?>[] parameterTypes = ClassUtils.toClass(args); 208 return invokeExactMethod(object, methodName, args, parameterTypes); 209 } 210 211 /** 212 * <p>Invokes a method whose parameter types match exactly the parameter 213 * types given.</p> 214 * 215 * <p>This uses reflection to invoke the method obtained from a call to 216 * {@link #getAccessibleMethod(Class,String,Class[])}.</p> 217 * 218 * @param object invoke method on this object 219 * @param methodName get method with this name 220 * @param args use these arguments - treat null as empty array 221 * @param parameterTypes match these parameters - treat {@code null} as empty array 222 * @return The value returned by the invoked method 223 * 224 * @throws NoSuchMethodException if there is no such accessible method 225 * @throws InvocationTargetException wraps an exception thrown by the 226 * method invoked 227 * @throws IllegalAccessException if the requested method is not accessible 228 * via reflection 229 */ 230 public static Object invokeExactMethod(final Object object, final String methodName, 231 Object[] args, Class<?>[] parameterTypes) 232 throws NoSuchMethodException, IllegalAccessException, 233 InvocationTargetException { 234 args = ArrayUtils.nullToEmpty(args); 235 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 236 final Method method = getAccessibleMethod(object.getClass(), methodName, 237 parameterTypes); 238 if (method == null) { 239 throw new NoSuchMethodException("No such accessible method: " 240 + methodName + "() on object: " 241 + object.getClass().getName()); 242 } 243 return method.invoke(object, args); 244 } 245 246 /** 247 * <p>Invokes a {@code static} method whose parameter types match exactly the parameter 248 * types given.</p> 249 * 250 * <p>This uses reflection to invoke the method obtained from a call to 251 * {@link #getAccessibleMethod(Class, String, Class[])}.</p> 252 * 253 * @param cls invoke static method on this class 254 * @param methodName get method with this name 255 * @param args use these arguments - treat {@code null} as empty array 256 * @param parameterTypes match these parameters - treat {@code null} as empty array 257 * @return The value returned by the invoked method 258 * 259 * @throws NoSuchMethodException if there is no such accessible method 260 * @throws InvocationTargetException wraps an exception thrown by the 261 * method invoked 262 * @throws IllegalAccessException if the requested method is not accessible 263 * via reflection 264 */ 265 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, 266 Object[] args, Class<?>[] parameterTypes) 267 throws NoSuchMethodException, IllegalAccessException, 268 InvocationTargetException { 269 args = ArrayUtils.nullToEmpty(args); 270 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 271 final Method method = getAccessibleMethod(cls, methodName, parameterTypes); 272 if (method == null) { 273 throw new NoSuchMethodException("No such accessible method: " 274 + methodName + "() on class: " + cls.getName()); 275 } 276 return method.invoke(null, args); 277 } 278 279 /** 280 * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p> 281 * 282 * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p> 283 * 284 * <p>This method supports calls to methods taking primitive parameters 285 * via passing in wrapping classes. So, for example, a {@code Boolean} class 286 * would match a {@code boolean} primitive.</p> 287 * 288 * <p>This is a convenient wrapper for 289 * {@link #invokeStaticMethod(Class, String, Object[], Class[])}. 290 * </p> 291 * 292 * @param cls invoke static method on this class 293 * @param methodName get method with this name 294 * @param args use these arguments - treat {@code null} as empty array 295 * @return The value returned by the invoked method 296 * 297 * @throws NoSuchMethodException if there is no such accessible method 298 * @throws InvocationTargetException wraps an exception thrown by the 299 * method invoked 300 * @throws IllegalAccessException if the requested method is not accessible 301 * via reflection 302 */ 303 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, 304 Object... args) throws NoSuchMethodException, 305 IllegalAccessException, InvocationTargetException { 306 args = ArrayUtils.nullToEmpty(args); 307 final Class<?>[] parameterTypes = ClassUtils.toClass(args); 308 return invokeStaticMethod(cls, methodName, args, parameterTypes); 309 } 310 311 /** 312 * <p>Invokes a named {@code static} method whose parameter type matches the object type.</p> 313 * 314 * <p>This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.</p> 315 * 316 * <p>This method supports calls to methods taking primitive parameters 317 * via passing in wrapping classes. So, for example, a {@code Boolean} class 318 * would match a {@code boolean} primitive.</p> 319 * 320 * 321 * @param cls invoke static method on this class 322 * @param methodName get method with this name 323 * @param args use these arguments - treat {@code null} as empty array 324 * @param parameterTypes match these parameters - treat {@code null} as empty array 325 * @return The value returned by the invoked method 326 * 327 * @throws NoSuchMethodException if there is no such accessible method 328 * @throws InvocationTargetException wraps an exception thrown by the 329 * method invoked 330 * @throws IllegalAccessException if the requested method is not accessible 331 * via reflection 332 */ 333 public static Object invokeStaticMethod(final Class<?> cls, final String methodName, 334 Object[] args, Class<?>[] parameterTypes) 335 throws NoSuchMethodException, IllegalAccessException, 336 InvocationTargetException { 337 args = ArrayUtils.nullToEmpty(args); 338 parameterTypes = ArrayUtils.nullToEmpty(parameterTypes); 339 final Method method = getMatchingAccessibleMethod(cls, methodName, 340 parameterTypes); 341 if (method == null) { 342 throw new NoSuchMethodException("No such accessible method: " 343 + methodName + "() on class: " + cls.getName()); 344 } 345 return method.invoke(null, args); 346 } 347 348 /** 349 * <p>Invokes a {@code static} method whose parameter types match exactly the object 350 * types.</p> 351 * 352 * <p>This uses reflection to invoke the method obtained from a call to 353 * {@link #getAccessibleMethod(Class, String, Class[])}.</p> 354 * 355 * @param cls invoke static method on this class 356 * @param methodName get method with this name 357 * @param args use these arguments - treat {@code null} as empty array 358 * @return The value returned by the invoked method 359 * 360 * @throws NoSuchMethodException if there is no such accessible method 361 * @throws InvocationTargetException wraps an exception thrown by the 362 * method invoked 363 * @throws IllegalAccessException if the requested method is not accessible 364 * via reflection 365 */ 366 public static Object invokeExactStaticMethod(final Class<?> cls, final String methodName, 367 Object... args) throws NoSuchMethodException, 368 IllegalAccessException, InvocationTargetException { 369 args = ArrayUtils.nullToEmpty(args); 370 final Class<?>[] parameterTypes = ClassUtils.toClass(args); 371 return invokeExactStaticMethod(cls, methodName, args, parameterTypes); 372 } 373 374 /** 375 * <p>Returns an accessible method (that is, one that can be invoked via 376 * reflection) with given name and parameters. If no such method 377 * can be found, return {@code null}. 378 * This is just a convenience wrapper for 379 * {@link #getAccessibleMethod(Method)}.</p> 380 * 381 * @param cls get method from this class 382 * @param methodName get method with this name 383 * @param parameterTypes with these parameters types 384 * @return The accessible method 385 */ 386 public static Method getAccessibleMethod(final Class<?> cls, final String methodName, 387 final Class<?>... parameterTypes) { 388 try { 389 return getAccessibleMethod(cls.getMethod(methodName, 390 parameterTypes)); 391 } catch (final NoSuchMethodException e) { 392 return null; 393 } 394 } 395 396 /** 397 * <p>Returns an accessible method (that is, one that can be invoked via 398 * reflection) that implements the specified Method. If no such method 399 * can be found, return {@code null}.</p> 400 * 401 * @param method The method that we wish to call 402 * @return The accessible method 403 */ 404 public static Method getAccessibleMethod(Method method) { 405 if (!MemberUtils.isAccessible(method)) { 406 return null; 407 } 408 // If the declaring class is public, we are done 409 final Class<?> cls = method.getDeclaringClass(); 410 if (Modifier.isPublic(cls.getModifiers())) { 411 return method; 412 } 413 final String methodName = method.getName(); 414 final Class<?>[] parameterTypes = method.getParameterTypes(); 415 416 // Check the implemented interfaces and subinterfaces 417 method = getAccessibleMethodFromInterfaceNest(cls, methodName, 418 parameterTypes); 419 420 // Check the superclass chain 421 if (method == null) { 422 method = getAccessibleMethodFromSuperclass(cls, methodName, 423 parameterTypes); 424 } 425 return method; 426 } 427 428 /** 429 * <p>Returns an accessible method (that is, one that can be invoked via 430 * reflection) by scanning through the superclasses. If no such method 431 * can be found, return {@code null}.</p> 432 * 433 * @param cls Class to be checked 434 * @param methodName Method name of the method we wish to call 435 * @param parameterTypes The parameter type signatures 436 * @return the accessible method or {@code null} if not found 437 */ 438 private static Method getAccessibleMethodFromSuperclass(final Class<?> cls, 439 final String methodName, final Class<?>... parameterTypes) { 440 Class<?> parentClass = cls.getSuperclass(); 441 while (parentClass != null) { 442 if (Modifier.isPublic(parentClass.getModifiers())) { 443 try { 444 return parentClass.getMethod(methodName, parameterTypes); 445 } catch (final NoSuchMethodException e) { 446 return null; 447 } 448 } 449 parentClass = parentClass.getSuperclass(); 450 } 451 return null; 452 } 453 454 /** 455 * <p>Returns an accessible method (that is, one that can be invoked via 456 * reflection) that implements the specified method, by scanning through 457 * all implemented interfaces and subinterfaces. If no such method 458 * can be found, return {@code null}.</p> 459 * 460 * <p>There isn't any good reason why this method must be {@code private}. 461 * It is because there doesn't seem any reason why other classes should 462 * call this rather than the higher level methods.</p> 463 * 464 * @param cls Parent class for the interfaces to be checked 465 * @param methodName Method name of the method we wish to call 466 * @param parameterTypes The parameter type signatures 467 * @return the accessible method or {@code null} if not found 468 */ 469 private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls, 470 final String methodName, final Class<?>... parameterTypes) { 471 // Search up the superclass chain 472 for (; cls != null; cls = cls.getSuperclass()) { 473 474 // Check the implemented interfaces of the parent class 475 final Class<?>[] interfaces = cls.getInterfaces(); 476 for (int i = 0; i < interfaces.length; i++) { 477 // Is this interface public? 478 if (!Modifier.isPublic(interfaces[i].getModifiers())) { 479 continue; 480 } 481 // Does the method exist on this interface? 482 try { 483 return interfaces[i].getDeclaredMethod(methodName, 484 parameterTypes); 485 } catch (final NoSuchMethodException e) { // NOPMD 486 /* 487 * Swallow, if no method is found after the loop then this 488 * method returns null. 489 */ 490 } 491 // Recursively check our parent interfaces 492 final Method method = getAccessibleMethodFromInterfaceNest(interfaces[i], 493 methodName, parameterTypes); 494 if (method != null) { 495 return method; 496 } 497 } 498 } 499 return null; 500 } 501 502 /** 503 * <p>Finds an accessible method that matches the given name and has compatible parameters. 504 * Compatible parameters mean that every method parameter is assignable from 505 * the given parameters. 506 * In other words, it finds a method with the given name 507 * that will take the parameters given.</p> 508 * 509 * <p>This method is used by 510 * {@link 511 * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. 512 * </p> 513 * 514 * <p>This method can match primitive parameter by passing in wrapper classes. 515 * For example, a {@code Boolean} will match a primitive {@code boolean} 516 * parameter. 517 * </p> 518 * 519 * @param cls find method in this class 520 * @param methodName find method with this name 521 * @param parameterTypes find method with most compatible parameters 522 * @return The accessible method 523 */ 524 public static Method getMatchingAccessibleMethod(final Class<?> cls, 525 final String methodName, final Class<?>... parameterTypes) { 526 try { 527 final Method method = cls.getMethod(methodName, parameterTypes); 528 MemberUtils.setAccessibleWorkaround(method); 529 return method; 530 } catch (final NoSuchMethodException e) { // NOPMD - Swallow the exception 531 } 532 // search through all methods 533 Method bestMatch = null; 534 final Method[] methods = cls.getMethods(); 535 for (final Method method : methods) { 536 // compare name and parameters 537 if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) { 538 // get accessible version of method 539 final Method accessibleMethod = getAccessibleMethod(method); 540 if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareParameterTypes( 541 accessibleMethod.getParameterTypes(), 542 bestMatch.getParameterTypes(), 543 parameterTypes) < 0)) { 544 bestMatch = accessibleMethod; 545 } 546 } 547 } 548 if (bestMatch != null) { 549 MemberUtils.setAccessibleWorkaround(bestMatch); 550 } 551 return bestMatch; 552 } 553 554 /** 555 * Get the hierarchy of overridden methods down to {@code result} respecting generics. 556 * @param method lowest to consider 557 * @param interfacesBehavior whether to search interfaces, {@code null} {@code implies} false 558 * @return Set<Method> in ascending order from sub- to superclass 559 * @throws NullPointerException if the specified method is {@code null} 560 * @since 3.2 561 */ 562 public static Set<Method> getOverrideHierarchy(final Method method, final Interfaces interfacesBehavior) { 563 Validate.notNull(method); 564 final Set<Method> result = new LinkedHashSet<Method>(); 565 result.add(method); 566 567 final Class<?>[] parameterTypes = method.getParameterTypes(); 568 569 final Class<?> declaringClass = method.getDeclaringClass(); 570 571 final Iterator<Class<?>> hierarchy = ClassUtils.hierarchy(declaringClass, interfacesBehavior).iterator(); 572 //skip the declaring class :P 573 hierarchy.next(); 574 hierarchyTraversal: while (hierarchy.hasNext()) { 575 final Class<?> c = hierarchy.next(); 576 final Method m = getMatchingAccessibleMethod(c, method.getName(), parameterTypes); 577 if (m == null) { 578 continue; 579 } 580 if (Arrays.equals(m.getParameterTypes(), parameterTypes)) { 581 // matches without generics 582 result.add(m); 583 continue; 584 } 585 // necessary to get arguments every time in the case that we are including interfaces 586 final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(declaringClass, m.getDeclaringClass()); 587 for (int i = 0; i < parameterTypes.length; i++) { 588 final Type childType = TypeUtils.unrollVariables(typeArguments, method.getGenericParameterTypes()[i]); 589 final Type parentType = TypeUtils.unrollVariables(typeArguments, m.getGenericParameterTypes()[i]); 590 if (!TypeUtils.equals(childType, parentType)) { 591 continue hierarchyTraversal; 592 } 593 } 594 result.add(m); 595 } 596 return result; 597 } 598 599 /** 600 * Gets all methods of the given class that are annotated with the given annotation. 601 * @param cls 602 * the {@link Class} to query 603 * @param annotationCls 604 * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched 605 * @return an array of Methods (possibly empty). 606 * @throws IllegalArgumentException 607 * if the class or annotation are {@code null} 608 * @since 3.4 609 */ 610 public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { 611 final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls); 612 return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]); 613 } 614 615 /** 616 * Gets all methods of the given class that are annotated with the given annotation. 617 * @param cls 618 * the {@link Class} to query 619 * @param annotationCls 620 * the {@link Annotation} that must be present on a method to be matched 621 * @return a list of Methods (possibly empty). 622 * @throws IllegalArgumentException 623 * if the class or annotation are {@code null} 624 * @since 3.4 625 */ 626 public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) { 627 Validate.isTrue(cls != null, "The class must not be null"); 628 Validate.isTrue(annotationCls != null, "The annotation class must not be null"); 629 final Method[] allMethods = cls.getMethods(); 630 final List<Method> annotatedMethods = new ArrayList<Method>(); 631 for (final Method method : allMethods) { 632 if (method.getAnnotation(annotationCls) != null) { 633 annotatedMethods.add(method); 634 } 635 } 636 return annotatedMethods; 637 } 638 639}