Class ReflectionUtil
Replaces hutool's ReflectUtil / AnnotationUtil.
- Since:
- 6.2.0
- Author:
- wisdomme
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionfindSlotOverriding(List<List<Method>> bucket, Method candidate) Finds the slot inbucketwhose declarations overridecandidate, ornullif none does.getAllFields(Class<?> clazz) Gets all fields of a class (including superclasses).getAllMethods(Class<?> clazz) Collects every method visible on the given class, walking the hierarchy and keeping only the most specific declaration of each overridable method.static <A extends Annotation>
AgetAnnotation(Class<?> clazz, Class<A> annotationClass) Gets an annotation on a class.static <A extends Annotation>
AgetAnnotation(Field field, Class<A> annotationClass) Gets an annotation on a field.getEffectiveCommandMappings(Class<?> clazz) The command mappings ofclazz, keyed by@CmdMappingformat: the one statement of which methods of a command executor are mappings.static FieldGets the specified field.static Field[]Gets all fields of a class (including superclasses).static ObjectgetFieldValue(Object obj, Field field) Gets a field's value.static ObjectgetFieldValue(Object obj, String fieldName) Gets a field's value.static Method[]getMethods(Class<?> clazz) Gets all methods of a class (including superclasses).static Method[]getMethods(Class<?> clazz, Predicate<Method> filter) Gets the methods that satisfy a condition.private static Class<?> getPrimitiveWrapper(Class<?> primitive) Gets the wrapper class corresponding to a primitive type.static booleanhasAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) Whether a class carries the specified annotation.static booleanhasAnnotation(Field field, Class<? extends Annotation> annotationClass) Whether a field carries the specified annotation.static <T> TInvokes a method.private static booleanisAssignable(Class<?> target, Class<?> source) Whether the type is assignable.static booleanisPackagePrivate(int modifiers) True for default (package-private) access: neitherpublic,protected, norprivate.static <T> TnewInstance(Class<T> clazz) Creates an instance (using the no-arg constructor).static <T> TnewInstance(Class<T> clazz, Object... params) Creates an instance (using a parameterized constructor).static <T> TnewInstanceIfPossible(Class<T> clazz) Creates an instance (using the no-arg constructor, private constructors included).static booleanWhethersuboverridessupper JLS 8.4.8.1.static StringpackageNameOf(Class<?> clazz) The class's package name, or the empty string for the unnamed package.static <A extends Annotation>
AresolveMethodOrClassAnnotation(Method method, Class<?> executorClass, Class<A> annotationType) ResolvesannotationTypefor a command mapping in three steps, most-derived first:method's own declaration;executorClass's own class-level declaration -- the concrete executor class actually dispatching, the same classPluginManager's load-time gate inspects throughexecutor.getClass()(WR-02); the class-level declaration of the class that declared the mapping in source. Neither@CmdCDnor@UsageLimitis@Inherited, so no step walks a class's ancestors.static <A extends Annotation>
AresolveMethodOrClassAnnotation(Method method, Class<A> annotationType) ResolvesannotationTypeformethodwithout a concrete executor class: the method's own declaration, then the class-level declaration on the class that declared the mapping in source.static voidsetFieldValue(Object obj, Field field, Object value) Sets a field's value.static voidsetFieldValue(Object obj, String fieldName, Object value) Sets a field's value.private static MethodsourceDeclarationOf(Method method) The declaration a module author wrote formethod: for an override generated byProxyFactory, the most derived non-proxy declaration in the proxy's superclass chain that the override overrides; otherwisemethoditself.
-
Constructor Details
-
ReflectionUtil
private ReflectionUtil()
-
-
Method Details
-
getAllFields
Gets all fields of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the field list
-
getFields
Gets all fields of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the field array
-
getFieldValue
Gets a field's value.- Parameters:
obj- the objectfield- the field- Returns:
- the field value
-
getFieldValue
Gets a field's value.- Parameters:
obj- the objectfieldName- the field name- Returns:
- the field value
-
setFieldValue
Sets a field's value.- Parameters:
obj- the objectfield- the fieldvalue- the value
-
setFieldValue
Sets a field's value.- Parameters:
obj- the objectfieldName- the field namevalue- the value
-
getField
Gets the specified field.- Parameters:
clazz- the classfieldName- the field name- Returns:
- the field, or
nullif not found
-
newInstance
Creates an instance (using the no-arg constructor).- Type Parameters:
T- the type- Parameters:
clazz- the class- Returns:
- the instance
-
newInstance
Creates an instance (using a parameterized constructor).- Type Parameters:
T- the type- Parameters:
clazz- the classparams- the constructor parameters- Returns:
- the instance
-
isAssignable
Whether the type is assignable. -
getPrimitiveWrapper
Gets the wrapper class corresponding to a primitive type. -
newInstanceIfPossible
Creates an instance (using the no-arg constructor, private constructors included).- Type Parameters:
T- the type- Parameters:
clazz- the class- Returns:
- the instance
-
getAnnotation
Gets an annotation on a class.- Type Parameters:
A- the annotation type- Parameters:
clazz- the classannotationClass- the annotation type- Returns:
- the annotation instance, or
nullif absent
-
getAnnotation
Gets an annotation on a field.- Type Parameters:
A- the annotation type- Parameters:
field- the fieldannotationClass- the annotation type- Returns:
- the annotation instance, or
nullif absent
-
hasAnnotation
Whether a class carries the specified annotation.- Parameters:
clazz- the classannotationClass- the annotation type- Returns:
- whether the annotation is present
-
hasAnnotation
Whether a field carries the specified annotation.- Parameters:
field- the fieldannotationClass- the annotation type- Returns:
- whether the annotation is present
-
resolveMethodOrClassAnnotation
public static <A extends Annotation> A resolveMethodOrClassAnnotation(Method method, Class<A> annotationType) ResolvesannotationTypeformethodwithout a concrete executor class: the method's own declaration, then the class-level declaration on the class that declared the mapping in source. Most-derived wins, the same precedenceSenderTypeValidatorapplies for@CmdTarget, and the precedent Spring's@Transactionaland Spring Security's@PreAuthorizeboth document for a class-versus-method conflict.Convenience delegate to
resolveMethodOrClassAnnotation(Method, Class, Class)withexecutorClassasnull; that overload's javadoc is the single statement of the rule, including how aProxyFactory-generated override is mapped back to the declaration its author wrote (case 36). Prefer the 3-argument overload whenever the dispatching executor's concrete class is known: only it sees a class-level annotation declared on a concrete subclass that inherits the mapping unoverridden (WR-02).- Type Parameters:
A- the annotation type- Parameters:
method- the matched command mapping methodannotationType- the annotation type to resolve- Returns:
- the method-level annotation if present, otherwise the class-level annotation of
the class that declared the mapping in source, or
nullif neither declares it - Since:
- 6.3.0
-
resolveMethodOrClassAnnotation
public static <A extends Annotation> A resolveMethodOrClassAnnotation(Method method, @Nullable Class<?> executorClass, Class<A> annotationType) ResolvesannotationTypefor a command mapping in three steps, most-derived first:method's own declaration;executorClass's own class-level declaration -- the concrete executor class actually dispatching, the same classPluginManager's load-time gate inspects throughexecutor.getClass()(WR-02);- the class-level declaration of the class that declared the mapping in source.
@CmdCDnor@UsageLimitis@Inherited, so no step walks a class's ancestors. Step 2 is what lets a class-level annotation on a concrete subclass govern a mapping it inherits unoverridden; step 3 is what lets a shared abstract base that declares both the mapping and the annotation govern every inheritor.Generated proxies (case 36). When
methodis an override generated byProxyFactory(its declaring class is a proxy, byProxyFactory.isProxyClass), step 3 does not read the generated class: it reads the class that declared the overridden method in source -- the most derived non-proxy declaration that the override overrides, byoverrides(Method, Method). The proxy copies only the proxied class's own type annotations and the intercepted methods' own annotations, so reading the generated class here would lose a base class's class-level annotation for exactly the mappings that carry an AOP annotation. Steps 1 and 2 are unchanged on a proxy (the copied method annotations and the copied type annotations of the proxied class), so the precedence is the same for proxied and ordinary executors, for inherited and for overridden mappings alike. An override declared in the concrete class is governed by that class, not by its base, with or without a proxy.Consumers.
CooldownValidator(whether a mapping is bound, its seconds, and the unresolved-binding failure),UsageLockValidator(the lock scope) andPluginManager's config-binding resolution at module load all call this method, so load-time resolution and dispatch-time enforcement share this one answer.- Type Parameters:
A- the annotation type- Parameters:
method- the matched command mapping method; may be aProxyFactoryoverrideexecutorClass- the concreteBaseCommandExecutorclass dispatching this command -- the same classPluginManager's load-time gate inspects -- ornullto skip step 2annotationType- the annotation type to resolve- Returns:
- the resolved annotation, or
nullif none of the three steps finds one - Since:
- 6.3.0
-
sourceDeclarationOf
The declaration a module author wrote formethod: for an override generated byProxyFactory, the most derived non-proxy declaration in the proxy's superclass chain that the override overrides; otherwisemethoditself.Proxy identity is read from
ProxyFactory.isProxyClass, its one source. Bridge and synthetic methods are skipped, as ingetAllMethods(Class). Nothing is cached: a static map keyed byMethodorClasswould pin module class loaders after the module unloads, and the walk only runs for a method declared by a proxy.- Parameters:
method- the method to map back, never null- Returns:
- the author's declaration, or
methodwhen it is not a proxy override or no declaration it overrides is found
-
invoke
Invokes a method.- Type Parameters:
T- the return type- Parameters:
obj- the objectmethod- the methodargs- the arguments- Returns:
- the method's return value
-
getMethods
Gets the methods that satisfy a condition.Delegates to
getAllMethods(Class)so callers get the same de-duplicated, bridge/synthetic-free view of the hierarchy - a rawgetDeclaredMethods()walk here would double-count an overridden method (or, once a bean is AOP-proxied, could return both the proxy's intercepted override and the original method as separate hits for the same logical method).- Parameters:
clazz- the classfilter- the filter condition- Returns:
- the method array
-
getMethods
Gets all methods of a class (including superclasses).- Parameters:
clazz- the class- Returns:
- the method array
-
getAllMethods
Collects every method visible on the given class, walking the hierarchy and keeping only the most specific declaration of each overridable method.Class.getDeclaredMethods()returns only the methods a class declares itself, andClass.getMethods()returns only public ones. Neither is right for annotation scanning on a bean that may be an AOP proxy: the proxy declares overrides only for intercepted methods, so scanning it directly loses every annotation on the rest (issue #190). Walking the hierarchy recovers them, and collapsing overrides keeps a callback from firing once per level when an override repeats its parent's annotation.Declarations are grouped into slots using
overrides(Method, Method), and each slot contributes exactly one entry: the most derived declaration, which is the one whose annotations a scanner should see. A slot remembers every declaration folded into it, not just that representative, because overriding is transitive (JLS 8.4.8.1): inx.A.m()(package-private) →x.B.m()(public) →y.C.m()(public), the leaf does not directly override the root - different packages - but does so through the middle declaration, and all three are one method. Testing a candidate only against the surviving representative would keep the root as a spurious second entry.privateandstaticmethods never participate in overriding, so a same-named, same-parameter declaration at another level always starts its own slot and both survive.Bridge and synthetic methods are skipped: they carry no author-written annotations and, being compiler artifacts, are never the method a scanner means to find.
Object's own methods are excluded.- Parameters:
clazz- the class to scan, may be null- Returns:
- the methods, subclass overrides first; empty if clazz is null
-
getEffectiveCommandMappings
The command mappings ofclazz, keyed by@CmdMappingformat: the one statement of which methods of a command executor are mappings.The rule: walk
getAllMethods(Class)-- subclass first, each overridden method collapsed to its most derived declaration (#190, so an AOP proxy, which declares overrides only for intercepted methods, keeps every mapping) -- and keep each method carrying@CmdMapping. The first method for a format wins, so a subclass's own mapping shadows a base mapping that uses the same format. Inherited, unoverridden mappings are included; an override that does not repeat@CmdMappingis not a mapping, because the most derived declaration is the one consulted.BaseCommandExecutorbuilds its dispatch table from this map, andCmdTargetCompositionchecks exactly these mappings at load (case 57), so the two cannot disagree about which inherited mappings exist. On a proxy class an intercepted mapping appears as the proxy's override;resolveMethodOrClassAnnotation(Method, Class, Class)maps such an override back to its author's declaration for class-level metadata (case 36).- Parameters:
clazz- the command class to scan, may be null- Returns:
- an unmodifiable map from format to mapping method, in discovery order; empty if
clazzis null - Since:
- 6.3.0
-
findSlotOverriding
Finds the slot inbucketwhose declarations overridecandidate, ornullif none does.Every declaration in a slot is tested, not just its representative - see
getAllMethods(Class)for why transitivity makes that necessary.- Parameters:
bucket- the slots sharingcandidate's name and parameter countcandidate- the declaration being placed- Returns:
- the slot
candidatebelongs to, or null if it opens a new one
-
overrides
Whethersuboverridessupper JLS 8.4.8.1.Overriding is directional and is not an equivalence relation, so it cannot be expressed as equality of any symmetric key - which is why this predicate exists rather than a comparison against a symmetric signature key. All of the following must hold:
- same name and same parameter types, in order;
sup's declaring class is a proper supertype ofsub's;- neither declaration is
privateorstatic-privatemethods are dispatched withinvokespecialand are not even inherited, andstaticmethods are hidden rather than overridden; - if
supis package-private, the two declaring classes are in the same package. Apublicorprotectedsupcarries no package condition.
Classobjects, so a generic declaration and an override that matches only its erasure are treated as distinct methods, even though a subsignature comparison - and javac - accept them as one:class GenBase<T> { public void take(T t) { } } // erases to take(Object) class GenChild extends GenBase<String> { @Override public void take(String t) { } }overrides(GenChild.take, GenBase.take)returnsfalsehere, and the two are reported as distinct methods even though javac accepts the@Override.There is deliberately no condition on
sub's own access: Java permits an override to widen access, and a package-private method widened topublicby a same-package subclass is a genuine override.Public so that
FinalContractValidatorshares this one implementation instead of re-deriving the rule - two independent copies are how the two consumers drifted apart in the first place (issue #190).Packages are compared by name, matching how the rest of this class treats them; two same-named packages defined by different class loaders are distinct runtime packages to the JVM, a distinction this check does not make.
- Parameters:
sub- the potentially overriding declaration, may be nullsup- the potentially overridden declaration, may be null- Returns:
- true if
suboverridessup; false if either is null
-
isPackagePrivate
public static boolean isPackagePrivate(int modifiers) True for default (package-private) access: neitherpublic,protected, norprivate.Public so that consumers deciding the same question share this implementation. See the note on
overrides(Method, Method)for why a second copy is a hazard rather than a convenience.- Parameters:
modifiers- the modifiers to test, fromMember#getModifiers()- Returns:
- true if the modifiers describe default access
-
packageNameOf
The class's package name, or the empty string for the unnamed package.Public for the same reason as
isPackagePrivate(int): package-private access is decided here and byAopEligibility, and the two must not answer differently.- Parameters:
clazz- the class to inspect- Returns:
- the package name, or the empty string
-