299 lines
15 KiB
Markdown
299 lines
15 KiB
Markdown
|
## AdvisorAdapter
|
|||
|
|
|||
|
- [AdvisorAdapter](#AdvisorAdapter)
|
|||
|
- [一、基本信息](#一基本信息)
|
|||
|
- [二、知识储备](#二知识储备)
|
|||
|
- [三、基本描述](#三基本描述)
|
|||
|
- [四、主要功能](#四主要功能)
|
|||
|
- [五、接口源码](#五接口源码)
|
|||
|
- [六、主要实现](#六主要实现)
|
|||
|
- [七、最佳实践](#七最佳实践)
|
|||
|
- [八、与其他组件的关系](#八与其他组件的关系)
|
|||
|
- [九、常见问题](#九常见问题)
|
|||
|
|
|||
|
### 一、基本信息
|
|||
|
|
|||
|
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
|
|||
|
|
|||
|
### 二、知识储备
|
|||
|
|
|||
|
1. **Advice**
|
|||
|
|
|||
|
+ 在目标方法执行前、后或出现异常时执行的逻辑。通知是 AOP 横切逻辑的具体实现,可以在切点之前、切点之后、抛出异常时或者环绕一个连接点来执行。常见的通知类型包括前置通知(Before Advice)、后置通知(After Advice)、返回通知(After Returning Advice)和异常通知(After Throwing Advice)等。
|
|||
|
|
|||
|
2. **Advisor**
|
|||
|
|
|||
|
+ 用于将切面(Aspect)与切点(Pointcut)以及通知(Advice)关联起来。`Advisor` 实质上是切面的逻辑定义,它定义了切点和通知之间的关系,并提供了一种方式将它们组合在一起。Spring 提供了多种类型的 Advisor,例如 `DefaultPointcutAdvisor`、`AspectJExpressionPointcutAdvisor` 等。
|
|||
|
|
|||
|
3. **Pointcut**
|
|||
|
|
|||
|
+ 用于描述切面在何处生效的条件,即在哪些连接点上应用通知。切点定义了一组连接点,它们是应用通知的目标方法。Spring 提供了多种类型的切点表达式,如基于名称、基于方法签名、基于 AspectJ 表达式等。
|
|||
|
|
|||
|
4. **设计模式**
|
|||
|
|
|||
|
+ 适配器模式是一种结构型设计模式,它通过将一个类的接口转换成客户端所期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
|
|||
|
|
|||
|
### 三、基本描述
|
|||
|
|
|||
|
`AdvisorAdapter` 接口是 Spring AOP 中的一个重要接口,用于将不同类型的通知(Advice)适配到拦截器链中,以便将其应用于目标方法的执行。它允许我们自定义适配器来将自定义的通知与 Spring AOP 框架结合,从而实现对目标方法的前置、后置、环绕等类型的增强操作,为 AOP 的灵活性和可扩展性提供了支持。
|
|||
|
|
|||
|
### 四、主要功能
|
|||
|
|
|||
|
1. **通知适配**
|
|||
|
|
|||
|
+ 将不同类型的通知(Advice)适配到 Spring AOP 拦截器链中,以便将其应用于目标方法的执行。
|
|||
|
|
|||
|
2. **支持不同通知类型**
|
|||
|
|
|||
|
+ 支持适配各种类型的通知,包括前置通知(MethodBeforeAdvice)、后置通知(AfterReturningAdvice)、环绕通知(MethodInterceptor)、抛出异常通知(ThrowsAdvice)等。
|
|||
|
|
|||
|
3. **适配器注册和管理**
|
|||
|
|
|||
|
+ 允许我们注册和管理不同类型通知的适配器,以便在应用中使用不同类型的通知。
|
|||
|
|
|||
|
4. **灵活性和扩展性**
|
|||
|
|
|||
|
+ 提供了灵活的方式来扩展 Spring AOP 的功能,允许开发者自定义适配器以满足特定的需求,从而增强 AOP 的灵活性和可扩展性。
|
|||
|
|
|||
|
5. **支持多种通知类型的组合**
|
|||
|
|
|||
|
+ 允许将多种类型的通知与目标方法结合起来,实现更复杂的 AOP 操作,如前置通知和后置通知的组合等。
|
|||
|
|
|||
|
### 五、接口源码
|
|||
|
|
|||
|
这个接口定义了一种机制,允许向 Spring AOP 框架中引入新的 Advisor 和 Advice 类型。实现该接口的对象可以将自定义的 Advice 类型转换为 AOP Alliance 拦截器,使得这些自定义的 Advice 类型能够在 Spring AOP 框架中被使用。通常情况下,大多数 Spring 用户不需要直接实现这个接口;只有在需要引入新的 Advisor 或 Advice 类型时才需要这样做。
|
|||
|
|
|||
|
```java
|
|||
|
/**
|
|||
|
* 允许扩展 Spring AOP 框架的接口,以处理新的 Advisor 和 Advice 类型。
|
|||
|
*
|
|||
|
* <p>实现该接口的对象可以从自定义的 Advice 类型创建 AOP Alliance 拦截器,
|
|||
|
* 从而使得这些 Advice 类型可以在 Spring AOP 框架中使用,该框架在底层使用拦截。
|
|||
|
*
|
|||
|
* <p>大多数 Spring 用户无需实现此接口;只有在需要向 Spring 引入更多的 Advisor 或 Advice 类型时才需要这样做。
|
|||
|
*
|
|||
|
* @author Rod Johnson
|
|||
|
*/
|
|||
|
public interface AdvisorAdapter {
|
|||
|
|
|||
|
/**
|
|||
|
* 此适配器是否了解该通知对象?是否可以使用 Advisor 包含此通知作为参数调用 getInterceptors 方法?
|
|||
|
* @param advice 一个 Advice,如 BeforeAdvice
|
|||
|
* @return 此适配器是否了解给定的 Advice 对象
|
|||
|
* @see #getInterceptor(org.springframework.aop.Advisor)
|
|||
|
* @see org.springframework.aop.BeforeAdvice
|
|||
|
*/
|
|||
|
boolean supportsAdvice(Advice advice);
|
|||
|
|
|||
|
/**
|
|||
|
* 返回一个 AOP Alliance MethodInterceptor,将给定 Advice 的行为暴露给基于拦截的 AOP 框架。
|
|||
|
* <p>不必担心 Advisor 中包含的 Pointcut;AOP 框架将负责检查切点。
|
|||
|
* @param advisor Advisor。supportsAdvice() 方法必须在此对象上返回 true
|
|||
|
* @return 此 Advisor 的 AOP Alliance 拦截器。无需为效率缓存实例,因为 AOP 框架会缓存 Advice 链。
|
|||
|
*/
|
|||
|
MethodInterceptor getInterceptor(Advisor advisor);
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 六、主要实现
|
|||
|
|
|||
|
1. **MethodBeforeAdviceAdapter**
|
|||
|
|
|||
|
+ 用于将 `MethodBeforeAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`MethodBeforeAdvice` 是一个在目标方法执行前执行的通知接口。
|
|||
|
|
|||
|
2. **ThrowsAdviceAdapter**
|
|||
|
|
|||
|
+ 用于将 `ThrowsAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`ThrowsAdvice` 通知用于捕获目标方法抛出的异常。
|
|||
|
|
|||
|
3. **AfterReturningAdviceAdapter**
|
|||
|
|
|||
|
+ 用于将 `AfterReturningAdvice` 类型的通知适配到 Spring AOP 拦截器链中。`AfterReturningAdvice` 通知在目标方法正常返回后执行。
|
|||
|
|
|||
|
### 七、最佳实践
|
|||
|
|
|||
|
用自定义的 AdvisorAdapter 和 Advice 来实现对目标方法的增强。在示例中,首先注册了一个自定义的 AdvisorAdapter(NullReturningAdviceAdapter),然后创建了一个代理工厂(ProxyFactory)并向其添加了一个自定义的通知(MyNullReturningAdvice)。最后,通过代理工厂获取了代理对象,并调用了两个方法,其中一个方法会触发通知,另一个方法不会触发通知。
|
|||
|
|
|||
|
```java
|
|||
|
public class AdvisorAdapterDemo {
|
|||
|
|
|||
|
public static void main(String[] args) {
|
|||
|
// 注册自定义适配器
|
|||
|
GlobalAdvisorAdapterRegistry.getInstance().registerAdvisorAdapter(new NullReturningAdviceAdapter());
|
|||
|
// 创建代理工厂
|
|||
|
ProxyFactory proxyFactory = new ProxyFactory(new MyService());
|
|||
|
// 添加Advisor
|
|||
|
proxyFactory.addAdvice(new MyNullReturningAdvice());
|
|||
|
// 获取代理对象
|
|||
|
MyService proxy = (MyService) proxyFactory.getProxy();
|
|||
|
// 不会触发通知
|
|||
|
System.out.println("foo return value : " + proxy.foo());
|
|||
|
// 换行
|
|||
|
System.out.println();
|
|||
|
// 会触发通知
|
|||
|
System.out.println("bar return value : " + proxy.bar());
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
一个空返回通知的适配器,用于将空返回通知(NullReturningAdvice)适配到拦截器链中。它实现了 AdvisorAdapter 接口,包含了支持给定通知和获取方法拦截器的功能,以便将特定类型的通知行为暴露给基于拦截的 AOP 框架。
|
|||
|
|
|||
|
```java
|
|||
|
/**
|
|||
|
* 空返回通知适配器,用于将空返回通知(NullReturningAdvice)适配到拦截器链中。
|
|||
|
*/
|
|||
|
public class NullReturningAdviceAdapter implements AdvisorAdapter {
|
|||
|
|
|||
|
/**
|
|||
|
* 判断该适配器是否支持给定的通知。
|
|||
|
* @param advice 一个通知,如空返回通知(NullReturningAdvice)
|
|||
|
* @return 如果该适配器支持给定的通知,则返回 true;否则返回 false
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public boolean supportsAdvice(Advice advice) {
|
|||
|
return (advice instanceof NullReturningAdvice);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取一个方法拦截器,将给定的通知行为暴露给基于拦截的 AOP 框架。
|
|||
|
* @param advisor Advisor。supportsAdvice() 方法必须在此对象上返回 true
|
|||
|
* @return 给定 Advisor 的方法拦截器
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public MethodInterceptor getInterceptor(Advisor advisor) {
|
|||
|
NullReturningAdvice advice = (NullReturningAdvice) advisor.getAdvice();
|
|||
|
return new NullReturningAdviceInterceptor(advice);
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
一个空返回通知拦截器,用于在方法执行后检查返回值是否为空,并根据情况执行空返回通知的逻辑。它实现了 MethodInterceptor 和 AfterAdvice 接口,通过拦截方法调用后的返回值来判断是否需要执行空返回通知,并在必要时调用空返回通知的逻辑。
|
|||
|
|
|||
|
```java
|
|||
|
/**
|
|||
|
* 空返回通知拦截器,用于在方法执行后检查返回值是否为空,并根据情况执行空返回通知的逻辑。
|
|||
|
*/
|
|||
|
public class NullReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice {
|
|||
|
|
|||
|
/** 空返回通知 */
|
|||
|
private final NullReturningAdvice advice;
|
|||
|
|
|||
|
/**
|
|||
|
* 构造一个空返回通知拦截器。
|
|||
|
* @param advice 空返回通知
|
|||
|
*/
|
|||
|
public NullReturningAdviceInterceptor(NullReturningAdvice advice) {
|
|||
|
Assert.notNull(advice, "Advice must not be null");
|
|||
|
this.advice = advice;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 在方法执行后拦截,检查返回值是否为空,并根据情况执行空返回通知的逻辑。
|
|||
|
* @param mi 方法调用信息
|
|||
|
* @return 方法执行结果,如果返回值为空,则根据空返回通知执行后的返回值
|
|||
|
* @throws Throwable 如果方法调用过程中发生异常,则抛出异常
|
|||
|
*/
|
|||
|
@Override
|
|||
|
public Object invoke(MethodInvocation mi) throws Throwable {
|
|||
|
// 执行方法调用,获取返回值
|
|||
|
Object retVal = mi.proceed();
|
|||
|
// 如果返回值为空,则根据空返回通知执行后的返回值
|
|||
|
if (retVal == null) {
|
|||
|
retVal = this.advice.nullReturning(mi.getMethod(), mi.getArguments(), mi.getThis());
|
|||
|
}
|
|||
|
return retVal;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
一个空返回通知的定义,继承了 AfterAdvice 接口。它包含了一个方法 nullReturning,用于在目标方法返回值为空时执行相应的逻辑,并返回一个新的返回值。
|
|||
|
|
|||
|
```java
|
|||
|
/**
|
|||
|
* 空返回通知接口,继承自 AfterAdvice。
|
|||
|
*/
|
|||
|
public interface NullReturningAdvice extends AfterAdvice {
|
|||
|
|
|||
|
/**
|
|||
|
* 当目标方法返回值为空时调用的方法。
|
|||
|
* @param method 目标方法
|
|||
|
* @param args 方法参数
|
|||
|
* @param target 目标对象
|
|||
|
* @return 空返回通知执行后的返回值
|
|||
|
* @throws Throwable 如果在执行空返回通知的过程中发生异常,则抛出异常
|
|||
|
*/
|
|||
|
Object nullReturning(Method method, Object[] args, @Nullable Object target) throws Throwable;
|
|||
|
}
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
实现了`NullReturningAdvice`空返回通知接口,用于在目标方法返回值为空时执行特定逻辑。在 nullReturning 方法中,它打印出被调用的方法名称,并返回一个默认的字符串值。
|
|||
|
|
|||
|
```java
|
|||
|
public class MyNullReturningAdvice implements NullReturningAdvice {
|
|||
|
|
|||
|
@Override
|
|||
|
public Object nullReturning(Method method, Object[] args, Object target) throws Throwable {
|
|||
|
System.out.println("Null Returning method " + method.getName() + " is called.");
|
|||
|
return "hello default value";
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
简单的服务类,包含了两个方法 foo 和 bar。foo 方法执行后返回字符串 "hello foo",而 bar 方法执行后返回 null。
|
|||
|
|
|||
|
```java
|
|||
|
public class MyService {
|
|||
|
|
|||
|
public String foo() {
|
|||
|
System.out.println("Executing foo method");
|
|||
|
return "hello foo";
|
|||
|
}
|
|||
|
|
|||
|
public String bar() {
|
|||
|
System.out.println("Executing bar method");
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
运行结果,调用了 foo 方法,它返回 "hello foo";然后调用了 bar 方法,由于其返回值为 null,因此触发了空返回通知,打印了相应的消息,并返回了默认值 "hello default value"。
|
|||
|
|
|||
|
```java
|
|||
|
Executing foo method
|
|||
|
foo return value : hello foo
|
|||
|
|
|||
|
Executing bar method
|
|||
|
Null Returning method bar is called.
|
|||
|
bar return value : hello default value
|
|||
|
```
|
|||
|
|
|||
|
### 八、与其他组件的关系
|
|||
|
|
|||
|
1. **Advisor**
|
|||
|
|
|||
|
+ `Advisor` 接口定义了一个通知(Advice)和切点(Pointcut)的组合,用于描述在何时和何地应用通知。`AdvisorAdapter` 接口通过适配器模式,将不同类型的通知适配到拦截器链中,从而使其能够与 `Advisor` 相关联。
|
|||
|
|
|||
|
2. **Advice**
|
|||
|
|
|||
|
+ `Advice` 接口是 Spring AOP 中通知的标准接口,它定义了各种类型的通知,如前置通知、后置通知、环绕通知等。`AdvisorAdapter` 接口通过适配器模式,将不同类型的通知适配到拦截器链中,以便在目标方法的执行前后应用通知逻辑。
|
|||
|
|
|||
|
3. **MethodInterceptor**
|
|||
|
|
|||
|
+ `MethodInterceptor` 接口是 AOP Alliance 中定义的一个标准接口,用于拦截目标方法的执行并执行额外的逻辑。`AdvisorAdapter` 接口的 `getInterceptor` 方法返回一个 `MethodInterceptor` 实例,将通知的行为暴露给基于拦截的 AOP 框架。
|
|||
|
|
|||
|
4. **ProxyFactory**
|
|||
|
|
|||
|
+ `ProxyFactory` 是 Spring AOP 中用于创建代理对象的工厂类,它可以添加通知和切点来创建代理对象。在创建代理对象时,`ProxyFactory` 会使用 `AdvisorAdapter` 接口将通知适配到拦截器链中,并将其应用于目标方法的执行。
|
|||
|
|
|||
|
### 九、常见问题
|
|||
|
|
|||
|
1. **如何实现自定义的 AdvisorAdapter?**
|
|||
|
- 我们想要实现自定义的 `AdvisorAdapter` 接口以支持特定类型的通知。在这种情况下,他们需要了解如何适配不同类型的通知到拦截器链中,并确保适配器的实现正确地处理目标方法的执行。
|
|||
|
2. **如何注册自定义的 AdvisorAdapter?**
|
|||
|
- 一旦实现了自定义的 `AdvisorAdapter` 接口,我们需要知道如何将其注册到 Spring AOP 框架中,以便在应用中使用。这涉及到配置文件、注解或编程方式的注册。
|
|||
|
3. **适配器的执行顺序是如何确定的?**
|
|||
|
- 当一个目标方法被调用时,多个适配器会被调用以适配不同类型的通知。我们会关心适配器的执行顺序以确保通知被正确地应用。在 Spring AOP 中,通常使用 AdvisorAdapterRegistry 来管理适配器,并且它通常遵循注册的顺序来决定适配器的执行顺序。
|
|||
|
4. **如何处理支持多种类型通知的适配器?**
|
|||
|
- 有些适配器支持多种类型的通知。在这种情况下,我们需要了解如何确保适配器可以正确地识别和处理不同类型的通知,并将其适配到拦截器链中。
|
|||
|
5. **如何处理不支持的通知类型?**
|
|||
|
- 在某些情况下,会出现不支持的通知类型。我们需要了解如何处理这种情况,例如抛出异常或忽略不支持的通知类型。
|