Class AopEligibility
Diagnosis, not policy. check(Class, Set) names every reason a class or one of its
annotated methods cannot be proxied, with a remedy for each; AopProxyResolver decides
what to do with them. Today it fails the load for FINAL_CLASS - nothing can subclass it,
so no proxy exists at all - and warns for the rest, which are single methods the proxy cannot
reach. That split follows Spring, which throws for a final class and ignores an annotation on a
method it cannot advise. Ignoring is not silence: every one is named in a warning, because an
annotation that quietly does nothing is what left @ExceptionCatch inert from 6.2.0 to
6.3.0. isProxyable(Method, Class) is the same rule set as a plain predicate, used to
decide what actually goes into the proxy. See issue #309.
private and static methods are not a capability gap but a usage error. The JVM
dispatches them with invokespecial and invokestatic, neither of which consults
the subclass, so no inheritance-based AOP framework can intercept them.
- Since:
- 6.3.0
- Author:
- wisdomme
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classA single reason a class cannot be proxied.private static enumThe five proxy-eligibility rules, unified per D-36. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final List<Class<? extends Annotation>> Annotations that request interception.private static final AtomicIntegerCounts how many times a rule's remediation text has been built, process-wide.private static final AopEligibility.Rule[]Cached once;AopEligibility.Rule.values()clones its backing array on every call. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic List<AopEligibility.Problem> Checks whether the given class can be proxied.(package private) static intTest-only read ofDESCRIBE_CALLS.Same asfindAopAnnotatedMethods(Class), but reads from a scan the caller already has instead of walking the hierarchy again.findAopAnnotatedMethods(Class<?> beanClass) Collects the methods that carry an AOP annotation on the method itself, walking the whole inheritance hierarchy.static List<Class<? extends Annotation>> The annotations that request interception.private static booleanisInaccessible(Method method, Class<?> beanClass) Whether the proxy could not even see the method: it is package-private and declared in a different package than the class the proxy extends.static booleanisProxyable(Method method, Class<?> beanClass) Whether an inheritance-based proxy generated forbeanClasscan intercept the given method - meaning it can both override it and reach the original throughsuper.private static booleanisShadowed(Method method, Class<?> beanClass) Whether a class between the bean and the method's declaring class already declares that exact name and parameter list, which makes the candidate unreachable throughsuper.
-
Field Details
-
AOP_ANNOTATIONS
Annotations that request interception. -
DESCRIBE_CALLS
Counts how many times a rule's remediation text has been built, process-wide.Exists only so
isProxyable(Method, Class)'s own test can prove it never reachesAopEligibility.Rule.describe(Method, Class): a counter that stays at zero across a call is stronger evidence than reading the method body, because it also catches a future edit that accidentally reintroduces the cost. Nothing in production code reads this value. -
RULES
Cached once;AopEligibility.Rule.values()clones its backing array on every call.
-
-
Constructor Details
-
AopEligibility
private AopEligibility()
-
-
Method Details
-
getAopAnnotations
The annotations that request interception.Exposed so that wiring code can prove every one of them is either served by an advisor or explicitly declared unavailable. Without that check, an annotation nobody wired up fails silently on beans that also carry an annotation that is wired. See issue #190.
- Returns:
- an unmodifiable view of the recognised AOP annotations
-
findAopAnnotatedMethods
Collects the methods that carry an AOP annotation on the method itself, walking the whole inheritance hierarchy.Class-level annotations are deliberately not represented here. Everything this method returns is fed to
check(Class, Set), which turns an unproxyable entry into a load failure - correct for a method the author explicitly named, wrong for one a class-level annotation happened to cover. Class-level coverage is resolved byAopProxyResolver.collectInterceptedMethodsinstead. See issue #309.Scans the class itself - callers on the startup path that already hold a
MethodScanshould callfindAopAnnotatedMethods(MethodScan)instead, so the hierarchy is not walked a second time (D-37).- Parameters:
beanClass- the class to scan- Returns:
- the methods carrying a method-level AOP annotation, subclass overrides first
-
findAopAnnotatedMethods
Same asfindAopAnnotatedMethods(Class), but reads from a scan the caller already has instead of walking the hierarchy again.AopProxyResolver.resolve(Class)builds oneMethodScanper resolve and hands it to this method, the intercepted-method collector, and the diagnostic pass, instead of each repeating the reflective hierarchy walkMethodScanalready carries the result of (D-37).- Parameters:
scan- the scan to read from- Returns:
- the methods carrying a method-level AOP annotation, subclass overrides first
-
check
Checks whether the given class can be proxied.- Parameters:
beanClass- the class to checkannotatedMethods- the method-level annotated methods, fromfindAopAnnotatedMethods(Class); may be empty when interception was requested at class level only- Returns:
- the blocking problems, empty if the class can be proxied
-
isProxyable
Whether an inheritance-based proxy generated forbeanClasscan intercept the given method - meaning it can both override it and reach the original throughsuper.Walks the same
AopEligibility.Ruleenum ascheck(Class, Set), which is what replaces the old javadoc sentence asking a human to keep the two in sync: adding a sixth rule fails to compile until it supplies bothviolates()anddescribe(), so the two callers cannot drift apart again. They exist separately because they serve opposite audiences. A method-level annotation is an explicit request, so an unproxyable one becomes a startup failure. A class-level annotation is a bulk request the author never vetted method by method, so the class-level path filters with this predicate instead of failing the whole module.Only
AopEligibility.Rule.violates(Method, Class)runs here - neverAopEligibility.Rule.describe(Method, Class), which builds the remediation string and is reserved forcheck(java.lang.Class<?>, java.util.Set<java.lang.reflect.Method>)'s reporting path. This method runs once per method per bean on the startup path, so it must cost nothing to call even for a method that fails every rule. See issue #309 and issue #317.The bean class is a parameter rather than being derived from the method because two of the five rules are relative to it. A method is not intrinsically unproxyable; it is unproxyable from a particular subclass. See issue #309.
- Parameters:
method- the method to test, may be nullbeanClass- the class the proxy will extend, may be null- Returns:
- true if an inheritance-based proxy can intercept it
-
isInaccessible
Whether the proxy could not even see the method: it is package-private and declared in a different package than the class the proxy extends.The generated proxy lives in the bean's package, so a package-private declaration from elsewhere is neither overridable nor reachable through
super. The test delegates toReflectionUtilrather than re-deriving it, becauseReflectionUtil.overridesdecides the same question when it collapses override slots. Two copies reading different sources would let the scan treat two declarations as one method while this treats them as different packages. -
isShadowed
Whether a class between the bean and the method's declaring class already declares that exact name and parameter list, which makes the candidate unreachable throughsuper.This is the erased half of a generic override. Given
Base<T>.take(T)andChild extends Base<String>overridingtake(String), the compiler emits a bridgeChild.take(Object).ReflectionUtil.getAllMethodsskips bridges and compares erased parameter types, sotake(String)does not collapsetake(Object)and both come back as separate methods - but only the first issuper-invokable. Bridge and synthetic declarations are deliberately included in the scan here, because they are exactly what does the shadowing.- Parameters:
method- the method to testbeanClass- the class the proxy will extend- Returns:
- true if a nearer declaration makes it unreachable through
super
-
describeInvocationCountForTesting
static int describeInvocationCountForTesting()Test-only read ofDESCRIBE_CALLS. Package-private and read-only: nothing outside this package, and nothing in production code at all, depends on its value.- Returns:
- how many times a rule's remediation text has been built since the JVM started
-