From 9095f1d584d16f9b50536e3a76c9628d0e4d0660 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 26 Jan 2022 17:32:07 +0100 Subject: [PATCH 1/2] Polish AspectJ documentation --- src/docs/asciidoc/core/core-aop.adoc | 50 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/docs/asciidoc/core/core-aop.adoc b/src/docs/asciidoc/core/core-aop.adoc index 6479f329b5d..7a539acff57 100644 --- a/src/docs/asciidoc/core/core-aop.adoc +++ b/src/docs/asciidoc/core/core-aop.adoc @@ -770,7 +770,7 @@ sub-packages: this(com.xyz.service.AccountService) ---- + -NOTE: 'this' is more commonly used in a binding form. See the section on <> +NOTE: `this` is more commonly used in a binding form. See the section on <> for how to make the proxy object available in the advice body. * Any join point (method execution only in Spring AOP) where the target object @@ -781,7 +781,7 @@ implements the `AccountService` interface: target(com.xyz.service.AccountService) ---- + -NOTE: 'target' is more commonly used in a binding form. See the <> section +NOTE: `target` is more commonly used in a binding form. See the <> section for how to make the target object available in the advice body. * Any join point (method execution only in Spring AOP) that takes a single parameter @@ -792,7 +792,7 @@ and where the argument passed at runtime is `Serializable`: args(java.io.Serializable) ---- + -NOTE: 'args' is more commonly used in a binding form. See the <> section +NOTE: `args` is more commonly used in a binding form. See the <> section for how to make the method arguments available in the advice body. + Note that the pointcut given in this example is different from `execution(* @@ -808,7 +808,7 @@ parameter of type `Serializable`. @target(org.springframework.transaction.annotation.Transactional) ---- + -NOTE: You can also use '@target' in a binding form. See the <> section for +NOTE: You can also use `@target` in a binding form. See the <> section for how to make the annotation object available in the advice body. * Any join point (method execution only in Spring AOP) where the declared type of the @@ -819,7 +819,7 @@ target object has an `@Transactional` annotation: @within(org.springframework.transaction.annotation.Transactional) ---- + -NOTE: You can also use '@within' in a binding form. See the <> section for +NOTE: You can also use `@within` in a binding form. See the <> section for how to make the annotation object available in the advice body. * Any join point (method execution only in Spring AOP) where the executing method has an @@ -830,7 +830,7 @@ how to make the annotation object available in the advice body. @annotation(org.springframework.transaction.annotation.Transactional) ---- + -NOTE: You can also use '@annotation' in a binding form. See the <> section +NOTE: You can also use `@annotation` in a binding form. See the <> section for how to make the annotation object available in the advice body. * Any join point (method execution only in Spring AOP) which takes a single parameter, @@ -841,7 +841,7 @@ and where the runtime type of the argument passed has the `@Classified` annotati @args(com.xyz.security.Classified) ---- + -NOTE: You can also use '@args' in a binding form. See the <> section +NOTE: You can also use `@args` in a binding form. See the <> section how to make the annotation object(s) available in the advice body. * Any join point (method execution only in Spring AOP) on a Spring bean named @@ -1213,7 +1213,7 @@ in contrast to `@AfterReturning` which only applies to successful normal returns [[aop-ataspectj-around-advice]] ==== Around Advice -The last kind of advice is around advice. Around advice runs "`around`" a matched +The last kind of advice is _around_ advice. Around advice runs "around" a matched method's execution. It has the opportunity to do work both before and after the method runs and to determine when, how, and even if the method actually gets to run at all. Around advice is often used if you need to share state before and after a method @@ -1301,8 +1301,9 @@ write generic advice that can find out about the method the advice is currently ===== Access to the Current `JoinPoint` Any advice method may declare, as its first parameter, a parameter of type -`org.aspectj.lang.JoinPoint` (note that around advice is required to declare a first +`org.aspectj.lang.JoinPoint`. Note that around advice is required to declare a first parameter of type `ProceedingJoinPoint`, which is a subclass of `JoinPoint`. + The `JoinPoint` interface provides a number of useful methods: * `getArgs()`: Returns the method arguments. @@ -1319,7 +1320,7 @@ See the https://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lan We have already seen how to bind the returned value or exception value (using after returning and after throwing advice). To make argument values available to the advice body, you can use the binding form of `args`. If you use a parameter name in place of a -type name in an args expression, the value of the corresponding argument is passed as +type name in an `args` expression, the value of the corresponding argument is passed as the parameter value when the advice is invoked. An example should make this clearer. Suppose you want to advise the execution of DAO operations that take an `Account` object as the first parameter, and you need access to the account in the advice body. @@ -1348,7 +1349,7 @@ parameter, and the argument passed to that parameter is an instance of `Account` Second, it makes the actual `Account` object available to the advice through the `account` parameter. -Another way of writing this is to declare a pointcut that "`provides`" the `Account` +Another way of writing this is to declare a pointcut that "provides" the `Account` object value when it matches a join point, and then refer to the named pointcut from the advice. This would look as follows: @@ -1376,13 +1377,12 @@ from the advice. This would look as follows: } ---- -See the AspectJ programming guide for more -details. +See the AspectJ programming guide for more details. -The proxy object ( `this`), target object ( `target`), and annotations ( `@within`, -`@target`, `@annotation`, and `@args`) can all be bound in a similar fashion. The next two -examples show how to match the execution of methods annotated with an -`@Auditable` annotation and extract the audit code: +The proxy object (`this`), target object (`target`), and annotations (`@within`, +`@target`, `@annotation`, and `@args`) can all be bound in a similar fashion. The next +two examples show how to match the execution of methods annotated with an `@Auditable` +annotation and extract the audit code: The first of the two examples shows the definition of the `@Auditable` annotation: @@ -1448,7 +1448,7 @@ you have a generic type like the following: ---- You can restrict interception of method types to certain parameter types by -typing the advice parameter to the parameter type for which you want to intercept the method: +tying the advice parameter to the parameter type for which you want to intercept the method: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java @@ -1575,18 +1575,18 @@ the `argNames` attribute: } ---- -* Using the `'argNames'` attribute is a little clumsy, so if the `'argNames'` attribute +* 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 + 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 + 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 +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 retain the needed information. * If the code has been compiled without the necessary debug information, Spring AOP @@ -2542,7 +2542,7 @@ With such a Boot class, we would get output similar to the following on standard [literal,subs="verbatim,quotes"] ---- -StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0 +StopWatch 'Profiling for 'Pengo' and '12': running time (millis) = 0 ----------------------------------------- ms % Task name ----------------------------------------- @@ -3540,7 +3540,7 @@ with references to beans defined in the child (servlet-specific) contexts by usi When deploying multiple web applications within the same container, ensure that each web application loads the types in `spring-aspects.jar` by using its own classloader -(for example, by placing `spring-aspects.jar` in `'WEB-INF/lib'`). If `spring-aspects.jar` +(for example, by placing `spring-aspects.jar` in `WEB-INF/lib`). If `spring-aspects.jar` is added only to the container-wide classpath (and hence loaded by the shared parent classloader), all web applications share the same aspect instance (which is probably not what you want). From 7f65e17ff2e728a2b1848da8db894f35fb09434f Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 26 Jan 2022 17:32:36 +0100 Subject: [PATCH 2/2] Improve documentation for implementing AspectJ around advice Closes gh-27980 --- src/docs/asciidoc/core/core-aop.adoc | 105 +++++++++++++++++---------- 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/src/docs/asciidoc/core/core-aop.adoc b/src/docs/asciidoc/core/core-aop.adoc index 7a539acff57..3901669e607 100644 --- a/src/docs/asciidoc/core/core-aop.adoc +++ b/src/docs/asciidoc/core/core-aop.adoc @@ -1217,29 +1217,56 @@ The last kind of advice is _around_ advice. Around advice runs "around" a matche method's execution. It has the opportunity to do work both before and after the method runs and to determine when, how, and even if the method actually gets to run at all. Around advice is often used if you need to share state before and after a method -execution in a thread-safe manner (starting and stopping a timer, for example). -Always use the least powerful form of advice that meets your requirements (that is, -do not use around advice if before advice would do). +execution in a thread-safe manner – for example, starting and stopping a timer. -Around advice is declared by using the `@Around` annotation. The first parameter of the -advice method must be of type `ProceedingJoinPoint`. Within the body of the advice, -calling `proceed()` on the `ProceedingJoinPoint` causes the underlying method to run. -The `proceed` method can also pass in an `Object[]`. The values in the array are used -as the arguments to the method execution when it proceeds. +[TIP] +==== +Always use the least powerful form of advice that meets your requirements. -NOTE: The behavior of `proceed` when called with an `Object[]` is a little different than -the behavior of `proceed` for around advice compiled by the AspectJ compiler. For around +For example, do not use _around_ advice if _before_ advice is sufficient for your needs. +==== + +Around advice is declared by annotating a method with the `@Around` annotation. The +method should declare `Object` as its return type, and the first parameter of the method +must be of type `ProceedingJoinPoint`. Within the body of the advice method, you must +invoke `proceed()` on the `ProceedingJoinPoint` in order for the underlying method to +run. Invoking `proceed()` without arguments will result in the caller's original +arguments being supplied to the underlying method when it is invoked. For advanced use +cases, there is an overloaded variant of the `proceed()` method which accepts an array of +arguments (`Object[]`). The values in the array will be used as the arguments to the +underlying method when it is invoked. + +[NOTE] +==== +The behavior of `proceed` when called with an `Object[]` is a little different than the +behavior of `proceed` for around advice compiled by the AspectJ compiler. For around advice written using the traditional AspectJ language, the number of arguments passed to `proceed` must match the number of arguments passed to the around advice (not the number of arguments taken by the underlying join point), and the value passed to proceed in a -given argument position supplants the original value at the join point for the entity -the value was bound to (do not worry if this does not make sense right now). The approach -taken by Spring is simpler and a better match to its proxy-based, execution-only -semantics. You only need to be aware of this difference if you compile @AspectJ -aspects written for Spring and use `proceed` with arguments with the AspectJ compiler -and weaver. There is a way to write such aspects that is 100% compatible across both -Spring AOP and AspectJ, and this is discussed in the -<>. +given argument position supplants the original value at the join point for the entity the +value was bound to (do not worry if this does not make sense right now). + +The approach taken by Spring is simpler and a better match to its proxy-based, +execution-only semantics. You only need to be aware of this difference if you compile +`@AspectJ` aspects written for Spring and use `proceed` with arguments with the AspectJ +compiler and weaver. There is a way to write such aspects that is 100% compatible across +both Spring AOP and AspectJ, and this is discussed in the +<>. +==== + +The value returned by the around advice is the return value seen by the caller of the +method. For example, a simple caching aspect could return a value from a cache if it has +one or invoke `proceed()` (and return that value) if it does not. Note that `proceed` +may be invoked once, many times, or not at all within the body of the around advice. All +of these are legal. + +WARNING: If you declare the return type of your around advice method as `void`, `null` +will always be returned to the caller, effectively ignoring the result of any invocation +of `proceed()`. It is therefore recommended that an around advice method declare a return +type of `Object`. The advice method should typically return the value returned from an +invocation of `proceed()`, even if the underlying method has a `void` return type. +However, the advice may optionally return a cached value, a wrapped value, or some other +value depending on the use case. The following example shows how to use around advice: @@ -1282,12 +1309,6 @@ The following example shows how to use around advice: } ---- -The value returned by the around advice is the return value seen by the caller of the -method. For example, a simple caching aspect could return a value from a cache if it -has one and invoke `proceed()` if it does not. Note that `proceed` may be invoked once, -many times, or not at all within the body of the around advice. All of these are legal. - - [[aop-ataspectj-advice-params]] ==== Advice Parameters @@ -2315,20 +2336,30 @@ You can declare it by using the `after` element, as the following example shows: [[aop-schema-advice-around]] ==== Around Advice -The last kind of advice is around advice. Around advice runs "around" a matched method -execution. It has the opportunity to do work both before and after the method runs -and to determine when, how, and even if the method actually gets to run at all. -Around advice is often used to share state before and after a method execution in a -thread-safe manner (starting and stopping a timer, for example). Always use the least -powerful form of advice that meets your requirements. Do not use around advice if -before advice can do the job. +The last kind of advice is _around_ advice. Around advice runs "around" a matched +method's execution. It has the opportunity to do work both before and after the method +runs and to determine when, how, and even if the method actually gets to run at all. +Around advice is often used if you need to share state before and after a method +execution in a thread-safe manner – for example, starting and stopping a timer. + +[TIP] +==== +Always use the least powerful form of advice that meets your requirements. + +For example, do not use _around_ advice if _before_ advice is sufficient for your needs. +==== + +You can declare around advice by using the `aop:around` element. The advice method should +declare `Object` as its return type, and the first parameter of the method must be of +type `ProceedingJoinPoint`. Within the body of the advice method, you must invoke +`proceed()` on the `ProceedingJoinPoint` in order for the underlying method to run. +Invoking `proceed()` without arguments will result in the caller's original arguments +being supplied to the underlying method when it is invoked. For advanced use cases, there +is an overloaded variant of the `proceed()` method which accepts an array of arguments +(`Object[]`). The values in the array will be used as the arguments to the underlying +method when it is invoked. See <> for notes on calling +`proceed` with an `Object[]`. -You can declare around advice by using the `aop:around` element. The first parameter of -the advice method must be of type `ProceedingJoinPoint`. Within the body of the advice, -calling `proceed()` on the `ProceedingJoinPoint` causes the underlying method to run. -The `proceed` method may also be called with an `Object[]`. The values in the array -are used as the arguments to the method execution when it proceeds. -See <> for notes on calling `proceed` with an `Object[]`. The following example shows how to declare around advice in XML: [source,xml,indent=0,subs="verbatim,quotes"]