现在我们可以研究一下 Spring AOP 如何处理建议。
建议生命周期
每个建议都是一个春天的豆子。建议实例可以在所有建议之间共享 对象或对每个建议的对象是唯一的。这对应于每个类或 每个实例的建议。
最常使用每类建议。它适用于一般建议,例如 交易顾问。这些不依赖于代理对象的状态或添加新的 州。他们只是根据方法和论点行事。
每个实例的建议适用于介绍,以支持 mixins。在这种情况下, 该建议将状态添加到代理对象。
您可以在同一个 AOP 代理中混合使用共享建议和每个实例的建议。
春季的建议类型
Spring 提供了多种建议类型,并且可扩展以支持 任意建议类型。本节介绍基本概念和标准建议类型。
拦截建议
Spring 中最基本的建议类型是围绕建议的拦截。
Spring 兼容 AOP 接口,用于使用方法的 around 建议
拦截。实施和围绕建议实施的类也应该实施
以下接口:Alliance
MethodInterceptor
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
该方法的参数公开了该方法
invoked、目标联接点、AOP 代理和方法的参数。该方法应返回调用的结果:联接的返回值
点。MethodInvocation
invoke()
invoke()
以下示例演示了一个简单的实现:MethodInterceptor
-
Java
-
Kotlin
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
class DebugInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any {
println("Before: invocation=[$invocation]")
val rval = invocation.proceed()
println("Invocation returned")
return rval
}
}
请注意对 的方法的调用。这将沿着
朝向连接点的拦截链。大多数侦听器都调用此方法,并且
返回其返回值。但是,像周围的任何建议一样,可以
返回不同的值或引发异常,而不是调用 Proceed 方法。
但是,您不想无缘无故地这样做。proceed()
MethodInvocation
MethodInterceptor
MethodInterceptor 实现提供与其他符合 AOP 联盟标准的 AOP 的互操作性
实现。本节其余部分讨论的其他建议类型
以特定于 Spring 的方式实现常见的 AOP 概念。虽然有一个优势
在使用最具体的建议类型时,如果出现以下情况,请坚持使用周围的建议
您可能希望在另一个 AOP 框架中运行该方面。请注意,点切口
目前不能在框架之间互操作,AOP 联盟也无法互操作
当前定义 Pointcut 接口。MethodInterceptor |
建议前
更简单的建议类型是之前的建议。这不需要对象,因为它仅在输入方法之前调用。MethodInvocation
事先建议的主要优点是无需调用该方法,因此,不可能无意中无法继续执行
拦截链。proceed()
以下列表显示了该接口:MethodBeforeAdvice
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
(Spring 的 API 设计将允许 在建议之前进行字段,尽管通常的对象适用于字段拦截,并且是 Spring 不太可能实现它。
请注意,返回类型为 。在建议可以在加入之前插入自定义行为之前
点运行,但无法更改返回值。如果之前的建议抛出一个
异常,它会停止拦截器链的进一步执行。例外
沿拦截器链向上传播。如果它未选中或签名
调用的方法,它直接传递给客户端。否则,它是
由 AOP 代理包装在未经检查的异常中。void
以下示例显示了 Spring 中的 before 建议,该建议对所有方法调用进行计数:
-
Java
-
Kotlin
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
class CountingBeforeAdvice : MethodBeforeAdvice {
var count: Int = 0
override fun before(m: Method, args: Array<Any>, target: Any?) {
++count
}
}
在建议可以与任何切入点一起使用之前。 |
抛出建议
如果连接点抛出,则在连接点返回后调用抛出建议
异常。Spring 提供打字投掷建议。请注意,这意味着该接口不包含任何方法。这是一个
标记接口,标识给定对象实现一个或多个类型化触发
建议方法。这些应采用以下形式:org.springframework.aop.ThrowsAdvice
afterThrowing([Method, args, target], subclassOfThrowable)
只需要最后一个参数。方法签名可以有一个或四个 参数,取决于建议方法是否对该方法感兴趣,并且 参数。接下来的两个列表显示了作为抛出建议示例的类。
如果抛出 a(包括从子类中抛出),则调用以下建议:RemoteException
-
Java
-
Kotlin
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
class RemoteThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
}
与前面不同
建议,下一个示例声明了四个参数,以便它有权访问调用的方法
参数和目标对象。如果抛出 a,则调用以下建议:ServletException
-
Java
-
Kotlin
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
class ServletThrowsAdviceWithArguments : ThrowsAdvice {
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
最后一个示例说明了如何在单个类中使用这两种方法
处理 和 .任意数量的投掷建议
方法可以组合在单个类中。以下列表显示了最后一个示例:RemoteException
ServletException
-
Java
-
Kotlin
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
class CombinedThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
如果 throws-advice 方法本身引发异常,它将覆盖 原始异常(即,它更改了抛给用户的异常)。压倒一切 exception 通常是 RuntimeException,它与任何方法兼容 签名。但是,如果 throws-advice 方法引发已检查的异常,则必须 匹配目标方法的声明异常,因此在某种程度上是 耦合到特定的目标方法签名。不要抛出未声明的支票 与目标方法的签名不兼容的异常! |
投掷建议可以与任何切点一起使用。 |
返回建议后
在 Spring 中返回建议后必须实现该接口,如下表所示:org.springframework.aop.AfterReturningAdvice
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
返回建议后可以访问返回值(它不能修改), 调用的方法、方法的参数和目标。
返回建议后的以下内容将计算所有成功的方法调用,这些方法具有 未抛出异常:
-
Java
-
Kotlin
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
class CountingAfterReturningAdvice : AfterReturningAdvice {
var count: Int = 0
private set
override fun afterReturning(returnValue: Any?, m: Method, args: Array<Any>, target: Any?) {
++count
}
}
此建议不会更改执行路径。如果它抛出异常,则 抛出拦截器链而不是返回值。
返回后,建议可以与任何切入点一起使用。 |
介绍建议
Spring将介绍建议视为一种特殊的拦截建议。
简介需要 an 和 that
实现以下接口:IntroductionAdvisor
IntroductionInterceptor
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
继承自 AOP Alliance 接口的方法必须
实现介绍。也就是说,如果调用的方法位于引入的
接口,引入拦截器负责处理方法调用——它
无法调用 。invoke()
MethodInterceptor
proceed()
介绍建议不能与任何切入点一起使用,因为它仅适用于课堂,
而不是方法,水平。您只能将介绍建议与 一起使用,它具有以下方法:IntroductionAdvisor
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class<?>[] getInterfaces();
}
没有,因此没有与介绍相关的
建议。只有类筛选是合乎逻辑的。MethodMatcher
Pointcut
该方法返回此顾问引入的接口。getInterfaces()
该方法在内部用于查看
引入的接口可以通过配置的 .validateInterfaces()
IntroductionInterceptor
考虑 Spring 测试套件中的一个示例,假设我们想要 将以下接口引入一个或多个对象:
-
Java
-
Kotlin
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
interface Lockable {
fun lock()
fun unlock()
fun locked(): Boolean
}
这说明了一种混合。我们希望能够将建议的对象投射到 ,
无论其类型和调用锁定和解锁方法如何。如果我们调用该方法,则
希望所有 setter 方法都抛出 .因此,我们可以添加一个方面
提供了使对象不可变的能力,而对象对此一无所知:
AOP的一个很好的例子。Lockable
lock()
LockedException
首先,我们需要一个能够完成繁重工作的人。在这个
案例,我们扩展了便利类。我们可以直接实现,但在大多数情况下使用是最好的。IntroductionInterceptor
org.springframework.aop.support.DelegatingIntroductionInterceptor
IntroductionInterceptor
DelegatingIntroductionInterceptor
旨在将介绍委托给
实际实现引入的接口,隐藏使用拦截
这样做。您可以使用构造函数参数将委托设置为任何对象。这
默认委托(当使用 no-argument 构造函数时)为 。因此,在下一个示例中,
委托是 的子类。
给定一个委托(默认情况下,它本身),一个实例
查找委托实现的所有接口(除 之外),并支持针对其中任何一个接口的介绍。
子类(如 可以调用该方法来抑制不应公开的接口)。但是,无论多少
接口准备支持,所使用的控件实际公开了哪些接口。一
引入的接口隐藏了目标对同一接口的任何实现。DelegatingIntroductionInterceptor
this
LockMixin
DelegatingIntroductionInterceptor
DelegatingIntroductionInterceptor
IntroductionInterceptor
LockMixin
suppressInterface(Class intf)
IntroductionInterceptor
IntroductionAdvisor
因此,扩展和实现自身。超类会自动拾取可以支持的超类
简介,所以我们不需要指定。我们可以引入任意数量的
以这种方式接口。LockMixin
DelegatingIntroductionInterceptor
Lockable
Lockable
请注意实例变量的用法。这有效地增加了额外的状态
到目标对象中保存的内容。locked
下面的示例显示了示例类:LockMixin
-
Java
-
Kotlin
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new LockedException();
}
return super.invoke(invocation);
}
}
class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
private var locked: Boolean = false
fun lock() {
this.locked = true
}
fun unlock() {
this.locked = false
}
fun locked(): Boolean {
return this.locked
}
override fun invoke(invocation: MethodInvocation): Any? {
if (locked() && invocation.method.name.indexOf("set") == 0) {
throw LockedException()
}
return super.invoke(invocation)
}
}
通常,您不需要重写该方法。实现(调用方法
引入该方法,否则通常朝着连接点前进
够。在本例中,我们需要添加一个检查:不能调用任何 setter 方法
如果处于锁定模式。invoke()
DelegatingIntroductionInterceptor
delegate
所需的介绍只需要保存一个不同的实例并指定引入的接口(在本例中,仅)。一个更复杂的示例可能会引用引言
拦截器(将被定义为原型)。在这种情况下,没有
与 相关的配置,因此我们使用 创建它。
下面的示例显示了我们的类:LockMixin
Lockable
LockMixin
new
LockMixinAdvisor
-
Java
-
Kotlin
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
我们可以非常简单地应用这个顾问,因为它不需要配置。(但是,它
没有 .) 就不可能使用 。与介绍一样,顾问必须是每个实例,
因为它是有状态的。对于每个建议的对象,我们需要一个不同的 和 实例。顾问包括被建议对象的一部分
州。IntroductionInterceptor
IntroductionAdvisor
LockMixinAdvisor
LockMixin
我们可以使用以下方法以编程方式应用此顾问程序
(推荐的方式)在 XML 配置中,就像任何其他顾问一样。所有代理创建
下面讨论的选项(包括“自动代理创建者”)可以正确处理介绍
和有状态的混合。Advised.addAdvisor()