From 0ada3c6eb5210e4f9fc3409b5e6aa4e4190b931b Mon Sep 17 00:00:00 2001 From: linlei Date: Tue, 7 May 2024 11:30:33 +0800 Subject: [PATCH] =?UTF-8?q?ExposeInvocationInterceptor=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20AopContext=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-aop/spring-aop-aopContext/README.md | 19 +------- .../README.md | 48 ++++++++++++------- .../ExposeInvocationInterceptorDemo.java | 2 +- .../main/java/com/xcs/spring/MyService.java | 4 +- 4 files changed, 35 insertions(+), 38 deletions(-) diff --git a/spring-aop/spring-aop-aopContext/README.md b/spring-aop/spring-aop-aopContext/README.md index 3459a32..19a2ef7 100644 --- a/spring-aop/spring-aop-aopContext/README.md +++ b/spring-aop/spring-aop-aopContext/README.md @@ -7,9 +7,6 @@ - [四、类源码](#四类源码) - [五、最佳实践](#五最佳实践) - [六、源码分析](#六源码分析) - - [JDK动态代理拦截器](#jdk动态代理拦截器) - - [CGLIB动态代理拦截器](#cglib动态代理拦截器) - - [七、常见问题](#七常见问题) ### 一、基本信息 @@ -187,7 +184,7 @@ bar... 在Spring AOP框架中,无论是在JDK动态代理还是CGLIB动态代理的拦截器中,都对`AopContext.setCurrentProxy(proxy)`进行了赋值操作。这个赋值操作的目的是将当前AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`获取代理对象。 -#### JDK动态代理拦截器 +**JDK动态代理拦截器** 在`org.springframework.aop.framework.JdkDynamicAopProxy#invoke`方法中,是JDK动态代理拦截器的一部分。主要处理了AOP代理的上下文。具体来说,在方法执行前,如果AOP代理对象已经暴露了(即`this.advised.exposeProxy`为`true`),则通过`AopContext.setCurrentProxy(proxy)`方法将当前的AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`来获取代理对象。在方法执行完成后,将之前设置的代理对象恢复,以保证AOP代理对象的上下文不会影响其他线程。 @@ -217,7 +214,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } ``` -#### CGLIB动态代理拦截器 +**CGLIB动态代理拦截器** 在`org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept`方法中,是CGLIB动态代理拦截器的一部分。在方法拦截过程中,它主要处理了AOP代理的上下文。具体来说,如果AOP代理对象已经暴露了(即`this.advised.exposeProxy`为`true`),则通过`AopContext.setCurrentProxy(proxy)`方法将当前的AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`来获取代理对象。在方法执行完成后,将之前设置的代理对象恢复,以保证AOP代理对象的上下文不会影响其他线程。 @@ -245,15 +242,3 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy } } ``` - -### 七、常见问题 - -1. **代理对象为空问题** - - + 如果在没有启用`exposeProxy`选项的情况下尝试使用`AopContext.currentProxy()`来获取代理对象,则可能会导致返回的代理对象为空,因为AOP代理对象并未暴露给方法内部。 -2. **多线程环境下的线程安全问题** - - + `AopContext`是基于线程的,如果在多线程环境下并发调用了`AopContext.setCurrentProxy(proxy)`和`AopContext.currentProxy()`,可能会出现线程安全问题,因此需要谨慎处理多线程情况。 -4. **可读性问题** - - + 在方法内部频繁地使用`AopContext.currentProxy()`来获取代理对象可能会降低代码的可读性,因为会使代码变得复杂,难以理解 \ No newline at end of file diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/README.md b/spring-aop/spring-aop-exposeInvocationInterceptor/README.md index 89f0210..08e8f8d 100644 --- a/spring-aop/spring-aop-exposeInvocationInterceptor/README.md +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/README.md @@ -6,8 +6,8 @@ - [三、主要功能](#三主要功能) - [四、类源码](#四类源码) - [五、最佳实践](#五最佳实践) - - [六、源码分析](#六源码分析) - - [七、常见问题](#七常见问题) + - [六、时序图](#六时序图) + - [七、源码分析](#七源码分析) ### 一、基本信息 @@ -127,7 +127,7 @@ public final class ExposeInvocationInterceptor implements MethodInterceptor, Pri ### 五、最佳实践 -创建了一个基于注解的应用程序上下文,从中获取了一个名为 `MyService` 的 bean,并调用了其 `doSomething()` 方法。 +创建了一个基于注解的应用程序上下文,从中获取了一个名为 `MyService` 的 bean,并调用了其 `foo()` 方法。 ```java public class ExposeInvocationInterceptorDemo { @@ -138,7 +138,7 @@ public class ExposeInvocationInterceptorDemo { // 从上下文中获取 MyService MyService myService = context.getBean(MyService.class); // 调用方法 - myService.doSomething(); + myService.foo(); } } ``` @@ -160,8 +160,8 @@ public class AppConfig { @Service public class MyService { - public void doSomething() { - System.out.println("Doing something..."); + public void foo() { + System.out.println("foo..."); } } ``` @@ -205,7 +205,25 @@ Proxy Class = class com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$f30643a6 Doing something... ``` -### 六、源码分析 +### 六、时序图 + +~~~mermaid +sequenceDiagram + AbstractAutowireCapableBeanFactory->>AbstractAutoProxyCreator: postProcessAfterInitialization() + Note over AbstractAutowireCapableBeanFactory,AbstractAutoProxyCreator: 调用后处理方法 + AbstractAutoProxyCreator->>AbstractAutoProxyCreator: wrapIfNecessary() + Note over AbstractAutoProxyCreator: 调用包装方法 + AbstractAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: getAdvicesAndAdvisorsForBean() + Note over AbstractAutoProxyCreator,AbstractAdvisorAutoProxyCreator: 获取通知和 Advisors + AbstractAdvisorAutoProxyCreator->>AbstractAdvisorAutoProxyCreator: findEligibleAdvisors() + Note over AbstractAdvisorAutoProxyCreator: 查找合适的 Advisors + AbstractAdvisorAutoProxyCreator->>AspectJAwareAdvisorAutoProxyCreator: extendAdvisors() + Note over AbstractAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator: Advisor 的扩展钩子 + AspectJAwareAdvisorAutoProxyCreator->>AspectJProxyUtils:makeAdvisorChainAspectJCapableIfNecessary() + Note over AspectJAwareAdvisorAutoProxyCreator,AspectJProxyUtils: 添加特殊的拦截器 +~~~ + +### 七、源码分析 在`org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中,在开头添加了一个 `ExposeInvocationInterceptor`。 @@ -220,7 +238,11 @@ protected void extendAdvisors(List candidateAdvisors) { } ``` -在`org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary`方法中,用于在代理链中添加特殊的拦截器,以确保与包含AspectJ建议的代理链一起正常工作。具体来说,它将 `ExposeInvocationInterceptor` 添加到建议列表的开头。这样做的目的是为了暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的),并使当前AspectJ JoinPoint可用。如果建议链中不存在AspectJ建议,则此调用不会产生任何效果。方法返回 `true` 表示成功向建议列表中添加了 `ExposeInvocationInterceptor`,否则返回 `false`。 +在`org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary` +方法中,用于在代理链中添加特殊的拦截器,以确保与包含AspectJ建议的代理链一起正常工作。具体来说,它将 `ExposeInvocationInterceptor` +添加到advisors列表的开头。这样做的目的是为了暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的),并使当前AspectJ +JoinPoint可用。如果advisors链中不存在AspectJ advisor,则此调用不会产生任何效果。方法返回 `true` +表示成功向建议列表中添加了 `ExposeInvocationInterceptor`,否则返回 `false`。 ```java /** @@ -266,13 +288,3 @@ private static boolean isAspectJAdvice(Advisor advisor) { ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut)); } ``` - -### 七、常见问题 - -1. **什么时候应该使用 `ExposeInvocationInterceptor`** - - - 当需要在 Spring AOP 中暴露方法调用的完整上下文时,通常使用 `ExposeInvocationInterceptor`。这在一些特定的切面编程场景下是必要的,比如需要在切点中访问方法参数、目标对象或其他方法调用信息时。 - -2. **为什么调用 `ExposeInvocationInterceptor.currentInvocation()` 抛出异常?** - - - 这可能是因为没有在拦截器链的开头正确配置 `ExposeInvocationInterceptor`,或者在不正确的线程上下文中调用了 `currentInvocation()` 方法。另外,如果没有正在进行的 AOP 调用,也会抛出异常。 \ No newline at end of file diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java index 40e1748..a033a76 100644 --- a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java @@ -10,6 +10,6 @@ public class ExposeInvocationInterceptorDemo { // 从上下文中获取 MyService MyService myService = context.getBean(MyService.class); // 调用方法 - myService.doSomething(); + myService.foo(); } } diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java index 0a7fba1..ea6a5c3 100644 --- a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Service; @Service public class MyService { - public void doSomething() { - System.out.println("Doing something..."); + public void foo() { + System.out.println("foo..."); } }