From 431ca04c080e9c011498e1e3ebc326b6c68f48da Mon Sep 17 00:00:00 2001 From: linlei Date: Tue, 23 Apr 2024 15:05:18 +0800 Subject: [PATCH] =?UTF-8?q?ExposeInvocationInterceptor=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + spring-aop/pom.xml | 1 + spring-aop/spring-aop-aopContext/README.md | 90 +++++- .../README.md | 278 ++++++++++++++++++ .../pom.xml | 14 + .../main/java/com/xcs/spring/AppConfig.java | 12 + .../ExposeInvocationInterceptorDemo.java | 15 + .../src/main/java/com/xcs/spring/LogUtil.java | 15 + .../com/xcs/spring/MyMethodInterceptor.java | 15 + .../main/java/com/xcs/spring/MyService.java | 11 + 10 files changed, 440 insertions(+), 12 deletions(-) create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/README.md create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/pom.xml create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/AppConfig.java create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/LogUtil.java create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java create mode 100644 spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java diff --git a/README.md b/README.md index d25bdca..edd985d 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,7 @@ - [AdvisorChainFactory](spring-aop/spring-aop-advisorChainFactory/README.md):创建Advisor链的工厂接口。 - [AdvisorAdapterRegistry](spring-aop/spring-aop-advisorAdapterRegistry/README.md):适配各种Advice到AOP拦截器,注册和管理Advisor适配器。 - [AopContext](spring-aop/spring-aop-aopContext/README.md):获取Spring AOP代理对象的工具。 + - [ExposeInvocationInterceptor](spring-aop/spring-aop-exposeInvocationInterceptor/README.md):暴露Spring AOP方法调用上下文的拦截器。 + Spring AOT diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index cf95a52..25433d8 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -38,6 +38,7 @@ spring-aop-advised spring-aop-aopContext spring-aop-targetSourceCreator + spring-aop-exposeInvocationInterceptor 4.0.0 diff --git a/spring-aop/spring-aop-aopContext/README.md b/spring-aop/spring-aop-aopContext/README.md index eb44ae9..3459a32 100644 --- a/spring-aop/spring-aop-aopContext/README.md +++ b/spring-aop/spring-aop-aopContext/README.md @@ -4,11 +4,12 @@ - [一、基本信息](#一基本信息) - [二、基本描述](#二基本描述) - [三、主要功能](#三主要功能) - - [四、最佳实践](#四最佳实践) - - [五、源码分析](#五源码分析) + - [四、类源码](#四类源码) + - [五、最佳实践](#五最佳实践) + - [六、源码分析](#六源码分析) - [JDK动态代理拦截器](#jdk动态代理拦截器) - [CGLIB动态代理拦截器](#cglib动态代理拦截器) - - [六、常见问题](#六常见问题) + - [七、常见问题](#七常见问题) ### 一、基本信息 @@ -32,7 +33,78 @@ + 在一些特定场景下,可能需要在不同的方法间传递AOP代理对象,而不是直接调用`this`。`AopContext`类提供了一种解决方案,可以在方法调用间传递AOP代理对象。 -### 四、最佳实践 +### 四、类源码 + +`AopContext`类提供了用于获取当前AOP调用信息的静态方法集合。通过`currentProxy()`方法可以获取当前AOP代理对象,前提是AOP框架已配置为暴露代理对象。这对目标对象或通知进行增强调用,以及查找通知配置非常有用。然而,由于性能成本较高,Spring的AOP框架默认不会暴露代理对象。 + +```java +/** + * 用于获取当前AOP调用信息的静态方法集合。 + * + *

如果AOP框架配置为暴露当前代理对象(非默认情况),则可使用 {@code currentProxy()} 方法获取正在使用的AOP代理对象。 + * 目标对象或通知可以使用此方法进行增强调用,类似于EJB中的 {@code getEJBObject()}。也可用于查找通知配置。 + * + *

Spring的AOP框架默认不暴露代理对象,因为这样做会带来性能开销。 + * + *

此类中的功能可被目标对象使用,以获取调用中的资源。然而,当存在合理替代方案时,不应使用此方法,因为这会使应用程序代码依赖于AOP下的使用和Spring AOP框架。 + * + * @author Rod Johnson + * @author Juergen Hoeller + * @since 13.03.2003 + */ +public final class AopContext { + + /** + * 线程本地变量,用于保存与该线程关联的AOP代理对象。 + * 除非控制代理配置的“exposeProxy”属性被设置为“true”,否则将包含{@code null}。 + * @see ProxyConfig#setExposeProxy + */ + private static final ThreadLocal currentProxy = new NamedThreadLocal<>("Current AOP proxy"); + + + private AopContext() { + } + + + /** + * 尝试返回当前AOP代理对象。此方法仅在调用方法通过AOP调用,并且AOP框架已设置为暴露代理对象时可用。 + * 否则,此方法将抛出IllegalStateException异常。 + * @return 当前AOP代理对象(永远不会返回{@code null}) + * @throws IllegalStateException 如果无法找到代理对象,因为该方法是在AOP调用上下文之外调用的,或者因为AOP框架尚未配置为暴露代理对象 + */ + public static Object currentProxy() throws IllegalStateException { + Object proxy = currentProxy.get(); + if (proxy == null) { + throw new IllegalStateException( + "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " + + "ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context."); + } + return proxy; + } + + /** + * 使给定的代理对象可通过{@code currentProxy()}方法访问。 + *

注意,调用者应谨慎地保留旧值。 + * @param proxy 要暴露的代理对象(或{@code null}以重置) + * @return 旧的代理对象,如果没有绑定,则可能为{@code null} + * @see #currentProxy() + */ + @Nullable + static Object setCurrentProxy(@Nullable Object proxy) { + Object old = currentProxy.get(); + if (proxy != null) { + currentProxy.set(proxy); + } + else { + currentProxy.remove(); + } + return old; + } + +} +``` + +### 五、最佳实践 使用Spring AOP创建一个代理对象,并在代理对象的方法调用前应用自定义的前置通知。首先,通过`ProxyFactory`创建了一个代理工厂,并设置了要被代理的目标对象`MyService`。然后通过`proxyFactory.setExposeProxy(true)`来暴露代理对象,以便在方法内部可以使用`AopContext`类访问到代理对象。接着,使用`proxyFactory.addAdvisor()`方法添加了一个切面通知器,将自定义的前置通知`MyMethodBeforeAdvice`应用到被`MyAnnotation`注解标记的方法上。最后,通过`proxyFactory.getProxy()`获取代理对象,并调用其方法`foo()`。 @@ -111,7 +183,7 @@ Before method bar is called. bar... ``` -### 五、源码分析 +### 六、源码分析 在Spring AOP框架中,无论是在JDK动态代理还是CGLIB动态代理的拦截器中,都对`AopContext.setCurrentProxy(proxy)`进行了赋值操作。这个赋值操作的目的是将当前AOP代理对象设置为当前线程的上下文中,以便在方法内部可以通过`AopContext.currentProxy()`获取代理对象。 @@ -174,20 +246,14 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy } ``` -### 六、常见问题 +### 七、常见问题 1. **代理对象为空问题** + 如果在没有启用`exposeProxy`选项的情况下尝试使用`AopContext.currentProxy()`来获取代理对象,则可能会导致返回的代理对象为空,因为AOP代理对象并未暴露给方法内部。 - 2. **多线程环境下的线程安全问题** + `AopContext`是基于线程的,如果在多线程环境下并发调用了`AopContext.setCurrentProxy(proxy)`和`AopContext.currentProxy()`,可能会出现线程安全问题,因此需要谨慎处理多线程情况。 - -3. **性能问题** - - + 在某些情况下,频繁地使用`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 new file mode 100644 index 0000000..89f0210 --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/README.md @@ -0,0 +1,278 @@ +## ExposeInvocationInterceptor + +- [ExposeInvocationInterceptor](#exposeinvocationinterceptor) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、类源码](#四类源码) + - [五、最佳实践](#五最佳实践) + - [六、源码分析](#六源码分析) + - [七、常见问题](#七常见问题) + + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`ExposeInvocationInterceptor`是Spring AOP中的一个拦截器类,主要功能是在AOP调用链中暴露当前方法调用的上下文信息,通过暴露`MethodInvocation`对象,使其他拦截器或切面能够访问并处理方法调用的相关信息。 + +### 三、主要功能 + +1. **暴露当前方法调用的上下文信息** + + + 通过暴露`MethodInvocation`对象,允许其他拦截器或切面访问当前方法调用的相关信息,如目标对象、方法、参数等。 + +2. **提供`currentInvocation()`方法** + + + 允许在拦截器或切面中调用`currentInvocation()`方法来获取当前方法调用的`MethodInvocation`对象,从而获取方法调用的上下文信息。 + +3. **支持AOP调用链的处理** + + + 作为Spring AOP的一个拦截器,`ExposeInvocationInterceptor`能够被添加到AOP代理链中,确保在调用链的初始阶段就将`MethodInvocation`对象暴露出来,以便后续的拦截器或切面可以使用。 + +### 四、类源码 + + `ExposeInvocationInterceptor`拦截器,其主要目的是将当前的方法调用上下文暴露为线程本地对象。它允许在Spring AOP中获取方法调用的详细信息,例如目标对象、方法、参数等。这个拦截器在AOP链中通常是第一个,用于确保其他拦截器或切面能够访问方法调用的完整上下文。 + +```java +/** + * 拦截器,将当前{@link org.aopalliance.intercept.MethodInvocation}暴露为线程本地对象。 + * 仅在必要时使用此拦截器;例如,当切点(例如,AspectJ表达式切点)需要知道完整的调用上下文时。 + * + *

除非绝对必要,否则不要使用此拦截器。目标对象通常不应知道Spring AOP, + * 因为这会创建对Spring API的依赖。目标对象应尽可能是纯POJO。 + * + *

如果使用,此拦截器通常将是拦截器链中的第一个。 + * + * @author Rod Johnson + * @author Juergen Hoeller + */ +@SuppressWarnings("serial") +public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { + + /** 此类的单例实例。 */ + public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor(); + + /** + * 此类的单例顾问。在使用Spring AOP时,请使用它,因为它可以避免创建新的Advisor来包装该实例。 + */ + public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) { + @Override + public String toString() { + return ExposeInvocationInterceptor.class.getName() +".ADVISOR"; + } + }; + + private static final ThreadLocal invocation = + new NamedThreadLocal<>("Current AOP method invocation"); + + + /** + * 返回与当前调用关联的AOP Alliance MethodInvocation对象。 + * @return 与当前调用关联的调用对象 + * @throws IllegalStateException 如果当前没有AOP调用, + * 或者ExposeInvocationInterceptor未添加到此拦截器链中 + */ + public static MethodInvocation currentInvocation() throws IllegalStateException { + MethodInvocation mi = invocation.get(); + if (mi == null) { + throw new IllegalStateException( + "No MethodInvocation found: Check that an AOP invocation is in progress and that the " + + "ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " + + "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " + + "In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " + + "must be invoked from the same thread."); + } + return mi; + } + + + /** + * 确保只能创建规范实例。 + */ + private ExposeInvocationInterceptor() { + } + + @Override + @Nullable + public Object invoke(MethodInvocation mi) throws Throwable { + MethodInvocation oldInvocation = invocation.get(); + invocation.set(mi); + try { + return mi.proceed(); + } + finally { + invocation.set(oldInvocation); + } + } + + @Override + public int getOrder() { + return PriorityOrdered.HIGHEST_PRECEDENCE + 1; + } + + /** + * Required to support serialization. Replaces with canonical instance + * on deserialization, protecting Singleton pattern. + *

Alternative to overriding the {@code equals} method. + */ + private Object readResolve() { + return INSTANCE; + } + +} +``` + +### 五、最佳实践 + +创建了一个基于注解的应用程序上下文,从中获取了一个名为 `MyService` 的 bean,并调用了其 `doSomething()` 方法。 + +```java +public class ExposeInvocationInterceptorDemo { + + public static void main(String[] args) { + // 创建一个基于注解的应用程序上下文 + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + // 从上下文中获取 MyService + MyService myService = context.getBean(MyService.class); + // 调用方法 + myService.doSomething(); + } +} +``` + +使用了 `@EnableAspectJAutoProxy` 注解启用了 AspectJ 自动代理功能,并且通过 `@ComponentScan` 注解扫描了包 `com.xcs.spring` 下的组件。 + +```java +@EnableAspectJAutoProxy +@Configuration +@ComponentScan("com.xcs.spring") +public class AppConfig { + +} +``` + +一个服务类,用于业务逻辑的实现。 + +```java +@Service +public class MyService { + + public void doSomething() { + System.out.println("Doing something..."); + } +} +``` + +`MyMethodInterceptor`标记为 `@Aspect` 和 `@Component`,表明它是一个切面,并且由 Spring 容器进行管理。其中包含一个名为 `before()` 的方法,使用 `@Before` 注解标记,表示在目标方法执行之前执行。方法内部调用了 `LogUtil.print()` 方法,用于记录日志或执行其他操作。这个切面主要是针对 `com.xcs.spring.MyService` 类中所有公共方法的执行,在方法执行之前添加了特定的逻辑。 + +```java +@Aspect +@Component +public class MyMethodInterceptor { + + @Before("execution(public * com.xcs.spring.MyService.*(..))") + public void before() { + LogUtil.print(); + } +} +``` + +通过 `ExposeInvocationInterceptor.currentInvocation()` 获取当前方法调用的 `ProxyMethodInvocation` 对象,然后打印了方法名称、参数长度、目标对象以及代理对象的类名。 + +```java +public class LogUtil { + + public static void print() { + ProxyMethodInvocation methodInvocation = (ProxyMethodInvocation) ExposeInvocationInterceptor.currentInvocation(); + System.out.println("Method = " + methodInvocation.getMethod()); + System.out.println("Arguments Length = " + methodInvocation.getArguments().length); + System.out.println("Target = " + methodInvocation.getThis()); + System.out.println("Proxy Class = " + methodInvocation.getProxy().getClass()); + } +} +``` + +运行结果,通过`ExposeInvocationInterceptor.currentInvocation()`获取方法调用上下文实现日志打印。 + +```java +Method = public void com.xcs.spring.MyService.doSomething() +Arguments Length = 0 +Target = com.xcs.spring.MyService@49964d75 +Proxy Class = class com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$f30643a6 +Doing something... +``` + +### 六、源码分析 + +在`org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors`方法中,在开头添加了一个 `ExposeInvocationInterceptor`。 + +```java +/** + * 将{@link ExposeInvocationInterceptor}添加到advice链的开头。 + *

在使用AspectJ切点表达式和AspectJ风格的advice时,需要此额外的Advisors。 + */ +@Override +protected void extendAdvisors(List candidateAdvisors) { + AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); +} +``` + +在`org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary`方法中,用于在代理链中添加特殊的拦截器,以确保与包含AspectJ建议的代理链一起正常工作。具体来说,它将 `ExposeInvocationInterceptor` 添加到建议列表的开头。这样做的目的是为了暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的),并使当前AspectJ JoinPoint可用。如果建议链中不存在AspectJ建议,则此调用不会产生任何效果。方法返回 `true` 表示成功向建议列表中添加了 `ExposeInvocationInterceptor`,否则返回 `false`。 + +```java +/** + * 如果需要,向包含AspectJ建议的代理链中添加特殊的建议: + * 具体来说,在列表的开头添加{@link ExposeInvocationInterceptor}。 + *

这将暴露当前Spring AOP调用(对于某些AspectJ切点匹配是必要的), + * 并使当前AspectJ JoinPoint可用。如果建议链中没有AspectJ建议,则调用不会产生任何效果。 + * @param advisors 可用的建议列表 + * @return 如果向列表中添加了{@link ExposeInvocationInterceptor},则返回{@code true},否则返回{@code false} + */ +public static boolean makeAdvisorChainAspectJCapableIfNecessary(List advisors) { + // 不要向空列表添加建议;这可能表示不需要代理 + if (!advisors.isEmpty()) { + boolean foundAspectJAdvice = false; + for (Advisor advisor : advisors) { + // 谨慎使用不带保护的Advice,因为这可能会急切地实例化非单例的AspectJ切面... + if (isAspectJAdvice(advisor)) { + foundAspectJAdvice = true; + break; + } + } + // 如果在建议链中找到AspectJ建议,并且没有ExposeInvocationInterceptor.ADVISOR,则添加 + if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { + advisors.add(0, ExposeInvocationInterceptor.ADVISOR); + return true; + } + } + return false; +} +``` + +在`org.springframework.aop.aspectj.AspectJProxyUtils#isAspectJAdvice`方法中,判断给定的 Advisor 是否包含 AspectJ Advice。它检查 Advisor 实例是否属于特定类型或者其 Advice 是否是 AbstractAspectJAdvice 的子类,或者其 Pointcut 是否是 AspectJExpressionPointcut 的实例。 + +```java +/** + * 判断给定的 Advisor 是否包含 AspectJ Advice。 + * @param advisor 要检查的 Advisor + */ +private static boolean isAspectJAdvice(Advisor advisor) { + return (advisor instanceof InstantiationModelAwarePointcutAdvisor || + advisor.getAdvice() instanceof AbstractAspectJAdvice || + (advisor instanceof PointcutAdvisor && + ((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/pom.xml b/spring-aop/spring-aop-exposeInvocationInterceptor/pom.xml new file mode 100644 index 0000000..c619d36 --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-exposeInvocationInterceptor + + \ No newline at end of file diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/AppConfig.java b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/AppConfig.java new file mode 100644 index 0000000..7c79966 --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/AppConfig.java @@ -0,0 +1,12 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@EnableAspectJAutoProxy +@Configuration +@ComponentScan("com.xcs.spring") +public class AppConfig { + +} 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 new file mode 100644 index 0000000..40e1748 --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/ExposeInvocationInterceptorDemo.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class ExposeInvocationInterceptorDemo { + + public static void main(String[] args) { + // 创建一个基于注解的应用程序上下文 + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + // 从上下文中获取 MyService + MyService myService = context.getBean(MyService.class); + // 调用方法 + myService.doSomething(); + } +} diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/LogUtil.java b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/LogUtil.java new file mode 100644 index 0000000..6a00fbf --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/LogUtil.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.springframework.aop.ProxyMethodInvocation; +import org.springframework.aop.interceptor.ExposeInvocationInterceptor; + +public class LogUtil { + + public static void print() { + ProxyMethodInvocation methodInvocation = (ProxyMethodInvocation) ExposeInvocationInterceptor.currentInvocation(); + System.out.println("Method = " + methodInvocation.getMethod()); + System.out.println("Arguments Length = " + methodInvocation.getArguments().length); + System.out.println("Target = " + methodInvocation.getThis()); + System.out.println("Proxy Class = " + methodInvocation.getProxy().getClass()); + } +} diff --git a/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java new file mode 100644 index 0000000..d171dda --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyMethodInterceptor.java @@ -0,0 +1,15 @@ +package com.xcs.spring; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class MyMethodInterceptor { + + @Before("execution(public * com.xcs.spring.MyService.*(..))") + public void before() { + LogUtil.print(); + } +} 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 new file mode 100644 index 0000000..0a7fba1 --- /dev/null +++ b/spring-aop/spring-aop-exposeInvocationInterceptor/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,11 @@ +package com.xcs.spring; + +import org.springframework.stereotype.Service; + +@Service +public class MyService { + + public void doSomething() { + System.out.println("Doing something..."); + } +}