From 244c97993b952859626ad4afab11fcbb3ba2e5a3 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 1 Mar 2023 17:06:38 +0100 Subject: [PATCH] Update documentation for @AspectJ argument name resolution algorithm Closes gh-30026 --- .../src/docs/asciidoc/core/core-aop.adoc | 106 ++++++++++-------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/framework-docs/src/docs/asciidoc/core/core-aop.adoc b/framework-docs/src/docs/asciidoc/core/core-aop.adoc index db01be6197..467072a52f 100644 --- a/framework-docs/src/docs/asciidoc/core/core-aop.adoc +++ b/framework-docs/src/docs/asciidoc/core/core-aop.adoc @@ -1526,17 +1526,54 @@ check the type of the elements. [[aop-ataspectj-advice-params-names]] ===== Determining Argument Names -The parameter binding in advice invocations relies on matching names used in pointcut -expressions to declared parameter names in advice and pointcut method signatures. -Parameter names are not available through Java reflection, so Spring AOP uses the -following strategy to determine parameter names: +Parameter binding in advice invocations relies on matching the names used in pointcut +expressions to the parameter names declared in advice and pointcut method signatures. + +NOTE: This section uses the terms _argument_ and _parameter_ interchangeably, since +AspectJ APIs refer to parameter names as argument names. + +Spring AOP uses the following `ParameterNameDiscoverer` implementations to determine +parameter names. Each discoverer will be given a chance to discover parameter names, and +the first successful discoverer wins. If none of the registered discoverers is capable +of determining parameter names, an `IllegalArgumentException` is thrown. + + +`KotlinReflectionParameterNameDiscoverer` :: Uses Kotlin reflection APIs if such APIs are + present on the classpath. +`StandardReflectionParameterNameDiscoverer` :: Uses the `java.lang.reflect.Parameter` API + available since Java 8. Requires that code be compiled with the `-parameters` flag for + `javac`. Recommended approach on Java 8+. +`LocalVariableTableParameterNameDiscoverer` :: Analyzes the local variable table available + in the byte code to determine parameter names from debug information. Requires that + code be compiled with debug symbols (`-g:vars` at a minimum). Deprecated as of Spring + Framework 6.0 for removal in Spring Framework 6.1 in favor of compiling code with + `-parameters`. Not supported in a GraalVM native image. +`AspectJAdviceParameterNameDiscoverer` :: Uses parameter names that have been explicitly + specified by the user via the `argNames` attribute in the corresponding advice or + pointcut annotation. See the following section for details. + +[[aop-ataspectj-advice-params-names-explicit]] +===== Explicit Argument Names + +@AspectJ advice and pointcut annotations have an optional `argNames` attribute that you +can use to specify the argument names of the annotated method. Note, however, that +explicit `argNames` will only be used by Spring as a fallback if none of the other +`ParameterNameDiscoverer` implementations is able to determine parameter names (see the +previous section for details). + +[TIP] +==== +If an @AspectJ aspect has been compiled by the AspectJ compiler (`ajc`) even without +debug information, you do not need to add the `argNames` attribute, since the compiler +retains the needed information. + +Similarly, if an @AspectJ aspect has been compiled with `javac` using the `-parameters` +flag, you do not need to add the `argNames` attribute, since the compiler retains the +needed information. +==== + +The following example shows how to use the `argNames` attribute: -* If the parameter names have been explicitly specified by the user, the specified - parameter names are used. Both the advice and the pointcut annotations have - an optional `argNames` attribute that you can use to specify the argument names of - the annotated method. These argument names are available at runtime. The following example - shows how to use the `argNames` attribute: -+ [source,java,indent=0,subs="verbatim",role="primary"] .Java ---- @@ -1549,7 +1586,7 @@ following strategy to determine parameter names: } ---- <1> References the `publicMethod` named pointcut defined in <>. -+ + [source,kotlin,indent=0,subs="verbatim",role="secondary"] .Kotlin ---- @@ -1562,12 +1599,12 @@ following strategy to determine parameter names: } ---- <1> References the `publicMethod` named pointcut defined in <>. -+ -If the first parameter is of the `JoinPoint`, `ProceedingJoinPoint`, or -`JoinPoint.StaticPart` type, you can leave out the name of the parameter from the value -of the `argNames` attribute. For example, if you modify the preceding advice to receive -the join point object, the `argNames` attribute need not include it: -+ + +If the first parameter is of type `JoinPoint`, `ProceedingJoinPoint`, or +`JoinPoint.StaticPart`, you can omit the name of the parameter from the value of the +`argNames` attribute. For example, if you modify the preceding advice to receive the join +point object, the `argNames` attribute does not need to include it: + [source,java,indent=0,subs="verbatim",role="primary"] .Java ---- @@ -1580,7 +1617,7 @@ the join point object, the `argNames` attribute need not include it: } ---- <1> References the `publicMethod` named pointcut defined in <>. -+ + [source,kotlin,indent=0,subs="verbatim",role="secondary"] .Kotlin ---- @@ -1593,13 +1630,13 @@ the join point object, the `argNames` attribute need not include it: } ---- <1> References the `publicMethod` named pointcut defined in <>. -+ + The special treatment given to the first parameter of the `JoinPoint`, `ProceedingJoinPoint`, and `JoinPoint.StaticPart` types is particularly convenient for -advice instances that do not collect any other join point context. In such situations, you may -omit the `argNames` attribute. For example, the following advice need not declare -the `argNames` attribute: -+ +advice instances that do not collect any other join point context. In such situations, +you may omit the `argNames` attribute. For example, the following advice does not need to +declare the `argNames` attribute: + [source,java,indent=0,subs="verbatim",role="primary"] .Java ---- @@ -1609,7 +1646,7 @@ the `argNames` attribute: } ---- <1> References the `publicMethod` named pointcut defined in <>. -+ + [source,kotlin,indent=0,subs="verbatim",role="secondary"] .Kotlin ---- @@ -1620,27 +1657,6 @@ the `argNames` attribute: ---- <1> References the `publicMethod` named pointcut defined in <>. -* Using the `argNames` attribute is a little clumsy, so if the `argNames` attribute - has not been specified, Spring AOP looks at the debug information for the - class and tries to determine the parameter names from the local variable table. This - information is present as long as the classes have been compiled with debug - information (`-g:vars` at a minimum). The consequences of compiling with this flag - on are: (1) your code is slightly easier to understand (reverse engineer), (2) - the class file sizes are very slightly bigger (typically inconsequential), (3) the - optimization to remove unused local variables is not applied by your compiler. In - other words, you should encounter no difficulties by building with this flag on. -+ -NOTE: If an @AspectJ aspect has been compiled by the AspectJ compiler (`ajc`) even -without the debug information, you need not add the `argNames` attribute, as the compiler -retains the needed information. - -* If the code has been compiled without the necessary debug information, Spring AOP - tries to deduce the pairing of binding variables to parameters (for example, if - only one variable is bound in the pointcut expression, and the advice method - takes only one parameter, the pairing is obvious). If the binding of variables is - ambiguous given the available information, an `AmbiguousBindingException` is - thrown. -* If all of the above strategies fail, an `IllegalArgumentException` is thrown. [[aop-ataspectj-advice-proceeding-with-the-call]] ===== Proceeding with Arguments