Skip to content

Commit 72f9125

Browse files
committed
Revise AOP proxying section of the reference manual
This commit revises the AOP proxying section as follows. - Documents all limitations of CGLIB-based proxies - Documents self injection as alternative to AopContext.currentProxy() - Avoids use of flippant language See gh-33454 Closes gh-33455
1 parent 5e1de19 commit 72f9125

File tree

1 file changed

+43
-34
lines changed

1 file changed

+43
-34
lines changed

framework-docs/modules/ROOT/pages/core/aop/proxying.adoc

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,26 @@ target object. JDK dynamic proxies are built into the JDK, whereas CGLIB is a co
66
open-source class definition library (repackaged into `spring-core`).
77

88
If the target object to be proxied implements at least one interface, a JDK dynamic
9-
proxy is used. All of the interfaces implemented by the target type are proxied.
10-
If the target object does not implement any interfaces, a CGLIB proxy is created.
9+
proxy is used, and all of the interfaces implemented by the target type are proxied.
10+
If the target object does not implement any interfaces, a CGLIB proxy is created which
11+
is a runtime-generated subclass of the target type.
1112

1213
If you want to force the use of CGLIB proxying (for example, to proxy every method
1314
defined for the target object, not only those implemented by its interfaces),
1415
you can do so. However, you should consider the following issues:
1516

16-
* With CGLIB, `final` methods cannot be advised, as they cannot be overridden in
17-
runtime-generated subclasses.
18-
* As of Spring 4.0, the constructor of your proxied object is NOT called twice anymore,
19-
since the CGLIB proxy instance is created through Objenesis. Only if your JVM does
20-
not allow for constructor bypassing, you might see double invocations and
21-
corresponding debug log entries from Spring's AOP support.
22-
* Your CGLIB proxy usage may face limitations with the JDK 9+ platform module system.
23-
As a typical case, you cannot create a CGLIB proxy for a class from the `java.lang`
24-
package when deploying on the module path. Such cases require a JVM bootstrap flag
17+
* `final` classes cannot be proxied, because they cannot be extended.
18+
* `final` methods cannot be advised, because they cannot be overridden.
19+
* `private` methods cannot be advised, because they cannot be overridden.
20+
* Methods that are not visible – for example, package-private methods in a parent class
21+
from a different package – cannot be advised because they are effectively private.
22+
* The constructor of your proxied object will not be called twice, since the CGLIB proxy
23+
instance is created through Objenesis. However, if your JVM does not allow for
24+
constructor bypassing, you might see double invocations and corresponding debug log
25+
entries from Spring's AOP support.
26+
* Your CGLIB proxy usage may face limitations with the Java Module System. As a typical
27+
case, you cannot create a CGLIB proxy for a class from the `java.lang` package when
28+
deploying on the module path. Such cases require a JVM bootstrap flag
2529
`--add-opens=java.base/java.lang=ALL-UNNAMED` which is not available for modules.
2630

2731
To force the use of CGLIB proxies, set the value of the `proxy-target-class` attribute
@@ -65,9 +69,8 @@ Spring AOP is proxy-based. It is vitally important that you grasp the semantics
6569
what that last statement actually means before you write your own aspects or use any of
6670
the Spring AOP-based aspects supplied with the Spring Framework.
6771

68-
Consider first the scenario where you have a plain-vanilla, un-proxied,
69-
nothing-special-about-it, straight object reference, as the following
70-
code snippet shows:
72+
Consider first the scenario where you have a plain-vanilla, un-proxied object reference,
73+
as the following code snippet shows:
7174

7275
[tabs]
7376
======
@@ -187,15 +190,24 @@ the interceptors (advice) that are relevant to that particular method call. Howe
187190
once the call has finally reached the target object (the `SimplePojo` reference in
188191
this case), any method calls that it may make on itself, such as `this.bar()` or
189192
`this.foo()`, are going to be invoked against the `this` reference, and not the proxy.
190-
This has important implications. It means that self-invocation is not going to result
191-
in the advice associated with a method invocation getting a chance to run.
192-
193-
Okay, so what is to be done about this? The best approach (the term "best" is used
194-
loosely here) is to refactor your code such that the self-invocation does not happen.
195-
This does entail some work on your part, but it is the best, least-invasive approach.
196-
The next approach is absolutely horrendous, and we hesitate to point it out, precisely
197-
because it is so horrendous. You can (painful as it is to us) totally tie the logic
198-
within your class to Spring AOP, as the following example shows:
193+
This has important implications. It means that self invocation is not going to result
194+
in the advice associated with a method invocation getting a chance to run. In other words,
195+
self invocation via an explicit or implicit `this` reference will bypass the advice.
196+
197+
To address that, you have the following options.
198+
199+
Avoid self invocation ::
200+
The best approach (the term "best" is used loosely here) is to refactor your code such
201+
that the self invocation does not happen. This does entail some work on your part, but
202+
it is the best, least-invasive approach.
203+
Inject a self reference ::
204+
An alternative approach is to make use of
205+
xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-self-injection[self injection],
206+
and invoke methods on the proxy via the self reference instead of via `this`.
207+
Use `AopContext.currentProxy()` ::
208+
This last approach is highly discouraged, and we hesitate to point it out, in favor of
209+
the previous options. However, as a last resort you can choose to tie the logic within
210+
your class to Spring AOP, as the following example shows.
199211

200212
[tabs]
201213
======
@@ -206,7 +218,7 @@ Java::
206218
public class SimplePojo implements Pojo {
207219
208220
public void foo() {
209-
// this works, but... gah!
221+
// This works, but it should be avoided if possible.
210222
((Pojo) AopContext.currentProxy()).bar();
211223
}
212224
@@ -223,7 +235,7 @@ Kotlin::
223235
class SimplePojo : Pojo {
224236
225237
fun foo() {
226-
// this works, but... gah!
238+
// This works, but it should be avoided if possible.
227239
(AopContext.currentProxy() as Pojo).bar()
228240
}
229241
@@ -234,10 +246,10 @@ Kotlin::
234246
----
235247
======
236248

237-
This totally couples your code to Spring AOP, and it makes the class itself aware of
238-
the fact that it is being used in an AOP context, which flies in the face of AOP. It
239-
also requires some additional configuration when the proxy is being created, as the
240-
following example shows:
249+
The use of `AopContext.currentProxy()` totally couples your code to Spring AOP, and it
250+
makes the class itself aware of the fact that it is being used in an AOP context, which
251+
reduces some of the benefits of AOP. It also requires that the `ProxyFactory` is
252+
configured to expose the proxy, as the following example shows:
241253

242254
[tabs]
243255
======
@@ -277,9 +289,6 @@ Kotlin::
277289
----
278290
======
279291

280-
Finally, it must be noted that AspectJ does not have this self-invocation issue because
281-
it is not a proxy-based AOP framework.
282-
283-
284-
292+
NOTE: AspectJ compile-time weaving and load-time weaving do not have this self-invocation
293+
issue because they apply advice within the bytecode instead of via a proxy.
285294

0 commit comments

Comments
 (0)