Skip to content

Commit

Permalink
Update documentation for @AspectJ argument name resolution algorithm
Browse files Browse the repository at this point in the history
Closes gh-30026
  • Loading branch information
sbrannen committed Mar 1, 2023
1 parent 9d28fe9 commit 244c979
Showing 1 changed file with 62 additions and 46 deletions.
108 changes: 62 additions & 46 deletions framework-docs/src/docs/asciidoc/core/core-aop.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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:

* 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:
+
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:

[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
Expand All @@ -1549,7 +1586,7 @@ following strategy to determine parameter names:
}
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.
+

[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
Expand All @@ -1562,12 +1599,12 @@ following strategy to determine parameter names:
}
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.
+
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
----
Expand All @@ -1580,7 +1617,7 @@ the join point object, the `argNames` attribute need not include it:
}
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.
+

[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
Expand All @@ -1593,13 +1630,13 @@ the join point object, the `argNames` attribute need not include it:
}
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.
+

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
----
Expand All @@ -1609,7 +1646,7 @@ the `argNames` attribute:
}
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.
+

[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
Expand All @@ -1620,27 +1657,6 @@ the `argNames` attribute:
----
<1> References the `publicMethod` named pointcut defined in <<aop-pointcuts-combining>>.

* 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
Expand Down

0 comments on commit 244c979

Please sign in to comment.