Class AopProxyResolver
This replaces the former AopProxyBeanPostProcessor. Proxy creation happens
before the bean exists, so it cannot be a BeanPostProcessor: both of that
interface's callbacks take an already-constructed instance. The container asks this resolver
for a class, instantiates it through the normal constructor paths, and the resulting object is
the bean. There is no second instance.
- Since:
- 6.3.0
- Author:
- wisdomme
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static classApplies an advisor's interceptor only to the methods that advisor matches. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final List<AopAdvisor> private final AtomicLongBumped by every change to the advisor set or the unavailable-annotation map.private static final Loggerprivate final Map<Class<? extends Annotation>, AnnotationLookupCache<?>> Per-annotation-type caches this resolver owns for its own class-level lookups.Bean class to the class the container should instantiate for it.private final Map<Class<? extends Annotation>, String> Annotations the framework recognises but deliberately does not implement in this release, mapped to the reason. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidaddAdvisor(AopAdvisor advisor) Adds an advisor to be considered during proxy resolution.voidaddUnavailableAnnotation(Class<? extends Annotation> type, String reason) Declares an annotation as recognised but unavailable.private StringbuildMessage(Class<?> beanClass, List<AopEligibility.Problem> problems) Builds a message that names every blocking problem and its remedy.Collects methods matched by at least one advisor, across the whole inheritance hierarchy.Gets all registered advisors, ordered byAopAdvisor.getOrder().private voidMarks every memoized resolution as computed against a configuration that no longer holds.private StringlocateAnnotation(MethodScan scan, Class<? extends Annotation> type) Finds where an annotation appears on the class, or null if it does not.private AnnotationLookupCache<?> lookupCacheFor(Class<? extends Annotation> type) private Class<?> Stores a resolution, but only if the configuration it was computed against is still the current one.private voidThrows if the bean uses an annotation declared unavailable.booleanremoveAdvisor(AopAdvisor advisor) Removes an advisor.Class<?> Resolves the class the container should instantiate for the given bean class.voidVerifies that every recognised AOP annotation is accounted for.private voidWarns for a class-level annotation that ends up covering no method at all.
-
Field Details
-
LOGGER
-
resolvedClasses
Bean class to the class the container should instantiate for it.resolveis called fromSimpleContainer.createBean, which runs once per instantiation - so for a prototype-scoped bean it ran on everygetBean(), repeating three hierarchy scans, re-emitting the "annotation ignored" warning that the javadoc calls a startup warning, and asking ByteBuddy for a brand new proxy class each time. The answer depends only on the bean class and this resolver's advisors, so it is computed once. Spring caches generated proxy classes for the same reason.Only successful resolutions are stored. A refusal re-runs and re-throws, which costs nothing because it fails the module load anyway.
Instance-scoped, like the annotation caches: it holds
Classreferences and must die with the container that owns it rather than pin a module's classes for the life of the JVM. -
configGeneration
Bumped by every change to the advisor set or the unavailable-annotation map.Clearing the memo after a mutation closes only one of the two interleavings. The other is a resolve() that read the old configuration, was overtaken by a mutation and its clear, and then stored its now-stale answer - which nothing would clear again. A resolution is kept only if the configuration it was computed against is still current.
-
advisors
-
lookupCaches
Per-annotation-type caches this resolver owns for its own class-level lookups. Instance- scoped likeresolvedClasses: it holdsClassreferences and must die with the container that owns this resolver, not pin a module's classes for the life of the JVM.
-
-
Constructor Details
-
AopProxyResolver
public AopProxyResolver()
-
-
Method Details
-
validateAnnotationCoverage
public void validateAnnotationCoverage()Verifies that every recognised AOP annotation is accounted for.Each annotation must either have a registered advisor that can intercept it, or be declared unavailable so that beans using it are rejected outright. An annotation that is neither is the silent-failure case this framework is removing: beans carrying it would be proxied for their other annotations while this one quietly does nothing.
- Throws:
ContainerException- if any recognised annotation is neither served nor declared
-
addAdvisor
Adds an advisor to be considered during proxy resolution.- Parameters:
advisor- the advisor to add
-
removeAdvisor
Removes an advisor.- Parameters:
advisor- the advisor to remove- Returns:
- true if the advisor was removed
-
getAdvisors
Gets all registered advisors, ordered byAopAdvisor.getOrder().- Returns:
- a copy of the advisor list
-
resolve
Resolves the class the container should instantiate for the given bean class.- Parameters:
beanClass- the declared bean class- Returns:
- the same class when no interception is needed, otherwise a generated subclass
- Throws:
ContainerException- if the class requests interception but cannot be proxied
-
invalidate
private void invalidate()Marks every memoized resolution as computed against a configuration that no longer holds. -
memoize
Stores a resolution, but only if the configuration it was computed against is still the current one. Returns the resolution either way - it is correct for this call, just not necessarily for the next one. -
locateAnnotation
Finds where an annotation appears on the class, or null if it does not.The class-level half is routed through this resolver's own per-type
AnnotationLookupCache(D-38) - it used to callAopAdvisor.findClassLevelAnnotationOwnerdirectly on every method of every unavailable-annotation entry, uncached. Not the same instancePluginManager.wireAopshares between the advisor andExceptionInterceptor: an unavailable annotation type is typically one neither of those ever sees, so this resolver owns its own per-type caches rather than assuming the wiring seam already built one for it.- Returns:
- the class name for a type-level annotation,
class#methodfor a method-level one, or null
-
lookupCacheFor
-
collectInterceptedMethods
Collects methods matched by at least one advisor, across the whole inheritance hierarchy. -
warnAboutClassLevelAnnotationsCoveringNothing
Warns for a class-level annotation that ends up covering no method at all.Every other skip in this class is named; this one used to be the exception, and
@ExceptionCatch class ServiceImpl extends AbstractService {}is a shape authors write. The annotation is located with the same hierarchy walk everything else uses - testingbeanClass.isAnnotationPresentinstead missed an annotation inherited from an ancestor, which is the silent no-op this warning exists to remove.The reason is derived rather than assumed. Ancestor scope is only one way to cover nothing; a class whose every method is unproxyable, or whose only methods are the excluded signatures, gets there too, and telling that author to "redeclare the method here" would send them after methods already declared there.
-
buildMessage
Builds a message that names every blocking problem and its remedy.