The previous chapter described the Spring’s support for AOP with @AspectJ and schema-based aspect definitions. In this
chapter, we discuss the lower-level Spring AOP APIs.
上一章介绍了 Spring 对基于@AspectJ 和基于 schema 的切面定义的支持。在本章中,我们将讨论更底层的 Spring AOP API。
For common applications, we recommend the use of Spring AOP with AspectJ pointcuts as described in the previous
chapter.
对于常见应用,我们推荐使用前一章所述的 Spring AOP 与 AspectJ 切入点。
6.1. Spring 中的 Pointcut API
This section describes how Spring handles the crucial pointcut concept.
本节描述了 Spring 如何处理关键切点概念。
Spring’s pointcut model enables pointcut reuse independent of advice types. You can target different advice with the
same pointcut.
Spring 的切点模型允许切点在不依赖于通知类型的情况下重用。您可以使用相同的切点针对不同的通知。
The org.springframework.aop.Pointcut interface is the central interface, used to target advices to particular classes
and methods. The complete interface follows:
org.springframework.aop.Pointcut 接口是中央接口,用于将建议针对特定的类和方法。完整的接口如下:
Splitting the Pointcut interface into two parts allows reuse of class and method matching parts and fine-grained
composition operations (such as performing a “union” with another method matcher).
将 Pointcut 接口拆分为两部分,允许重用类和方法匹配部分以及细粒度的组合操作(例如与另一个方法匹配器执行“合并”操作)。
The ClassFilter interface is used to restrict the pointcut to a given set of target classes. If the matches() method
always returns true, all target classes are matched. The following listing shows the ClassFilter interface
definition:
ClassFilter 接口用于将切入点限制在给定的目标类集合中。如果 matches() 方法始终返回 true,则匹配所有目标类。以下列表显示了
ClassFilter 接口定义:
The MethodMatcher interface is normally more important. The complete interface follows:
MethodMatcher 接口通常更重要。完整的接口如下:
The matches(Method, Class) method is used to test whether this pointcut ever matches a given method on a target class.
This evaluation can be performed when an AOP proxy is created to avoid the need for a test on every method invocation.
If the two-argument matches method returns true for a given method, and the isRuntime() method for the
MethodMatcher returns true, the three-argument matches method is invoked on every method invocation. This lets a
pointcut look at the arguments passed to the method invocation immediately before the target advice starts.
matches(Method, Class) 方法用于测试此切入点是否匹配目标类上的给定方法。此评估可以在创建 AOP
代理时进行,以避免在每个方法调用上进行测试的需要。如果两个参数的 matches 方法对于给定方法返回 true ,并且 MethodMatcher
的 isRuntime() 方法返回 true ,则在每次方法调用上都会调用三个参数的匹配方法。这使得切入点可以查看在目标通知开始之前立即传递给方法调用的参数。
Most MethodMatcher implementations are static, meaning that their isRuntime() method returns false. In this case,
the three-argument matches method is never invoked.
大多数 MethodMatcher 实现是静态的,这意味着它们的 isRuntime() 方法返回 false 。在这种情况下,三参数的 matches
方法从未被调用。
If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when
an AOP proxy is created.
如果可能,尽量使切入点静态化,以便在创建 AOP 代理时,AOP 框架能够缓存切入点评估的结果。
6.1.2. 切入点操作
Spring supports operations (notably, union and intersection) on pointcuts.
Spring 支持对切入点进行操作(特别是,并集和交集)。
Union means the methods that either pointcut matches. Intersection means the methods that both pointcuts match. Union is
usually more useful. You can compose pointcuts by using the static methods in the
org.springframework.aop.support.Pointcuts class or by using the ComposablePointcut class in the same package.
However, using AspectJ pointcut expressions is usually a simpler approach.
联合意味着匹配任一点切的方法。交集意味着两个点切都匹配的方法。联合通常更有用。您可以通过使用
org.springframework.aop.support.Pointcuts 类中的静态方法或使用同一包中的 ComposablePointcut 类来组合点切。然而,通常使用
AspectJ 点切表达式是一种更简单的方法。
6.1.3. AspectJ 表达式切入点
Since 2.0, the most important type of pointcut used by Spring is
org.springframework.aop.aspectj.AspectJExpressionPointcut. This is a pointcut that uses an AspectJ-supplied library to
parse an AspectJ pointcut expression string.
自 2.0 以来,Spring 使用最重要的点切类型是 org.springframework.aop.aspectj.AspectJExpressionPointcut 。这是一个使用
AspectJ 提供的库来解析 AspectJ 点切表达式字符串的点切。
See the previous chapter for a discussion of supported AspectJ pointcut primitives.
查看上一章以了解支持的 AspectJ 切入点原语讨论。
6.1.4. 方便性切入点实现
Spring provides several convenient pointcut implementations. You can use some of them directly; others are intended to
be subclassed in application-specific pointcuts.
Spring 提供了几个方便的切入点实现。您可以直接使用其中一些;其他则是为了在特定应用中的切入点中被继承而设计的。
Static pointcuts are based on the method and the target class and cannot take into account the method’s arguments.
Static pointcuts suffice — and are best — for most usages. Spring can evaluate a static pointcut only once, when a
method is first invoked.
静态切入点基于方法和目标类,无法考虑方法的参数。静态切入点对于大多数用法来说足够了——并且是最好的。Spring
只能在方法首次被调用时评估一次静态切入点。
After that, there is no need to evaluate the pointcut again with each method invocation.
之后,无需在每次方法调用时再次评估切入点。
The rest of this section describes some of the static pointcut implementations that are included with Spring.
本节其余部分描述了 Spring 中包含的一些静态切入点实现。
正则表达式切点
One obvious way to specify static pointcuts is regular expressions. Several AOP frameworks besides Spring make this
possible. org.springframework.aop.support.JdkRegexpMethodPointcut is a generic regular expression pointcut that uses
the regular expression support in the JDK.
一种明显的指定静态切入点的方法是使用正则表达式。除了 Spring 之外,还有几个 AOP 框架使这成为可能。
org.springframework.aop.support.JdkRegexpMethodPointcut 是一个通用的正则表达式切入点,它使用了 JDK 中的正则表达式支持。
With the JdkRegexpMethodPointcut class, you can provide a list of pattern strings. If any of these is a match, the
pointcut evaluates to true. (As a consequence, the resulting pointcut is effectively the union of the specified
patterns.)
使用 JdkRegexpMethodPointcut 类,您可以提供一系列模式字符串。如果其中任何一个匹配,切点将评估为 true
。(因此,生成的切点实际上是指定模式的并集。)
The following example shows how to use JdkRegexpMethodPointcut:
以下示例展示了如何使用 JdkRegexpMethodPointcut :
Spring provides a convenience class named RegexpMethodPointcutAdvisor, which lets us also reference an Advice (
remember that an Advice can be an interceptor, before advice, throws advice, and others). Behind the scenes, Spring
uses a JdkRegexpMethodPointcut. Using RegexpMethodPointcutAdvisor simplifies wiring, as the one bean encapsulates
both pointcut and advice, as the following example shows:
Spring 提供了一个名为 RegexpMethodPointcutAdvisor 的便利类,它让我们也可以引用一个 Advice (记住, Advice
可以是一个拦截器、前置通知、抛出通知等)。幕后,Spring 使用一个 JdkRegexpMethodPointcut 。使用 RegexpMethodPointcutAdvisor
简化了连接,因为一个 Bean 封装了切点和通知,如下例所示:
You can use RegexpMethodPointcutAdvisor with any Advice type.
您可以使用 RegexpMethodPointcutAdvisor 与任何 Advice 类型一起使用。
属性驱动切点
An important type of static pointcut is a metadata-driven pointcut. This uses the values of metadata attributes (
typically, source-level metadata).
一种重要的静态切入点是元数据驱动的切入点。这使用元数据属性值(通常是源级别元数据)。
Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments as well as
static information. This means that they must be evaluated with every method invocation and that the result cannot be
cached, as arguments will vary.
动态切入点比静态切入点更难以评估。它们考虑方法参数以及静态信息。这意味着它们必须在每次方法调用时进行评估,并且结果不能缓存,因为参数会变化。
The main example is the control flow pointcut.
主要示例是 control flow 切入点。
Spring control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, although less powerful. (There is
currently no way to specify that a pointcut runs below a join point matched by another pointcut.) A control flow
pointcut matches the current call stack. For example, it might fire if the join point was invoked by a method in the
com.mycompany.web package or by the SomeCaller class. Control flow pointcuts are specified by using the
org.springframework.aop.support.ControlFlowPointcut class.
Spring 控制流切入点在概念上类似于 AspectJ cflow 切入点,尽管功能较弱。(目前尚无方法指定一个切入点在另一个切入点匹配的连接点下方运行。)控制流切入点匹配当前调用栈。例如,如果连接点是由
com.mycompany.web 包中的方法或 SomeCaller 类调用的,则可能会触发。控制流切入点通过使用
org.springframework.aop.support.ControlFlowPointcut 类来指定。
Control flow pointcuts are significantly more expensive to evaluate at runtime than even other dynamic pointcuts. In
Java 1.4, the cost is about five times that of other dynamic pointcuts.
控制流切入点在运行时评估的成本比其他动态切入点高得多。在 Java 1.4 中,其成本约为其他动态切入点的五倍。
6.1.5. 切点超类
Spring provides useful pointcut superclasses to help you to implement your own pointcuts.
Spring 提供了有用的切入点超类,以帮助您实现自己的切入点。
Because static pointcuts are most useful, you should probably subclass StaticMethodMatcherPointcut. This requires
implementing only one abstract method (although you can override other methods to customize behavior). The following
example shows how to subclass StaticMethodMatcherPointcut:
因为静态切入点最为有用,你可能需要继承 StaticMethodMatcherPointcut 。这只需要实现一个抽象方法(尽管你也可以覆盖其他方法来自定义行为)。以下示例展示了如何继承
StaticMethodMatcherPointcut :
There are also superclasses for dynamic pointcuts. You can use custom pointcuts with any advice type.
也存在用于动态切入点的高级类。您可以使用自定义切入点与任何建议类型一起使用。
Because pointcuts in Spring AOP are Java classes rather than language features (as in AspectJ), you can declare custom
pointcuts, whether static or dynamic. Custom pointcuts in Spring can be arbitrarily complex.
因为 Spring AOP 中的切入点是 Java 类而不是语言特性(如 AspectJ 中的那样),所以您可以声明自定义切入点,无论是静态的还是动态的。Spring
中的自定义切入点可以是任意复杂的。
However, we recommend using the AspectJ pointcut expression language, if you can.
然而,如果您可以的话,我们建议使用 AspectJ 切点表达式语言。
Later versions of Spring may offer support for “semantic pointcuts” as offered by JAC — for example, “all methods that
change instance variables in the target object.”
Spring 的后续版本可能将支持 JAC 提供的“语义切入点”,例如,“所有更改目标对象实例变量的方法。”
6.2. Spring 中的建议 API
Now we can examine how Spring AOP handles advice.
现在我们可以检查 Spring AOP 如何处理通知。
6.2.1. 咨询生命周期
Each advice is a Spring bean. An advice instance can be shared across all advised objects or be unique to each advised
object. This corresponds to per-class or per-instance advice.
每条建议都是一个 Spring Bean。建议实例可以在所有被建议的对象之间共享,或者对每个被建议的对象是唯一的。这对应于按类或按实例的建议。
Per-class advice is used most often. It is appropriate for generic advice, such as transaction advisors. These do not
depend on the state of the proxied object or add new state. They merely act on the method and arguments.
每类建议最常被使用。它适用于通用建议,例如交易顾问。这些不依赖于代理对象的状态或添加新状态。它们仅对方法和参数进行操作。
Per-instance advice is appropriate for introductions, to support mixins. In this case, the advice adds state to the
proxied object.
实例化建议适用于介绍,以支持混入。在这种情况下,建议为代理对象添加状态。
You can use a mix of shared and per-instance advice in the same AOP proxy.
您可以在同一个 AOP 代理中使用共享和实例化建议的混合。
6.2.2. Spring 中的建议类型
Spring provides several advice types and is extensible to support arbitrary advice types. This section describes the
basic concepts and standard advice types.
Spring 提供了几种建议类型,并可扩展以支持任意建议类型。本节描述了基本概念和标准建议类型。
拦截建议周围
The most fundamental advice type in Spring is interception around advice.
Spring 中最基本的建议类型是围绕建议的拦截。
Spring is compliant with the AOP Alliance interface for around advice that uses method interception. Classes that
implement MethodInterceptor and that implement around advice should also implement the following interface:
Spring 遵循 AOP Alliance 接口,用于使用方法拦截的环绕通知。实现 MethodInterceptor 并实现环绕通知的类还应实现以下接口:
The MethodInvocation argument to the invoke() method exposes the method being invoked, the target join point, the
AOP proxy, and the arguments to the method. The invoke() method should return the invocation’s result: the return
value of the join point.
MethodInvocation 参数暴露了被调用的方法、目标连接点、AOP 代理以及方法参数。 invoke() 方法应返回调用的结果:连接点的返回值。
The following example shows a simple MethodInterceptor implementation:
以下示例展示了简单的 MethodInterceptor 实现:
Note the call to the proceed() method of MethodInvocation. This proceeds down the interceptor chain towards the join
point. Most interceptors invoke this method and return its return value. However, a MethodInterceptor, like any around
advice, can return a different value or throw an exception rather than invoke the proceed method. However, you do not
want to do this without good reason.
注意调用 MethodInvocation 的 proceed() 方法。这沿着拦截器链向下进行,直到连接点。大多数拦截器都会调用此方法并返回其返回值。然而,一个
MethodInterceptor ,就像任何环绕通知一样,可以返回不同的值或抛出异常,而不是调用 proceed 方法。但是,如果没有充分的理由,你不希望这样做。
MethodInterceptor implementations offer interoperability with other AOP Alliance-compliant AOP implementations. The
other advice types discussed in the remainder of this section implement common AOP concepts but in a Spring-specific
way.
MethodInterceptor 实现与其他 AOP Alliance 兼容的 AOP 实现的互操作性。本节剩余部分讨论的其他建议类型实现了常见的 AOP
概念,但以 Spring 特定方式实现。
While there is an advantage in using the most specific advice type, stick with MethodInterceptor around advice if you
are likely to want to run the aspect in another AOP framework. Note that pointcuts are not currently interoperable
between frameworks, and the AOP Alliance does not currently define pointcut interfaces.
虽然使用最具体的建议类型有优势,但如果您可能需要在另一个 AOP 框架中运行方面,请坚持使用 MethodInterceptor
围绕建议。请注意,当前切点在不同框架之间不可互操作,AOP 联盟目前没有定义切点接口。
A simpler advice type is a before advice. This does not need a MethodInvocation object, since it is called only before
entering the method.
一种更简单的建议类型是“前置建议”。这种类型不需要 MethodInvocation 对象,因为它仅在进入方法之前被调用。
The main advantage of a before advice is that there is no need to invoke the proceed() method and, therefore, no
possibility of inadvertently failing to proceed down the interceptor chain.
主优势在于无需调用 proceed() 方法,因此不会出现意外未能继续执行拦截器链的可能性。
The following listing shows the MethodBeforeAdvice interface:
以下列表显示了 MethodBeforeAdvice 接口:
(Spring’s API design would allow for field before advice, although the usual objects apply to field interception and it
is unlikely for Spring to ever implement it.)
(春季的 API 设计允许在通知之前处理字段,尽管通常的对象适用于字段拦截,而且 Spring 不太可能实现它。)
Note that the return type is void. Before advice can insert custom behavior before the join point runs but cannot
change the return value. If a before advice throws an exception, it stops further execution of the interceptor chain.
The exception propagates back up the interceptor chain.
请注意,返回类型是 void
。在建议之前可以插入自定义行为在连接点运行之前,但不能更改返回值。如果建议抛出异常,它将停止拦截器链的进一步执行。异常会向上传播到拦截器链。
If it is unchecked or on the signature of the invoked method, it is passed directly to the client. Otherwise, it is
wrapped in an unchecked exception by the AOP proxy.
如果未选中或位于被调用方法的签名上,则直接传递给客户端。否则,它将由 AOP 代理包装在未检查的异常中。
The following example shows a before advice in Spring, which counts all method invocations:
以下示例展示了 Spring 中的前置通知,用于计算所有方法调用:
Before advice can be used with any pointcut.
在建议可以使用任何切入点之前。
Throws advice is invoked after the return of the join point if the join point threw an exception. Spring offers typed
throws advice. Note that this means that the org.springframework.aop.ThrowsAdvice interface does not contain any
methods. It is a tag interface identifying that the given object implements one or more typed throws advice methods.
These should be in the following form:
抛出建议在连接点抛出异常后调用。Spring 提供了类型化抛出建议。请注意,这意味着 org.springframework.aop.ThrowsAdvice
接口不包含任何方法。它是一个标记接口,用于标识给定的对象实现了一个或多个类型化抛出建议方法。这些方法应采用以下形式:
Only the last argument is required. The method signatures may have either one or four arguments, depending on whether
the advice method is interested in the method and arguments. The next two listing show classes that are examples of
throws advice.
仅需要最后一个参数。方法签名可能有一个或四个参数,这取决于建议方法是否对方法和参数感兴趣。接下来的两个列表展示了抛出建议的示例类。
The following advice is invoked if a RemoteException is thrown (including from subclasses):
以下建议在抛出 RemoteException (包括从子类中抛出)时被调用:
Unlike the preceding advice, the next example declares four arguments, so that it has access to the invoked method,
method arguments, and target object. The following advice is invoked if a ServletException is thrown:
与前面的建议不同,下一个示例声明了四个参数,因此它可以访问被调用的方法、方法参数和目标对象。如果抛出 ServletException
,将调用以下建议:
The final example illustrates how these two methods could be used in a single class that handles both RemoteException
and ServletException. Any number of throws advice methods can be combined in a single class. The following listing
shows the final example:
最终示例说明了这两种方法如何在处理 RemoteException 和 ServletException 的单一类中使用。任何数量的抛出建议方法都可以组合在单个类中。以下列表显示了最终示例:
If a throws-advice method throws an exception itself, it overrides the original exception (that is, it changes the
exception thrown to the user). The overriding exception is typically a RuntimeException, which is compatible with any
method signature.
如果一个抛出建议的方法本身抛出异常,它将覆盖原始异常(即,它更改了抛给用户的异常)。覆盖的异常通常是
RuntimeException,它与任何方法签名兼容。
However, if a throws-advice method throws a checked exception, it must match the declared exceptions of the target
method and is, hence, to some degree coupled to specific target method signatures. Do not throw an undeclared checked
exception that is incompatible with the target method’s signature!
然而,如果抛出建议的方法抛出检查型异常,它必须与目标方法的声明异常相匹配,因此,在某种程度上与特定的目标方法签名耦合。不要抛出与目标方法签名不兼容的未声明检查型异常!
Throws advice can be used with any pointcut.
抛出建议可以与任何切入点一起使用。
An after returning advice in Spring must implement the org.springframework.aop.AfterReturningAdvice interface, which
the following listing shows:
Spring 返回后建议必须实现 org.springframework.aop.AfterReturningAdvice 接口,以下列表展示了该接口:
An after returning advice has access to the return value (which it cannot modify), the invoked method, the method’s
arguments, and the target.
返回后,建议有权限访问返回值(它不能修改)、被调用的方法、方法的参数和目标。
The following after returning advice counts all successful method invocations that have not thrown exceptions:
以下在返回建议后统计所有未抛出异常的成功方法调用:
This advice does not change the execution path. If it throws an exception, it is thrown up the interceptor chain instead
of the return value.
这条建议不会改变执行路径。如果抛出异常,它将在拦截器链中抛出,而不是返回值。
After returning advice can be used with any pointcut.
返回建议后可以与任何切入点一起使用。
Spring treats introduction advice as a special kind of interception advice.
春季将介绍建议视为一种特殊的拦截建议。
Introduction requires an IntroductionAdvisor and an IntroductionInterceptor that implement the following
interface:
简介需要实现以下接口的 IntroductionAdvisor 和 IntroductionInterceptor :
The invoke() method inherited from the AOP Alliance MethodInterceptor interface must implement the introduction.
That is, if the invoked method is on an introduced interface, the introduction interceptor is responsible for handling
the method call — it cannot invoke proceed().
从 AOP Alliance MethodInterceptor 接口继承的 invoke() 方法必须实现引入。也就是说,如果被调用的方法是在引入的接口上,引入拦截器负责处理方法调用——它不能调用
proceed() 。
Introduction advice cannot be used with any pointcut, as it applies only at the class, rather than the method, level.
You can only use introduction advice with the IntroductionAdvisor, which has the following methods:
简介建议不能与任何切入点一起使用,因为它只适用于类级别,而不是方法级别。您只能使用带有 IntroductionAdvisor
的简介建议,该注解具有以下方法:
There is no MethodMatcher and, hence, no Pointcut associated with introduction advice. Only class filtering is
logical.
没有 MethodMatcher ,因此也没有与介绍建议相关的 Pointcut 。只有按班级筛选是合理的。
The getInterfaces() method returns the interfaces introduced by this advisor.
该方法返回由该顾问引入的接口。
The validateInterfaces() method is used internally to see whether or not the introduced interfaces can be implemented
by the configured IntroductionInterceptor.
validateInterfaces() 方法用于内部检查引入的接口是否可以被配置的 IntroductionInterceptor 实现。
Consider an example from the Spring test suite and suppose we want to introduce the following interface to one or more
objects:
考虑 Spring 测试套件中的一个示例,假设我们想要将以下接口引入一个或多个对象中:
This illustrates a mixin. We want to be able to cast advised objects to Lockable, whatever their type and call lock
and unlock methods. If we call the lock() method, we want all setter methods to throw a LockedException. Thus, we
can add an aspect that provides the ability to make objects immutable without them having any knowledge of it: a good
example of AOP.
这展示了混入(mixin)。我们希望能够将建议的对象转换为 Lockable ,无论它们的类型如何,并调用锁定和解锁方法。如果我们调用
lock() 方法,我们希望所有设置方法都抛出 LockedException
异常。这样,我们可以添加一个提供使对象不可变的能力的方面,而对象本身无需了解这一点:这是面向方面编程(AOP)的一个好例子。
First, we need an IntroductionInterceptor that does the heavy lifting. In this case, we extend the
org.springframework.aop.support.DelegatingIntroductionInterceptor convenience class. We could implement
IntroductionInterceptor directly, but using DelegatingIntroductionInterceptor is best for most cases.
首先,我们需要一个负责重负载的 IntroductionInterceptor 。在这种情况下,我们扩展了
org.springframework.aop.support.DelegatingIntroductionInterceptor 便利类。我们可以直接实现 IntroductionInterceptor
,但使用 DelegatingIntroductionInterceptor 在大多数情况下是最好的。
The DelegatingIntroductionInterceptor is designed to delegate an introduction to an actual implementation of the
introduced interfaces, concealing the use of interception to do so. You can set the delegate to any object using a
constructor argument. The default delegate (when the no-argument constructor is used) is this. Thus, in the next
example, the delegate is the LockMixin subclass of DelegatingIntroductionInterceptor. Given a delegate (by default,
itself), a DelegatingIntroductionInterceptor instance looks for all interfaces implemented by the delegate (other than
IntroductionInterceptor) and supports introductions against any of them. Subclasses such as LockMixin can call the
suppressInterface(Class intf) method to suppress interfaces that should not be exposed. However, no matter how many
interfaces an IntroductionInterceptor is prepared to support, the IntroductionAdvisor used controls which interfaces
are actually exposed. An introduced interface conceals any implementation of the same interface by the target.
DelegatingIntroductionInterceptor 被设计用来委托一个接口的实际实现,同时隐藏使用拦截来实现这一点的过程。您可以使用构造函数参数将委托设置为任何对象。默认委托(当使用无参数构造函数时)是
this 。因此,在下一个示例中,委托是 DelegatingIntroductionInterceptor 的 LockMixin 子类。给定一个委托(默认情况下,自身),
DelegatingIntroductionInterceptor 实例会查找委托(除了 IntroductionInterceptor 之外)实现的所有接口,并支持对这些接口中的任何一个进行介绍。例如
LockMixin 这样的子类可以调用 suppressInterface(Class intf) 方法来抑制不应公开的接口。然而,无论
IntroductionInterceptor 准备支持多少接口,使用的 IntroductionAdvisor 控制哪些接口实际上被公开。一个介绍接口隐藏了目标中相同接口的任何实现。
Thus, LockMixin extends DelegatingIntroductionInterceptor and implements Lockable itself. The superclass
automatically picks up that Lockable can be supported for introduction, so we do not need to specify that. We could
introduce any number of interfaces in this way.
因此, LockMixin 扩展 DelegatingIntroductionInterceptor 并自行实现 Lockable 。超类会自动识别 Lockable
可以用于引入,因此我们无需指定。我们可以以此方式引入任意数量的接口。
Note the use of the locked instance variable. This effectively adds additional state to that held in the target
object.
注意使用 locked 实例变量。这实际上向目标对象持有的状态中添加了额外的状态。
The following example shows the example LockMixin class:
以下示例展示了示例 LockMixin 类:
Often, you need not override the invoke() method. The DelegatingIntroductionInterceptor implementation (which calls
the delegate method if the method is introduced, otherwise proceeds towards the join point) usually suffices. In the
present case, we need to add a check: no setter method can be invoked if in locked mode.
通常,您无需重写 invoke() 方法。 DelegatingIntroductionInterceptor 实现(如果引入了该方法,则调用 delegate
方法,否则继续到连接点)通常足够。在当前情况下,我们需要添加一个检查:在锁定模式下,不能调用 setter 方法。
The required introduction only needs to hold a distinct LockMixin instance and specify the introduced interfaces (in
this case, only Lockable). A more complex example might take a reference to the introduction interceptor (which would
be defined as a prototype). In this case, there is no configuration relevant for a LockMixin, so we create it by using
new. The following example shows our LockMixinAdvisor class:
所需介绍只需持有唯一的 LockMixin 实例并指定引入的接口(在这种情况下,仅为 Lockable
)。一个更复杂的例子可能需要引用介绍拦截器(这将作为原型定义)。在这种情况下,没有与 LockMixin 相关的配置,因此我们通过使用
new 创建它。以下示例显示了我们的 LockMixinAdvisor 类:
We can apply this advisor very simply, because it requires no configuration. (However, it is impossible to use an
IntroductionInterceptor without an IntroductionAdvisor.) As usual with introductions, the advisor must be
per-instance, as it is stateful. We need a different instance of LockMixinAdvisor, and hence LockMixin, for each
advised object. The advisor comprises part of the advised object’s state.
我们可以非常简单地应用这个顾问,因为它不需要配置。(然而,没有 IntroductionAdvisor 就无法使用 IntroductionInterceptor
。)像往常一样,在介绍时,顾问必须是按实例的,因为它是有状态的。我们需要为每个被顾问对象创建不同的 LockMixinAdvisor
,因此也需要不同的 LockMixin 。顾问构成了被顾问对象状态的一部分。
We can apply this advisor programmatically by using the Advised.addAdvisor() method or (the recommended way) in XML
configuration, as any other advisor. All proxy creation choices discussed below, including “auto proxy creators,”
correctly handle introductions and stateful mixins.
我们可以通过使用 Advised.addAdvisor() 方法或(推荐方式)在 XML
配置中程序化地应用此顾问,就像任何其他顾问一样。以下讨论的所有代理创建选择,包括“自动代理创建器”,都正确处理了引入和有状态混合。
6.3. Spring 中的顾问 API
In Spring, an Advisor is an aspect that contains only a single advice object associated with a pointcut expression.
在春季,一个顾问是一个只包含与切入点表达式相关的一个建议对象的方面。
Apart from the special case of introductions, any advisor can be used with any advice.
org.springframework.aop.support.DefaultPointcutAdvisor is the most commonly used advisor class. It can be used with a
MethodInterceptor, BeforeAdvice, or ThrowsAdvice.
除了介绍的特殊情况外,任何顾问都可以与任何建议一起使用。 org.springframework.aop.support.DefaultPointcutAdvisor
是最常用的顾问类。它可以与 MethodInterceptor 、 BeforeAdvice 或 ThrowsAdvice 一起使用。
It is possible to mix advisor and advice types in Spring in the same AOP proxy. For example, you could use an
interception around advice, throws advice, and before advice in one proxy configuration. Spring automatically creates
the necessary interceptor chain.
Spring 中可以在同一个 AOP 代理中混合顾问和咨询类型。例如,您可以在一个代理配置中使用围绕咨询的拦截、抛出咨询和前置咨询。Spring
会自动创建必要的拦截器链。
ProxyFactoryBean to Create AOP Proxies6.4. 使用 ProxyFactoryBean 创建 AOP 代理
If you use the Spring IoC container (an ApplicationContext or BeanFactory) for your business objects (and you should
be!), you want to use one of Spring’s AOP FactoryBean implementations. (Remember that a factory bean introduces a
layer of indirection, letting it create objects of a different type.)
如果您使用 Spring IoC 容器(一个 ApplicationContext 或 BeanFactory )来处理业务对象(您应该这样做!),您希望使用 Spring 的
AOP FactoryBean 实现之一。(记住,工厂 bean 引入了一层间接层,允许它创建不同类型的对象。)
The Spring AOP support also uses factory beans under the covers.
Spring AOP 支持也使用了工厂 Bean。
The basic way to create an AOP proxy in Spring is to use the org.springframework.aop.framework.ProxyFactoryBean. This
gives complete control over the pointcuts, any advice that applies, and their ordering. However, there are simpler
options that are preferable if you do not need such control.
Spring 中创建 AOP 代理的基本方法是使用 org.springframework.aop.framework.ProxyFactoryBean
。这提供了对切入点、任何适用的建议及其顺序的完全控制。然而,如果您不需要这种控制,有更简单的选项更可取。
The ProxyFactoryBean, like other Spring FactoryBean implementations, introduces a level of indirection. If you
define a ProxyFactoryBean named foo, objects that reference foo do not see the ProxyFactoryBean instance itself
but an object created by the implementation of the getObject() method in the ProxyFactoryBean . This method creates
an AOP proxy that wraps a target object.
ProxyFactoryBean ,与其他 Spring FactoryBean 实现一样,引入了间接层。如果您定义一个名为 foo 的 ProxyFactoryBean ,引用
foo 的对象不会看到 ProxyFactoryBean 实例本身,而是由 ProxyFactoryBean 中 getObject() 方法的实现创建的对象。此方法创建了一个包装目标对象的
AOP 代理。
One of the most important benefits of using a ProxyFactoryBean or another IoC-aware class to create AOP proxies is
that advices and pointcuts can also be managed by IoC. This is a powerful feature, enabling certain approaches that are
hard to achieve with other AOP frameworks.
使用 ProxyFactoryBean 或另一个 IoC 感知类创建 AOP 代理的一个重要好处是,通知和切入点也可以由 IoC 管理。这是一个强大的功能,使得某些在其他
AOP 框架中难以实现的方法成为可能。
For example, an advice may itself reference application objects (besides the target, which should be available in any
AOP framework), benefiting from all the pluggability provided by Dependency Injection.
例如,一条建议本身可能引用应用程序对象(除了目标,它应在任何 AOP 框架中可用),从而受益于依赖注入提供的所有可插拔性。
6.4.2. JavaBean 属性
In common with most FactoryBean implementations provided with Spring, the ProxyFactoryBean class is itself a
JavaBean. Its properties are used to:
与 Spring 提供的多数 FactoryBean 实现一样, ProxyFactoryBean 类本身就是一个 JavaBean。它的属性用于:
Specify the target you want to proxy.
指定您要代理的目标。
Specify whether to use CGLIB (described later and see also JDK- and CGLIB-based proxies).
指定是否使用 CGLIB(稍后描述,也可参见基于 JDK 和 CGLIB 的代理)。
Some key properties are inherited from org.springframework.aop.framework.ProxyConfig (the superclass for all AOP proxy
factories in Spring). These key properties include the following:
一些关键属性是从 org.springframework.aop.framework.ProxyConfig (Spring 中所有 AOP 代理工厂的超类)继承而来的。这些关键属性包括以下内容:
proxyTargetClass: true if the target class is to be proxied, rather than the target class’s interfaces. If this
property value is set to true, then CGLIB proxies are created (but see
also JDK- and CGLIB-based proxies).
proxyTargetClass : true 如果要代理目标类,而不是目标类的接口。如果此属性值设置为 true ,则创建 CGLIB 代理(但也可参见基于
JDK 和 CGLIB 的代理)。
optimize: Controls whether or not aggressive optimizations are applied to proxies created through CGLIB. You should
not blithely use this setting unless you fully understand how the relevant AOP proxy handles optimization. This is
currently used only for CGLIB proxies.
optimize : 控制是否对通过 CGLIB 创建的代理应用激进优化。除非您完全理解相关 AOP 代理如何处理优化,否则不应随意使用此设置。目前此设置仅用于
CGLIB 代理。
It has no effect with JDK dynamic proxies.
它对 JDK 动态代理没有影响。
frozen: If a proxy configuration is frozen, changes to the configuration are no longer allowed. This is useful
both as a slight optimization and for those cases when you do not want callers to be able to manipulate the proxy (
through the Advised interface) after the proxy has been created. The default value of this property is false, so
changes (such as adding additional advice) are allowed.
如果代理配置为 frozen ,则不允许更改配置。这既是一种轻微的优化,也适用于那些您不希望调用者能够在代理创建后操纵代理(通过
Advised 接口)的情况。此属性的默认值为 false ,因此允许更改(如添加额外的建议)。
exposeProxy: Determines whether or not the current proxy should be exposed in a ThreadLocal so that it can be
accessed by the target. If a target needs to obtain the proxy and the exposeProxy property is set to true, the
target can use the AopContext.currentProxy() method.
exposeProxy :确定当前代理是否应在 ThreadLocal 中暴露,以便目标可以访问。如果目标需要获取代理且 exposeProxy 属性设置为
true ,则目标可以使用 AopContext.currentProxy() 方法。
Other properties specific to ProxyFactoryBean include the following:
其他特定于 ProxyFactoryBean 的属性包括以下内容:
proxyInterfaces: An array of String interface names. If this is not supplied, a CGLIB proxy for the target class
is used (but see also JDK- and CGLIB-based proxies).
proxyInterfaces :一个接口名称数组。如果没有提供,则使用目标类的 CGLIB 代理(但也可以参考基于 JDK 和 CGLIB 的代理)。
interceptorNames: A String array of Advisor, interceptor, or other advice names to apply. Ordering is
significant, on a first come-first served basis. That is to say that the first interceptor in the list is the first to
be able to intercept the invocation.
interceptorNames : 要应用的一组拦截器或其他建议名称。顺序很重要,按照先到先得的原则。也就是说,列表中的第一个拦截器将是第一个能够拦截调用的拦截器。
The names are bean names in the current factory, including bean names from ancestor factories. You cannot mention bean
references here, since doing so results in the ProxyFactoryBean ignoring the singleton setting of the advice.
当前工厂中的名称是豆名称,包括祖先工厂中的豆名称。在这里不能提及豆引用,因为这样做会导致 ProxyFactoryBean 忽略建议的单例设置。
You can append an interceptor name with an asterisk (*). Doing so results in the application of all advisor beans
with names that start with the part before the asterisk to be applied. You can find an example of using this feature
in Using “Global” Advisors.
您可以使用带星号( * )的拦截器名称。这样做会导致应用所有以星号之前部分开头的顾问 bean。您可以在“使用“全局”顾问”中找到使用此功能的示例。
singleton: Whether or not the factory should return a single object, no matter how often the getObject() method is
called. Several FactoryBean implementations offer such a method. The default value is true. If you want to use
stateful advice - for example, for stateful mixins - use prototype advices along with a singleton value of false.
singleton:无论调用 getObject() 方法多少次,工厂是否应该返回一个单一对象。几个 FactoryBean 实现提供了这样的方法。默认值是
true 。如果您想使用有状态的建议——例如,对于有状态的混合——请使用原型建议,并配合 false 的单例值。
6.4.3. 基于 JDK 和 CGLIB 的代理
This section serves as the definitive documentation on how the ProxyFactoryBean chooses to create either a JDK-based
proxy or a CGLIB-based proxy for a particular target object (which is to be proxied).
本节作为如何为特定目标对象(需要代理的对象)选择创建基于 JDK 的代理或基于 CGLIB 的代理的权威文档。
The behavior of the ProxyFactoryBean with regard to creating JDK- or CGLIB-based proxies changed between versions
1.2.x and 2.0 of Spring. The ProxyFactoryBean now exhibits similar semantics with regard to auto-detecting interfaces
as those of the TransactionProxyFactoryBean class.
Spring 框架中,关于使用 JDK 或 CGLIB 创建代理的行为在 1.2.x 版本和 2.0 版本之间发生了变化。现在, ProxyFactoryBean
在自动检测接口方面的语义与 TransactionProxyFactoryBean 类的语义相似。
If the class of a target object that is to be proxied (hereafter simply referred to as the target class) does not
implement any interfaces, a CGLIB-based proxy is created.
如果目标对象要代理的类(以下简称目标类)没有实现任何接口,将创建一个基于 CGLIB 的代理。
This is the easiest scenario, because JDK proxies are interface-based, and no interfaces means JDK proxying is not even
possible. You can plug in the target bean and specify the list of interceptors by setting the interceptorNames
property. Note that a CGLIB-based proxy is created even if the proxyTargetClass property of the ProxyFactoryBean has
been set to false. (Doing so makes no sense and is best removed from the bean definition, because it is, at best,
redundant, and, at worst confusing.)
这是最简单的情况,因为 JDK 代理是基于接口的,没有接口甚至无法进行 JDK 代理。您可以通过设置 interceptorNames 属性来插入目标
bean 并指定拦截器列表。请注意,即使 ProxyFactoryBean 的 proxyTargetClass 属性已被设置为 false ,也会创建基于 CGLIB
的代理。(这样做没有意义,最好从 bean 定义中移除,因为这最多是多余的,最坏的情况下会令人困惑。)
If the target class implements one (or more) interfaces, the type of proxy that is created depends on the configuration
of the ProxyFactoryBean.
如果目标类实现了一个(或多个)接口,创建的代理类型取决于 ProxyFactoryBean 的配置。
If the proxyTargetClass property of the ProxyFactoryBean has been set to true, a CGLIB-based proxy is created.
This makes sense and is in keeping with the principle of least surprise. Even if the proxyInterfaces property of the
ProxyFactoryBean has been set to one or more fully qualified interface names, the fact that the proxyTargetClass
property is set to true causes CGLIB-based proxying to be in effect.
如果 ProxyFactoryBean 的 proxyTargetClass 属性已被设置为 true ,则会创建一个基于 CGLIB 的代理。这很有意义,并且符合最小惊讶原则。即使
ProxyFactoryBean 的 proxyInterfaces 属性已被设置为一个或多个完全限定的接口名称,但 proxyTargetClass 属性设置为
true 的事实会导致基于 CGLIB 的代理生效。
If the proxyInterfaces property of the ProxyFactoryBean has been set to one or more fully qualified interface names,
a JDK-based proxy is created. The created proxy implements all of the interfaces that were specified in the
proxyInterfaces property. If the target class happens to implement a whole lot more interfaces than those specified in
the proxyInterfaces property, that is all well and good, but those additional interfaces are not implemented by the
returned proxy.
如果 ProxyFactoryBean 的 proxyInterfaces 属性已被设置为一个或多个完全限定的接口名称,则会创建一个基于 JDK
的代理。创建的代理实现了在 proxyInterfaces 属性中指定的所有接口。如果目标类恰好实现了比在 proxyInterfaces
属性中指定的更多接口,那也很好,但这些额外的接口不会被返回的代理实现。
If the proxyInterfaces property of the ProxyFactoryBean has not been set, but the target class does implement one (
or more) interfaces, the ProxyFactoryBean auto-detects the fact that the target class does actually implement at least
one interface, and a JDK-based proxy is created. The interfaces that are actually proxied are all of the interfaces that
the target class implements.
如果 ProxyFactoryBean 的 proxyInterfaces 属性尚未设置,但目标类确实实现了(一个或多个)接口, ProxyFactoryBean
会自动检测目标类确实实现了至少一个接口,并创建一个基于 JDK 的代理。实际被代理的接口是目标类实现的所有接口。
In effect, this is the same as supplying a list of each and every interface that the target class implements to the
proxyInterfaces property. However, it is significantly less work and less prone to typographical errors.
实际上,这相当于向 proxyInterfaces 属性提供一个目标类实现的所有接口的列表。然而,这样做的工作量要小得多,且更不容易出错。
6.4.4. 代理接口
Consider a simple example of ProxyFactoryBean in action. This example involves:
考虑一个 ProxyFactoryBean 在行动中的简单示例。此示例涉及:
A target bean that is proxied. This is the personTarget bean definition in the example.
目标被代理的 Bean。这是示例中的 personTarget Bean 定义。
An Advisor and an Interceptor used to provide advice.
一个 Advisor 和一个 Interceptor 曾用来提供建议。
An AOP proxy bean definition to specify the target object (the personTarget bean), the interfaces to proxy, and the
advices to apply.
一个 AOP 代理 bean 定义,用于指定目标对象( personTarget bean)、要代理的接口以及要应用的建议。
The following listing shows the example:
以下列表显示了示例:
Note that the interceptorNames property takes a list of String, which holds the bean names of the interceptors or
advisors in the current factory. You can use advisors, interceptors, before, after returning, and throws advice objects.
The ordering of advisors is significant.
请注意, interceptorNames 属性接受一个 String 列表,该列表包含当前工厂中拦截器或顾问的 bean
名称。您可以使用顾问、拦截器、在返回之前、返回之后和抛出时使用建议对象。顾问的顺序很重要。
You might be wondering why the list does not hold bean references. The reason for this is that, if the singleton
property of the ProxyFactoryBean is set to false, it must be able to return independent proxy instances. If any of
the advisors is itself a prototype, an independent instance would need to be returned, so it is necessary to be able to
obtain an instance of the prototype from the factory. Holding a reference is not sufficient.
您可能想知道为什么列表中没有包含 bean 引用。这是因为,如果 ProxyFactoryBean 的 singleton 属性设置为 false
,它必须能够返回独立的代理实例。如果任何一个顾问本身是原型,就需要返回一个独立的实例,因此必须能够从工厂中获取原型的实例。仅仅持有引用是不够的。
The person bean definition shown earlier can be used in place of a Person implementation, as follows:
前面展示的 person 豆定义可以替代 Person 实现,如下所示:
Other beans in the same IoC context can express a strongly typed dependency on it, as with an ordinary Java object. The
following example shows how to do so:
其他在相同 IoC 上下文中的豆类可以像普通 Java 对象一样表达对其的强类型依赖。以下示例展示了如何实现:
The PersonUser class in this example exposes a property of type Person. As far as it is concerned, the AOP proxy can
be used transparently in place of a “real” person implementation. However, its class would be a dynamic proxy class. It
would be possible to cast it to the Advised interface (discussed later).
该示例中的 PersonUser 类公开了一个类型为 Person 的属性。就其本身而言,AOP 代理可以透明地替代“真实”人员实现。然而,它的类将是一个动态代理类。它可以被转换为
Advised 接口(稍后讨论)。
You can conceal the distinction between target and proxy by using an anonymous inner bean. Only the ProxyFactoryBean
definition is different. The advice is included only for completeness. The following example shows how to use an
anonymous inner bean:
您可以通过使用匿名内部 bean 来隐藏目标与代理之间的区别。只有 ProxyFactoryBean 定义不同。建议仅为了完整性而包含。以下示例展示了如何使用匿名内部
bean:
Using an anonymous inner bean has the advantage that there is only one object of type Person. This is useful if we
want to prevent users of the application context from obtaining a reference to the un-advised object or need to avoid
any ambiguity with Spring IoC autowiring. There is also, arguably, an advantage in that the ProxyFactoryBean
definition is self-contained. However, there are times when being able to obtain the un-advised target from the factory
might actually be an advantage (for example, in certain test scenarios).
使用匿名内部 Bean 的优势在于只有一个类型为 Person 的对象。如果我们想防止应用程序上下文的使用者获取到不推荐的对象引用,或者需要避免与
Spring IoC 自动装配的任何歧义,这将是有用的。此外,可以说, ProxyFactoryBean
的定义是自包含的。然而,在某些测试场景中,能够从工厂中获取到不推荐的目标实际上可能是一个优势。
What if you need to proxy a class, rather than one or more interfaces?
如果您需要代理一个类,而不是一个或多个接口呢?
Imagine that in our earlier example, there was no Person interface. We needed to advise a class called Person that
did not implement any business interface. In this case, you can configure Spring to use CGLIB proxying rather than
dynamic proxies. To do so, set the proxyTargetClass property on the ProxyFactoryBean shown earlier to true. While
it is best to program to interfaces rather than classes, the ability to advise classes that do not implement interfaces
can be useful when working with legacy code. (In general, Spring is not prescriptive.
想象在我们的早期示例中,没有 Person 接口。我们需要通知一个名为 Person 的类,该类没有实现任何业务接口。在这种情况下,您可以配置
Spring 使用 CGLIB 代理而不是动态代理。为此,将前面显示的 ProxyFactoryBean 上的 proxyTargetClass 属性设置为 true
。虽然最好面向接口编程而不是面向类,但在处理遗留代码时,能够通知未实现接口的类的能力可能很有用。(一般来说,Spring
并不强制规定。)
While it makes it easy to apply good practices, it avoids forcing a particular approach.)
虽然这使得应用良好实践变得容易,但它避免了强制采取特定方法。
If you want to, you can force the use of CGLIB in any case, even if you do have interfaces.
如果您愿意,即使在有接口的情况下,也可以强制使用 CGLIB。
CGLIB proxying works by generating a subclass of the target class at runtime. Spring configures this generated subclass
to delegate method calls to the original target. The subclass is used to implement the Decorator pattern, weaving in the
advice.
CGLIB 代理通过在运行时生成目标类的子类来实现。Spring 配置这个生成的子类将方法调用委托给原始目标。子类用于实现装饰器模式,编织进建议。
CGLIB proxying should generally be transparent to users. However, there are some issues to consider:
CGLIB 代理通常应该对用户透明。然而,有一些问题需要考虑:
Final methods cannot be advised, as they cannot be overridden.
Final 方法无法被建议,因为它们不能被重写。
There is no need to add CGLIB to your classpath. As of Spring 3.2, CGLIB is repackaged and included in the spring-core
JAR. In other words, CGLIB-based AOP works “out of the box”, as do JDK dynamic proxies.
无需将 CGLIB 添加到类路径中。从 Spring 3.2 版本开始,CGLIB 被重新打包并包含在 spring-core JAR 中。换句话说,基于 CGLIB 的
AOP 和 JDK 动态代理都可以“开箱即用”。
There is little performance difference between CGLIB proxying and dynamic proxies. Performance should not be a decisive
consideration in this case.
CGLIB 代理和动态代理之间的性能差异很小。在这种情况下,性能不应成为决定性因素。
6.4.6. 使用“全球”顾问
By appending an asterisk to an interceptor name, all advisors with bean names that match the part before the asterisk
are added to the advisor chain. This can come in handy if you need to add a standard set of “global” advisors. The
following example defines two global advisors:
通过在拦截器名称后添加一个星号,可以将所有具有与星号之前部分匹配的 bean
名称的顾问添加到顾问链中。这在你需要添加一组标准的“全局”顾问时非常有用。以下示例定义了两个全局顾问:
6.5. 简洁的代理定义
Especially when defining transactional proxies, you may end up with many similar proxy definitions. The use of parent
and child bean definitions, along with inner bean definitions, can result in much cleaner and more concise proxy
definitions.
特别是在定义事务代理时,你可能会得到许多类似的代理定义。使用父和子 Bean 定义,以及内部 Bean 定义,可以使代理定义更加简洁和清晰。
First, we create a parent, template, bean definition for the proxy, as follows:
首先,我们为代理创建一个父级、模板、bean 定义,如下所示:
This is never instantiated itself, so it can actually be incomplete. Then, each proxy that needs to be created is a
child bean definition, which wraps the target of the proxy as an inner bean definition, since the target is never used
on its own anyway.
这本身从未被实例化,因此实际上可能是不完整的。然后,每个需要创建的代理都是一个子 bean 定义,它将代理的目标作为内部 bean
定义包装起来,因为目标本身通常不会单独使用。
The following example shows such a child bean:
以下示例显示了一个这样的子 bean:
You can override properties from the parent template. In the following example, we override the transaction propagation
settings:
您可以覆盖父模板中的属性。在以下示例中,我们覆盖了事务传播设置:
Note that in the parent bean example, we explicitly marked the parent bean definition as being abstract by setting the
abstract attribute to true, as described previously, so that it may not actually
ever be instantiated. Application contexts (but not simple bean factories), by default, pre-instantiate all
singletons.
请注意,在父 bean 示例中,我们通过将 abstract 属性设置为 true ,明确地将父 bean
定义标记为抽象,如前所述,因此它实际上可能永远不会被实例化。应用程序上下文(但不是简单的 bean
工厂)默认情况下会预先实例化所有单例。
Therefore, it is important (at least for singleton beans) that, if you have a (parent) bean definition that you intend
to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to
true. Otherwise, the application context actually tries to pre-instantiate it.
因此,对于单例 bean 来说,非常重要(至少对于只打算用作模板的 bean 定义),如果这个定义指定了一个类,你必须确保设置 abstract
属性为 true 。否则,应用程序上下文实际上会尝试预先实例化它。
ProxyFactory6.6. 使用 ProxyFactory 以编程方式创建 AOP 代理
It is easy to create AOP proxies programmatically with Spring. This lets you use Spring AOP without dependency on Spring
IoC.
使用 Spring 可以轻松以编程方式创建 AOP 代理。这允许您在使用 Spring AOP 时无需依赖 Spring IoC。
The interfaces implemented by the target object are automatically proxied. The following listing shows creation of a
proxy for a target object, with one interceptor and one advisor:
目标对象实现的应用程序接口被自动代理。以下列表展示了为目标对象创建一个代理的过程,包含一个拦截器和一位顾问:
The first step is to construct an object of type org.springframework.aop.framework.ProxyFactory. You can create this
with a target object, as in the preceding example, or specify the interfaces to be proxied in an alternate
constructor.
第一步是构建一个类型为 org.springframework.aop.framework.ProxyFactory 的对象。您可以使用目标对象创建此对象,如前例所示,或者指定要代理的接口以使用另一个构造函数。
You can add advices (with interceptors as a specialized kind of advice), advisors, or both and manipulate them for the
life of the ProxyFactory. If you add an IntroductionInterceptionAroundAdvisor, you can cause the proxy to implement
additional interfaces.
您可以添加建议(包括拦截器作为建议的专门类型)、顾问或两者兼而有之,并在整个 ProxyFactory 的生命周期中操作它们。如果您添加一个
IntroductionInterceptionAroundAdvisor ,您可以使代理实现额外的接口。
There are also convenience methods on ProxyFactory (inherited from AdvisedSupport) that let you add other advice
types, such as before and throws advice. AdvisedSupport is the superclass of both ProxyFactory and
ProxyFactoryBean.
也存在在 ProxyFactory (继承自 AdvisedSupport )上的便利方法,允许您添加其他建议类型,例如在之前和抛出建议。
AdvisedSupport 是 ProxyFactory 和 ProxyFactoryBean 的超类。
Integrating AOP proxy creation with the IoC framework is best practice in most applications. We recommend that you
externalize configuration from Java code with AOP, as you should in general.
将 AOP 代理创建与 IoC 框架集成是大多数应用的最佳实践。我们建议您使用 AOP 将配置从 Java 代码外部化,这在一般情况下也是应该做的。
6.7. 操作建议对象
However you create AOP proxies, you can manipulate them BY using the org.springframework.aop.framework.Advised
interface. Any AOP proxy can be cast to this interface, no matter which other interfaces it implements. This interface
includes the following methods:
然而您如何创建 AOP 代理,您都可以通过使用 org.springframework.aop.framework.Advised 接口来操作它们。任何 AOP
代理都可以转换为这个接口,无论它实现了哪些其他接口。这个接口包括以下方法:
The getAdvisors() method returns an Advisor for every advisor, interceptor, or other advice type that has been added
to the factory. If you added an Advisor, the returned advisor at this index is the object that you added. If you added
an interceptor or other advice type, Spring wrapped this in an advisor with a pointcut that always returns true. Thus,
if you added a MethodInterceptor, the advisor returned for this index is a DefaultPointcutAdvisor that returns your
MethodInterceptor and a pointcut that matches all classes and methods.
该方法返回工厂中添加的每个顾问、拦截器或其他建议类型的 Advisor 。如果您添加了 Advisor
,则此索引处返回的顾问是您添加的对象。如果您添加了拦截器或其他建议类型,Spring 将其包装在一个顾问中,该顾问具有始终返回
true 的切点。因此,如果您添加了 MethodInterceptor ,则返回此索引的顾问是一个返回您的 MethodInterceptor 和匹配所有类和方法的切点的
DefaultPointcutAdvisor 。
The addAdvisor() methods can be used to add any Advisor. Usually, the advisor holding pointcut and advice is the
generic DefaultPointcutAdvisor, which you can use with any advice or pointcut (but not for introductions).
addAdvisor() 方法可用于添加任何 Advisor 。通常,持有切入点和建议的顾问是通用的 DefaultPointcutAdvisor
,您可以使用任何建议或切入点(但不能用于介绍)。
By default, it is possible to add or remove advisors or interceptors even once a proxy has been created. The only
restriction is that it is impossible to add or remove an introduction advisor, as existing proxies from the factory do
not show the interface change.
默认情况下,即使创建了一个代理,也可以添加或删除顾问或拦截器。唯一的限制是,无法添加或删除介绍型顾问,因为来自工厂的现有代理不会显示界面更改。
(You can obtain a new proxy from the factory to avoid this problem.)
(您可以从工厂获取一个新的代理来避免这个问题。)
The following example shows casting an AOP proxy to the Advised interface and examining and manipulating its advice:
以下示例展示了将 AOP 代理转换为 Advised 接口,并检查和操作其建议:
It is questionable whether it is advisable (no pun intended) to modify advice on a business object in production,
although there are, no doubt, legitimate usage cases. However, it can be very useful in development (for example, in
tests).
关于在生产环境中修改业务对象的建议是否可取(无意为之),这一点值得怀疑,尽管无疑存在合法的使用场景。然而,在开发过程中(例如,在测试中)这可能会非常有用。
We have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice,
getting inside a method invocation that we want to test. (For example, the advice can get inside a transaction created
for that method, perhaps to run SQL to check that a database was correctly updated, before marking the transaction for
roll back.)
我们有时发现能够以拦截器或其他建议的形式添加测试代码非常有用,这样我们就可以进入我们想要测试的方法调用中。(例如,建议可以进入为该方法创建的事务中,可能运行
SQL 来检查数据库是否正确更新,然后在标记事务回滚之前。)
Depending on how you created the proxy, you can usually set a frozen flag. In that case, the Advised isFrozen()
method returns true, and any attempts to modify advice through addition or removal results in an AopConfigException.
The ability to freeze the state of an advised object is useful in some cases (for example, to prevent calling code
removing a security interceptor).
根据您创建代理的方式,您通常可以设置一个 frozen 标志。在这种情况下, Advised isFrozen() 方法返回 true
,并且任何通过添加或删除来修改建议的尝试都会导致一个 AopConfigException 。在某些情况下,冻结建议对象的状态是有用的(例如,防止调用代码移除安全拦截器)。
6.8. 使用“自动代理”功能
So far, we have considered explicit creation of AOP proxies by using a ProxyFactoryBean or similar factory bean.
到目前为止,我们已经考虑了通过使用 ProxyFactoryBean 或类似工厂 bean 显式创建 AOP 代理。
Spring also lets us use “auto-proxy” bean definitions, which can automatically proxy selected bean definitions. This is
built on Spring’s “bean post processor” infrastructure, which enables modification of any bean definition as the
container loads.
Spring 还允许我们使用“自动代理”的 bean 定义,它可以自动代理选定的 bean 定义。这是基于 Spring 的“bean
后处理器”基础设施,它使得在容器加载时可以修改任何 bean 定义。
In this model, you set up some special bean definitions in your XML bean definition file to configure the auto-proxy
infrastructure. This lets you declare the targets eligible for auto-proxying. You need not use ProxyFactoryBean.
在这个模型中,您在 XML Bean 定义文件中设置了一些特殊的 Bean 定义,以配置自动代理基础设施。这使得您可以声明可自动代理的目标。您不需要使用
ProxyFactoryBean 。
There are two ways to do this:
有两种方法来做这件事:
By using an auto-proxy creator that refers to specific beans in the current context.
通过使用引用当前上下文中特定 bean 的自动代理创建器。
A special case of auto-proxy creation that deserves to be considered separately: auto-proxy creation driven by
source-level metadata attributes.
一个值得单独考虑的自动代理创建的特殊情况:由源级元数据属性驱动的自动代理创建。
6.8.1. 自动代理 Bean 定义
This section covers the auto-proxy creators provided by the org.springframework.aop.framework.autoproxy package.
本节涵盖了由 org.springframework.aop.framework.autoproxy 包提供的自动代理创建器。
BeanNameAutoProxyCreatorThe BeanNameAutoProxyCreator class is a BeanPostProcessor that automatically creates AOP proxies for beans with
names that match literal values or wildcards. The following example shows how to create a BeanNameAutoProxyCreator
bean:
BeanNameAutoProxyCreator 类是一个自动为名称匹配字面值或通配符的 bean 创建 AOP 代理的 BeanPostProcessor
。以下示例展示了如何创建一个 BeanNameAutoProxyCreator bean:
As with ProxyFactoryBean, there is an interceptorNames property rather than a list of interceptors, to allow correct
behavior for prototype advisors. Named “interceptors” can be advisors or any advice type.
与 ProxyFactoryBean 一样,存在一个 interceptorNames 属性而不是拦截器列表,以允许原型顾问的正确行为。名为“interceptors”可以是顾问或任何建议类型。
As with auto-proxying in general, the main point of using BeanNameAutoProxyCreator is to apply the same configuration
consistently to multiple objects, with minimal volume of configuration. It is a popular choice for applying declarative
transactions to multiple objects.
与一般自动代理一样,使用 BeanNameAutoProxyCreator 的主要目的是将相同的配置一致地应用于多个对象,配置量最小。它是将声明性事务应用于多个对象的流行选择。
Bean definitions whose names match, such as jdkMyBean and onlyJdk in the preceding example, are plain old bean
definitions with the target class. An AOP proxy is automatically created by the BeanNameAutoProxyCreator. The same
advice is applied to all matching beans. Note that, if advisors are used (rather than the interceptor in the preceding
example), the pointcuts may apply differently to different beans.
匹配名称的 Bean 定义,如前例中的 jdkMyBean 和 onlyJdk ,是带有目标类的普通 Bean 定义。通过 BeanNameAutoProxyCreator
自动创建 AOP 代理。相同的建议应用于所有匹配的 Bean。请注意,如果使用顾问(而不是前例中的拦截器),切点可能对不同 Bean 有不同的应用。
DefaultAdvisorAutoProxyCreatorA more general and extremely powerful auto-proxy creator is DefaultAdvisorAutoProxyCreator. This automagically applies
eligible advisors in the current context, without the need to include specific bean names in the auto-proxy advisor’s
bean definition. It offers the same merit of consistent configuration and avoidance of duplication as
BeanNameAutoProxyCreator.
一个更通用且极其强大的自动代理创建器是 DefaultAdvisorAutoProxyCreator 。它自动应用当前上下文中的合格顾问,无需在自动代理顾问的
bean 定义中包含特定的 bean 名称。它提供了与 BeanNameAutoProxyCreator 相同的优点,即一致的配置和避免重复。
Using this mechanism involves:
使用此机制涉及:
Specifying a DefaultAdvisorAutoProxyCreator bean definition.
指定一个 DefaultAdvisorAutoProxyCreator 面豆定义。
Specifying any number of advisors in the same or related contexts. Note that these must be advisors, not interceptors
or other advices. This is necessary, because there must be a pointcut to evaluate, to check the eligibility of each
advice to candidate bean definitions.
指定相同或相关上下文中的任意数量的顾问。请注意,这些必须是顾问,而不是拦截器或其他建议。这是必要的,因为必须有一个切入点来评估,以检查每条建议对候选
bean 定义的适用性。
The DefaultAdvisorAutoProxyCreator automatically evaluates the pointcut contained in each advisor, to see what (if
any) advice it should apply to each business object (such as businessObject1 and businessObject2 in the example).
The DefaultAdvisorAutoProxyCreator automatically evaluates the pointcut contained in each advisor, to see what (if
any) advice it should apply to each business object (such as businessObject1 and businessObject2 in the example). 该
DefaultAdvisorAutoProxyCreator自动评估每个顾问中包含的点切,以查看它应该应用于每个业务对象(例如示例中的
businessObject1和businessObject2)的建议(如果有的话)。
This means that any number of advisors can be applied automatically to each business object. If no pointcut in any of
the advisors matches any method in a business object, the object is not proxied.
这意味着可以自动将任意数量的顾问应用于每个业务对象。如果任何顾问中的切入点都不匹配业务对象中的任何方法,则该对象不会被代理。
As bean definitions are added for new business objects, they are automatically proxied if necessary.
随着为新业务对象添加 Bean 定义,如果需要,它们将自动进行代理。
Auto-proxying in general has the advantage of making it impossible for callers or dependencies to obtain an un-advised
object. Calling getBean("businessObject1") on this ApplicationContext returns an AOP proxy, not the target business
object. (The “inner bean” idiom shown earlier also offers this benefit.)
自动代理通常具有使调用者或依赖项无法获取不推荐的对象的优势。调用此 ApplicationContext 上的 getBean("businessObject1")
将返回一个 AOP 代理,而不是目标业务对象。(之前展示的“内部豆”习语也提供了这种好处。)
The following example creates a DefaultAdvisorAutoProxyCreator bean and the other elements discussed in this
section:
以下示例创建了一个 DefaultAdvisorAutoProxyCreator 的 bean 以及本节中讨论的其他元素:
The DefaultAdvisorAutoProxyCreator is very useful if you want to apply the same advice consistently to many business
objects. Once the infrastructure definitions are in place, you can add new business objects without including specific
proxy configuration.
DefaultAdvisorAutoProxyCreator
在您希望将相同的建议持续应用于许多业务对象时非常有用。一旦基础设施定义就位,您就可以添加新的业务对象,而无需包括特定的代理配置。
You can also easily drop in additional aspects (for example, tracing or performance monitoring aspects) with minimal
change to configuration.
您也可以轻松地添加额外的方面(例如,跟踪或性能监控方面),而对配置的更改最小。
The DefaultAdvisorAutoProxyCreator offers support for filtering (by using a naming convention so that only certain
advisors are evaluated, which allows the use of multiple, differently configured, AdvisorAutoProxyCreators in the same
factory) and ordering. Advisors can implement the org.springframework.core.Ordered interface to ensure correct
ordering if this is an issue. The TransactionAttributeSourceAdvisor used in the preceding example has a configurable
order value. The default setting is unordered.
The DefaultAdvisorAutoProxyCreator 提供了对过滤(通过使用命名约定,以便仅评估某些顾问,这允许在同一个工厂中使用多个不同配置的
AdvisorAutoProxyCreators)和排序的支持。顾问可以通过实现 org.springframework.core.Ordered 接口来确保正确的排序,如果这是一个问题。前一个示例中使用的
TransactionAttributeSourceAdvisor 具有可配置的顺序值。默认设置是无序的。
TargetSource Implementations6.9. 使用 TargetSource 实现
Spring offers the concept of a TargetSource, expressed in the org.springframework.aop.TargetSource interface. This
interface is responsible for returning the “target object” that implements the join point. The TargetSource
implementation is asked for a target instance each time the AOP proxy handles a method invocation.
Spring 提供了 TargetSource 的概念,该概念在 org.springframework.aop.TargetSource 接口中表达。该接口负责返回实现连接点的“目标对象”。每次
AOP 代理处理方法调用时,都会请求 TargetSource 实现提供一个目标实例。
Developers who use Spring AOP do not normally need to work directly with TargetSource implementations, but this
provides a powerful means of supporting pooling, hot swappable, and other sophisticated targets. For example, a pooling
TargetSource can return a different target instance for each invocation, by using a pool to manage instances.
使用 Spring AOP 的开发者通常不需要直接与 TargetSource 实现打交道,但这提供了一种强大的支持池化、热插拔和其他复杂目标的方法。例如,一个池化
TargetSource 可以通过使用池来管理实例,为每次调用返回不同的目标实例。
If you do not specify a TargetSource, a default implementation is used to wrap a local object. The same target is
returned for each invocation (as you would expect).
如果您未指定 TargetSource ,将使用默认实现来包装本地对象。每次调用都会返回相同的目标(正如您所期望的)。
The rest of this section describes the standard target sources provided with Spring and how you can use them.
本节其余部分描述了 Spring 提供的标准目标源以及如何使用它们。
When using a custom target source, your target will usually need to be a prototype rather than a singleton bean
definition. This allows Spring to create a new target instance when required.
当使用自定义目标源时,您的目标通常需要是一个原型而不是单例 bean 定义。这允许 Spring 在需要时创建一个新的目标实例。
6.9.1. 热插拔目标源
The org.springframework.aop.target.HotSwappableTargetSource exists to let the target of an AOP proxy be switched while
letting callers keep their references to it.
org.springframework.aop.target.HotSwappableTargetSource 的存在是为了在切换 AOP 代理的目标的同时,让调用者保持对其的引用。
Changing the target source’s target takes effect immediately. The HotSwappableTargetSource is thread-safe.
更改目标源的目标立即生效。 HotSwappableTargetSource 是线程安全的。
You can change the target by using the swap() method on HotSwappableTargetSource, as the follow example shows:
您可以通过在 HotSwappableTargetSource 上使用 swap() 方法来更改目标,如下例所示:
The following example shows the required XML definitions:
以下示例显示了所需的 XML 定义:
The preceding swap() call changes the target of the swappable bean. Clients that hold a reference to that bean are
unaware of the change but immediately start hitting the new target.
前一个 swap() 调用更改了可交换 bean 的目标。持有该 bean 引用的客户端对这一变化浑然不觉,但立即开始撞击新的目标。
Although this example does not add any advice (it is not necessary to add advice to use a TargetSource), any
TargetSource can be used in conjunction with arbitrary advice.
尽管这个例子没有添加任何建议(使用 TargetSource 不需要添加建议),任何 TargetSource 都可以与任意建议一起使用。
6.9.2. 池化目标来源
Using a pooling target source provides a similar programming model to stateless session EJBs, in which a pool of
identical instances is maintained, with method invocations going to free objects in the pool.
使用池化目标源提供与无状态会话 EJBs 相似的编程模型,其中维护一个相同实例的池,方法调用流向池中的空闲对象。
A crucial difference between Spring pooling and SLSB pooling is that Spring pooling can be applied to any POJO. As with
Spring in general, this service can be applied in a non-invasive way.
Spring 池化和 SLSB 池化之间的一个关键区别是,Spring 池化可以应用于任何 POJO。与 Spring 通常一样,这项服务可以以非侵入性的方式应用。
Spring provides support for Commons Pool 2.2, which provides a fairly efficient pooling implementation. You need the
commons-pool Jar on your application’s classpath to use this feature. You can also subclass
org.springframework.aop.target.AbstractPoolingTargetSource to support any other pooling API.
Spring 支持 Commons Pool 2.2,它提供了一个相当高效的池化实现。您需要在应用程序的类路径上包含 commons-pool Jar
才能使用此功能。您还可以从 org.springframework.aop.target.AbstractPoolingTargetSource 派生子类以支持任何其他池化 API。
Commons Pool 1.5+ is also supported but is deprecated as of Spring Framework 4.2.
Commons Pool 1.5+ 也受支持,但自 Spring Framework 4.2 起已弃用。
The following listing shows an example configuration:
以下列表显示了一个示例配置:
Note that the target object (businessObjectTarget in the preceding example) must be a prototype. This lets the
PoolingTargetSource implementation create new instances of the target to grow the pool as necessary. See
the javadoc of
AbstractPoolingTargetSource
and the concrete subclass you wish to use for information about its properties. maxSize is the most basic and is
always guaranteed to be present.
请注意,目标对象(前一个示例中的 businessObjectTarget )必须是一个原型。这允许 PoolingTargetSource 实现创建目标的新实例以按需扩展池。请参阅
AbstractPoolingTargetSource 的 javadoc 以及您希望使用的具体子类以获取有关其属性的信息。 maxSize 是最基本的,并且始终保证存在。
In this case, myInterceptor is the name of an interceptor that would need to be defined in the same IoC context.
However, you need not specify interceptors to use pooling. If you want only pooling and no other advice, do not set the
interceptorNames property at all.
在这种情况下, myInterceptor 是一个需要在同一 IoC 上下文中定义的拦截器的名称。然而,您不需要指定拦截器来使用池化。如果您只想使用池化而不需要其他建议,则根本不要设置
interceptorNames 属性。
You can configure Spring to be able to cast any pooled object to the org.springframework.aop.target.PoolingConfig
interface, which exposes information about the configuration and current size of the pool through an introduction. You
need to define an advisor similar to the following:
您可以将 Spring 配置为能够将任何池化对象转换为 org.springframework.aop.target.PoolingConfig
接口,通过介绍暴露有关配置和当前池大小的信息。您需要定义一个类似于以下顾问:
This advisor is obtained by calling a convenience method on the AbstractPoolingTargetSource class, hence the use of
MethodInvokingFactoryBean. This advisor’s name (poolConfigAdvisor, here) must be in the list of interceptors names
in the ProxyFactoryBean that exposes the pooled object.
此顾问通过在 AbstractPoolingTargetSource 类上调用便捷方法获得,因此使用了 MethodInvokingFactoryBean 。此顾问的名称(
poolConfigAdvisor ,此处)必须在暴露池化对象的 ProxyFactoryBean 拦截器名称列表中。
The cast is defined as follows:
演员阵容如下定义:
Pooling stateless service objects is not usually necessary. We do not believe it should be the default choice, as most
stateless objects are naturally thread safe, and instance pooling is problematic if resources are cached.
池化无状态服务对象通常是不必要的。我们不认为它应该是默认选择,因为大多数无状态对象自然是线程安全的,如果资源被缓存,实例池化会有问题。
Simpler pooling is available by using auto-proxying. You can set the TargetSource implementations used by any
auto-proxy creator.
通过使用自动代理,可以实现更简单的池化。您可以设置任何自动代理创建者使用的 TargetSource 实现。
6.9.3. 原型目标源
Setting up a “prototype” target source is similar to setting up a pooling TargetSource. In this case, a new instance
of the target is created on every method invocation. Although the cost of creating a new object is not high in a modern
JVM, the cost of wiring up the new object (satisfying its IoC dependencies) may be more expensive.
设置一个“原型”目标源类似于设置一个池化 TargetSource 。在这种情况下,每次方法调用都会创建目标的新实例。尽管在现代化的 JVM
中创建新对象的成本不高,但连接新对象(满足其 IoC 依赖)的成本可能更高。
Thus, you should not use this approach without very good reason.
因此,除非有非常充分的理由,否则您不应使用这种方法。
To do this, you could modify the poolTargetSource definition shown earlier as follows (we also changed the name, for
clarity):
为此,您可以修改前面显示的 poolTargetSource 定义如下(我们也更改了名称,以便于理解):
The only property is the name of the target bean. Inheritance is used in the TargetSource implementations to ensure
consistent naming. As with the pooling target source, the target bean must be a prototype bean definition.
目标对象的唯一属性是名称。在 TargetSource 实现中,使用继承来确保命名的一致性。与池化目标源一样,目标对象必须是一个原型
bean 定义。
ThreadLocal Target Sources6.9.4. ThreadLocal 目标来源
ThreadLocal target sources are useful if you need an object to be created for each incoming request (per thread that
is). The concept of a ThreadLocal provides a JDK-wide facility to transparently store a resource alongside a thread.
Setting up a ThreadLocalTargetSource is pretty much the same as was explained for the other types of target source, as
the following example shows:
目标源在需要为每个传入请求(每个线程)创建一个对象时很有用。 ThreadLocal 的概念为 JDK 提供了一个跨线程透明存储资源的设施。设置
ThreadLocalTargetSource 与之前解释的其他类型的目标源设置基本相同,以下示例将展示这一点:
ThreadLocal instances come with serious issues (potentially resulting in memory leaks) when incorrectly using them in
multi-threaded and multi-classloader environments. You should always consider wrapping a threadlocal in some other class
and never directly use the ThreadLocal itself (except in the wrapper class). Also, you should always remember to
correctly set and unset (where the latter simply involves a call to ThreadLocal.set(null)) the resource local to the
thread. Unsetting should be done in any case, since not unsetting it might result in problematic behavior. Spring’s
ThreadLocal support does this for you and should always be considered in favor of using ThreadLocal instances
without other proper handling code.
ThreadLocal 实例在多线程和多类加载器环境中使用不当时会存在严重问题(可能导致内存泄漏)。您应该始终考虑将 threadlocal
包装在其他类中,并且永远不要直接使用 ThreadLocal 本身(除非在包装类中)。此外,您应该始终记住正确设置和取消设置(后者仅涉及对
ThreadLocal.set(null) 的调用)线程本地的资源。在任何情况下都应该取消设置,因为不取消设置可能会导致问题行为。Spring 的
ThreadLocal 支持为您做了这件事,并且应该始终优先考虑,而不是使用 ThreadLocal 实例而不进行其他适当的处理代码。
6.10. 定义新的建议类型
Spring AOP is designed to be extensible. While the interception implementation strategy is presently used internally, it
is possible to support arbitrary advice types in addition to the interception around advice, before, throws advice, and
after returning advice.
Spring AOP 设计为可扩展。虽然当前内部使用拦截实现策略,但它可以支持除了拦截通知、前置通知、抛出通知和返回后通知之外,任意类型的建议。
The org.springframework.aop.framework.adapter package is an SPI package that lets support for new custom advice types
be added without changing the core framework. The only constraint on a custom Advice type is that it must implement
the org.aopalliance.aop.Advice marker interface.
org.springframework.aop.framework.adapter 包是一个 SPI 包,允许在不更改核心框架的情况下添加对新自定义建议类型的支持。对自定义
Advice 类型的唯一约束是它必须实现 org.aopalliance.aop.Advice 标记接口。