diff --git a/README.md b/README.md index 561bbdd..0d912f0 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,10 @@ - [Pointcut](spring-aop/spring-aop-pointcut/README.md):定义切入点,匹配被拦截的方法。 - [Advice](spring-aop/spring-aop-advice/README.md):AOP核心接口,定义切面通知行为。 - [MethodInterceptor](spring-aop/spring-aop-advice-methodInterceptor/README.md):拦截方法执行,允许在前后添加额外逻辑。 + - [MethodBeforeAdvice](spring-aop/spring-aop-advice-methodBeforeAdvice/README.md):允许在方法调用之前插入自定义逻辑。 + - [AfterReturningAdvice](spring-aop/spring-aop-advice-methodBeforeAdvice/README.md):允许在方法调用之后插入自定义逻辑。 + - [ThrowsAdvice](spring-aop/spring-aop-advice-throwsAdvice/README.md):异常通知,捕获方法抛出的异常,执行额外逻辑。 + - [IntroductionInterceptor](spring-aop/spring-aop-advice-introductionInterceptor/README.md):动态地向目标对象引入新的功能或属性。 - [Advisor](spring-aop/spring-aop-advisor/README.md):用于将通知和切点结合,实现切面编程的横切关注点。 - [AdvisorAdapter](spring-aop/spring-aop-advisorAdapter/README.md):适配不同类型通知到拦截器链。 - [TargetSource](spring-aop/spring-aop-targetSource/README.md):管理AOP代理对象的获取与释放。 diff --git a/spring-aop/pom.xml b/spring-aop/pom.xml index f254c2d..aed24c9 100644 --- a/spring-aop/pom.xml +++ b/spring-aop/pom.xml @@ -31,6 +31,9 @@ spring-aop-annotation-enableLoadTimeWeaving spring-aop-advice-methodInterceptor spring-aop-advice-methodBeforeAdvice + spring-aop-advice-afterReturningAdvice + spring-aop-advice-throwsAdvice + spring-aop-advice-introductionInterceptor 4.0.0 diff --git a/spring-aop/spring-aop-advice-afterReturningAdvice/README.md b/spring-aop/spring-aop-advice-afterReturningAdvice/README.md new file mode 100644 index 0000000..10662d8 --- /dev/null +++ b/spring-aop/spring-aop-advice-afterReturningAdvice/README.md @@ -0,0 +1,140 @@ +## AfterReturningAdvice + +- [AfterReturningAdvice](#AfterReturningAdvice) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`AfterReturningAdvice`接口是Spring AOP框架中的一个核心接口,用于在目标方法执行后拦截并执行自定义逻辑。通过实现该接口的`afterReturning`方法,可以在目标方法成功返回结果后进行一些操作,如日志记录、性能监控等。 + +### 三、主要功能 + +1. **日志记录** + + + 记录目标方法的执行情况,如方法名称、参数值、返回结果等,以便跟踪应用程序的运行状态。 + +2. **性能监控** + + + 统计目标方法的执行时间,分析应用程序的性能瓶颈,优化程序性能。 + +3. **缓存处理** + + + 在方法返回结果后,将结果缓存起来,以提高后续相同请求的响应速度。 + +4. **统一处理** + + + 执行一些与业务逻辑无关的统一处理,如资源释放、清理等。 + +### 四、接口源码 + +`AfterReturningAdvice`接口主要用于在方法正常返回时执行后置通知。后置通知可以查看方法的返回值,但不能修改它,并且仅在方法正常返回时被调用,不会在抛出异常时被调用。该接口提供了一个`afterReturning`方法,在方法成功返回后进行回调,其中包含了返回值、被调用的方法、方法的参数以及方法调用的目标对象等信息。 + +```java +/** + * 后置返回通知仅在方法正常返回时被调用,如果抛出异常则不会被调用。这样的通知可以查看方法的返回值,但不能修改它。 + * + * 作者:Rod Johnson + * @see MethodBeforeAdvice + * @see ThrowsAdvice + */ +public interface AfterReturningAdvice extends AfterAdvice { + + /** + * 在给定方法成功返回后的回调。 + * @param returnValue 方法返回的值,如果有的话 + * @param method 被调用的方法 + * @param args 方法的参数 + * @param target 方法调用的目标对象。可能为{@code null}。 + * @throws Throwable 如果此对象希望中止调用。如果方法签名允许,将返回任何抛出的异常给调用者。否则,异常将被包装为运行时异常。 + */ + void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable; + +} +``` + +### 五、主要实现 + +1. **AspectJAfterReturningAdvice** + + 实现了返回后通知,使用 AspectJ 风格定义的通知,用于在目标方法成功执行并返回结果后执行额外的逻辑。 + +### 六、最佳实践 + +使用Spring AOP中的后置返回通知(AfterReturningAdvice)。首先,创建了一个代理工厂(ProxyFactory)并指定目标对象(MyService)。然后,创建了一个后置返回通知(MyAfterReturningAdvice)并添加到代理工厂中。接着,通过代理工厂获取代理对象,并调用代理对象的方法。 + +```java +public class AfterReturningAdviceDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 创建通知 + proxyFactory.addAdvice(new MyAfterReturningAdvice()); + // 获取代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomething(); + } +} +``` + +`MyAfterReturningAdvice`的类,它实现了Spring AOP框架中的`AfterReturningAdvice`接口。在`afterReturning`方法中,当目标方法成功返回结果时,它将打印出目标方法的名称以及返回的值。 + +```java +public class MyAfterReturningAdvice implements AfterReturningAdvice { + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + System.out.println("After method " + method.getName() + " is called, returned value: " + returnValue); + } +} +``` + +`MyService` 类是一个简单的服务类,其中包含了一个名为 `doSomething()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 + +```java +public class MyService { + + public String doSomething() { + System.out.println("Doing something..."); + return "hello world"; + } +} +``` + +运行结果,成功地执行了代理对象的`doSomething()`方法,并在方法执行完成后,后置返回通知`MyAfterReturningAdvice`被触发。 + +```java +Doing something... +After method doSomething is called, returned value: hello world +``` + +### 七、源码分析 + +暂无 + +### 八、常见问题 + +1. **无法拦截异常** + + 仅在目标方法成功返回时被调用,无法捕获和处理方法抛出的异常。 + +2. **无法修改返回值** + + + `afterReturning`方法可以看到方法的返回值,但无法修改它。如果需要修改返回值,可以考虑使用环绕通知(Around Advice)。 + +3. **无法访问方法体内的局部变量** + + + 只能获取到方法的返回值、方法名称、参数和目标对象,无法访问方法内的局部变量。 + +4. **无法控制方法执行流程** + + + 只能在方法成功返回后执行,无法控制方法执行的前后顺序,也无法阻止方法的执行。 \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-afterReturningAdvice/pom.xml b/spring-aop/spring-aop-advice-afterReturningAdvice/pom.xml new file mode 100644 index 0000000..cc9253b --- /dev/null +++ b/spring-aop/spring-aop-advice-afterReturningAdvice/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-advice-afterReturningAdvice + + \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/AfterReturningAdviceDemo.java b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/AfterReturningAdviceDemo.java new file mode 100644 index 0000000..7e968c1 --- /dev/null +++ b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/AfterReturningAdviceDemo.java @@ -0,0 +1,17 @@ +package com.xcs.spring; + +import org.springframework.aop.framework.ProxyFactory; + +public class AfterReturningAdviceDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 创建通知 + proxyFactory.addAdvice(new MyAfterReturningAdvice()); + // 获取代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomething(); + } +} diff --git a/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyAfterReturningAdvice.java b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyAfterReturningAdvice.java new file mode 100644 index 0000000..5d137ef --- /dev/null +++ b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyAfterReturningAdvice.java @@ -0,0 +1,12 @@ +package com.xcs.spring; + +import org.springframework.aop.AfterReturningAdvice; + +import java.lang.reflect.Method; + +public class MyAfterReturningAdvice implements AfterReturningAdvice { + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + System.out.println("After method " + method.getName() + " is called, returned value: " + returnValue); + } +} diff --git a/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyService.java new file mode 100644 index 0000000..66cef7c --- /dev/null +++ b/spring-aop/spring-aop-advice-afterReturningAdvice/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +public class MyService { + + public String doSomething() { + System.out.println("Doing something..."); + return "hello world"; + } +} diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/README.md b/spring-aop/spring-aop-advice-introductionInterceptor/README.md new file mode 100644 index 0000000..6bae4b4 --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/README.md @@ -0,0 +1,165 @@ +## IntroductionInterceptor + +- [IntroductionInterceptor](#IntroductionInterceptor) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`IntroductionInterceptor` 接口是 Spring AOP 中的一个关键接口,用于实现引介(Introduction)功能,允许向目标对象动态地添加新的方法或属性,而无需修改目标类的代码,从而实现横切关注点的功能,如日志记录、事务管理等。 + +### 三、主要功能 + +1. **引介新的接口或类** + + 通过实现 `introduce()` 方法,在目标对象的方法调用之前,向目标对象引介新的接口或类,从而使目标对象具有额外的功能或属性。 + +### 四、接口源码 + +`IntroductionInterceptor` 接口,它是 AOP 联盟 `MethodInterceptor` 的子接口,允许拦截器实现额外的接口,并通过使用该拦截器的代理对象来使用这些接口。`IntroductionInterceptor` 接口体现了 AOP 中的引介(Introduction)概念,通过引介,可以将额外的功能添加到目标对象中,类似于混合(mixins)的概念,使得可以构建复合对象,实现类似于 Java 中多继承的目标。 + +```java +/** + * AOP联盟 MethodInterceptor 的子接口,允许拦截器实现额外的接口,并通过使用该拦截器的代理对象来使用这些接口。这是一个基本的AOP概念,称为引介。 + * + *

引介通常是混合,允许构建能够实现多继承目标的复合对象。 + * + * @author Rod Johnson + * @see DynamicIntroductionAdvice + */ +public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { + +} +``` + +### 五、主要实现 + +1. **DelegatingIntroductionInterceptor** + + + `DelegatingIntroductionInterceptor` 是 Spring AOP 提供的通用引介拦截器,允许我们定义自定义的引介逻辑,并在需要时将其应用于目标对象。通过配置,可以动态地向目标对象添加新的方法或属性,而不必修改目标类的代码,提高了代码的可维护性和灵活性。 + +2. **DelegatePerTargetObjectIntroductionInterceptor** + + + `DelegatePerTargetObjectIntroductionInterceptor` 是 `DelegatingIntroductionInterceptor` 的子类,为每个目标对象创建一个独立的引介代理对象。这意味着每个目标对象都可以拥有自己独立的引介逻辑,而不会受到其他目标对象的影响。这种灵活性特别适用于需要为不同的目标对象动态添加不同功能或属性的场景,提供了更高级的定制能力。 + +### 六、最佳实践 + +使用 Spring AOP 中的引介功能。它创建了一个代理工厂,并通过设置强制使用 CGLIB 代理来创建代理对象。然后,它添加了一个通知器,将自定义的引介通知(`MyMonitoringIntroductionAdvice`)应用于目标对象(`MyService` 类),使得目标对象实现了 `MyMonitoringCapable` 接口。最后,它调用了代理对象的方法,并在必要时启用了监控功能,展示了如何在运行时动态地向目标对象引入新的功能。 + +```java +public class IntroductionInterceptorDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 强制私用CGLIB + proxyFactory.setProxyTargetClass(true); + // 创建通知 + proxyFactory.addAdvisor(new DefaultIntroductionAdvisor(new MyMonitoringIntroductionAdvice(), MyMonitoringCapable.class)); + // 创建代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomething(); + // 开始监控 + ((MyMonitoringCapable) proxy).toggleMonitoring(); + // 再次调用代理对象的方法 + proxy.doSomething(); + } +} +``` + +`MyMonitoringIntroductionAdvice` 类是一个实现了 `DelegatingIntroductionInterceptor` 接口和 `MyMonitoringCapable` 接口的自定义引介通知类。它具有一个 `active` 属性来表示监控是否处于激活状态,并提供了一个方法 `toggleMonitoring()` 来启用监控功能。在被监控的方法被调用时,如果监控处于激活状态,该类会输出相应的日志信息,包括方法执行时间等。通过继承 `doProceed()` 方法,它能够在方法执行前后添加自定义逻辑,实现了监控功能的动态引入。 + +```java +public class MyMonitoringIntroductionAdvice extends DelegatingIntroductionInterceptor implements MyMonitoringCapable { + + private boolean active = false; + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public void toggleMonitoring() { + setActive(true); + } + + // 当被监控的方法被调用时,如果监控处于激活状态,则输出日志 + @Override + protected Object doProceed(MethodInvocation mi) throws Throwable { + if (this.active) { + System.out.println("开启监控..."); + long startTime = System.currentTimeMillis(); + Object result = super.doProceed(mi); + long endTime = System.currentTimeMillis(); + System.out.println(mi.getClass().getName() + "." + mi.getMethod().getName() + " 耗费时间:" + (endTime - startTime) + " 毫秒"); + System.out.println("结束监控..."); + return result; + } + return super.doProceed(mi); + } +} +``` + +`MyMonitoringCapable` 接口定义了一个 `toggleMonitoring()` 方法,用于启用或禁用监控功能。 + +```java +public interface MyMonitoringCapable { + void toggleMonitoring(); +} +``` + +`MyService` 类是一个简单的服务类,其中包含了一个名为 `doSomething()` 的方法。在上下文中,`MyService` 类被用作目标对象,即需要被拦截和增强的对象。 + +```java +public class MyService { + + public String doSomething() { + System.out.println("Doing something..."); + return "hello world"; + } +} +``` + +### 七、源码分析 + +暂无 + +### 八、常见问题 + +1. **引介的作用和优势是什么?** + + - 这个问题探讨了引介在 AOP 中的作用和优势,以及它与其他 AOP 概念(如通知、切点)的区别。 + +2. **如何实现引介?** + + - 这个问题涉及到如何使用 `IntroductionInterceptor` 接口以及其子类来实现引介功能,以及在 Spring AOP 中如何配置和应用引介。 + +3. **引介和通知之间的区别是什么?** + + - 这个问题探讨了引介和其他类型的通知(如前置通知、后置通知)之间的区别,以及它们在 AOP 中的不同用途。 + +4. **引介在哪些场景下会被使用?** + + - 这个问题讨论了引介在实际开发中的应用场景,以及它如何帮助解决特定的横切关注点。 + +5. **引介是否有性能影响?** + + - 这个问题关注引介在运行时对性能的影响,以及如何优化引介以减少潜在的性能开销。 + +6. **如何处理引介与目标类之间的依赖关系?** + + - 这个问题涉及引介与目标类之间的依赖关系管理,以及如何确保引介逻辑能够正确地与目标类交互。 + +7. **引介在 Spring 中的实现原理是什么?** + + - 这个问题探讨了 Spring AOP 是如何利用代理机制实现引介功能的,以及它与其他 AOP 框架的实现方式的区别。 \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/pom.xml b/spring-aop/spring-aop-advice-introductionInterceptor/pom.xml new file mode 100644 index 0000000..6c6c0a8 --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-advice-introductionInterceptor + + \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/IntroductionInterceptorDemo.java b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/IntroductionInterceptorDemo.java new file mode 100644 index 0000000..5b01379 --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/IntroductionInterceptorDemo.java @@ -0,0 +1,24 @@ +package com.xcs.spring; + +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.DefaultIntroductionAdvisor; + +public class IntroductionInterceptorDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 强制私用CGLIB + proxyFactory.setProxyTargetClass(true); + // 创建通知 + proxyFactory.addAdvisor(new DefaultIntroductionAdvisor(new MyMonitoringIntroductionAdvice(), MyMonitoringCapable.class)); + // 创建代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomething(); + // 开始监控 + ((MyMonitoringCapable) proxy).toggleMonitoring(); + // 再次调用代理对象的方法 + proxy.doSomething(); + } +} diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringCapable.java b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringCapable.java new file mode 100644 index 0000000..7bc345c --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringCapable.java @@ -0,0 +1,5 @@ +package com.xcs.spring; + +public interface MyMonitoringCapable { + void toggleMonitoring(); +} diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringIntroductionAdvice.java b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringIntroductionAdvice.java new file mode 100644 index 0000000..bf56355 --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyMonitoringIntroductionAdvice.java @@ -0,0 +1,33 @@ +package com.xcs.spring; + +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.support.DelegatingIntroductionInterceptor; + +public class MyMonitoringIntroductionAdvice extends DelegatingIntroductionInterceptor implements MyMonitoringCapable { + + private boolean active = false; + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public void toggleMonitoring() { + setActive(true); + } + + // 当被监控的方法被调用时,如果监控处于激活状态,则输出日志 + @Override + protected Object doProceed(MethodInvocation mi) throws Throwable { + if (this.active) { + System.out.println("开启监控..."); + long startTime = System.currentTimeMillis(); + Object result = super.doProceed(mi); + long endTime = System.currentTimeMillis(); + System.out.println(mi.getClass().getName() + "." + mi.getMethod().getName() + " 耗费时间:" + (endTime - startTime) + " 毫秒"); + System.out.println("结束监控..."); + return result; + } + return super.doProceed(mi); + } +} diff --git a/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyService.java new file mode 100644 index 0000000..66cef7c --- /dev/null +++ b/spring-aop/spring-aop-advice-introductionInterceptor/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +public class MyService { + + public String doSomething() { + System.out.println("Doing something..."); + return "hello world"; + } +} diff --git a/spring-aop/spring-aop-advice-throwsAdvice/README.md b/spring-aop/spring-aop-advice-throwsAdvice/README.md new file mode 100644 index 0000000..13552fa --- /dev/null +++ b/spring-aop/spring-aop-advice-throwsAdvice/README.md @@ -0,0 +1,161 @@ +## ThrowsAdvice + +- [ThrowsAdvice](#ThrowsAdvice) + - [一、基本信息](#一基本信息) + - [二、基本描述](#二基本描述) + - [三、主要功能](#三主要功能) + - [四、接口源码](#四接口源码) + - [五、主要实现](#五主要实现) + - [六、最佳实践](#六最佳实践) + - [七、源码分析](#七源码分析) + - [八、常见问题](#八常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、基本描述 + +`ThrowsAdvice`接口是Spring AOP中的一种通知类型,用于在方法抛出异常时执行额外的逻辑。实现该接口的类可以捕获方法抛出的异常并执行自定义的异常处理逻辑,比如记录日志、发送通知等。 + +### 三、主要功能 + +1. **捕获异常** + + + 允许在目标方法抛出异常时捕获这些异常。 + +2. **执行额外逻辑** + + + 提供了`afterThrowing()`方法,允许实现类在方法抛出异常时执行额外的逻辑,比如记录日志、发送通知等。 + +3. **参数传递** + + + `afterThrowing()`方法提供了抛出异常的方法对象、参数数组、目标对象和异常对象作为参数,方便实现类在处理异常时获取相关信息。 + +4. **定制化处理** + + + 可以根据业务需求定制化异常处理逻辑,使应用程序更加灵活和健壮。 + +### 四、接口源码 + +`ThrowsAdvice`接口,用作异常通知的标签接口。它没有任何方法,方法是通过反射调用的。实现类必须实现`afterThrowing()`方法,以处理方法抛出的异常。该方法的参数形式为`void afterThrowing([Method, args, target], ThrowableSubclass)`,前三个参数可选,用于提供关于连接点的更多信息, + +```java +/** + * 用于异常通知的标签接口。 + * + *

该接口没有任何方法,因为方法是通过反射调用的。实现类必须实现以下形式的方法: + * + *

void afterThrowing([Method, args, target], ThrowableSubclass);
+ * + *

以下是一些有效的方法示例: + * + *

public void afterThrowing(Exception ex)
+ *
public void afterThrowing(RemoteException)
+ *
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
+ *
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
+ * + * 前三个参数是可选的,只有在我们需要有关连接点更多信息时才有用,如AspectJ中的after-throwing通知。 + * + *

注意: 如果throws-advice方法本身抛出异常,它将覆盖原始异常(即将异常更改为用户)。 + * 覆盖的异常通常是RuntimeException; 这与任何方法签名兼容。但是,如果throws-advice方法抛出一个已检查的异常, + * 它将必须匹配目标方法的声明异常,并且在某种程度上与特定目标方法签名耦合。 + * 不要抛出与目标方法签名不兼容的未声明的检查异常! + * + * @author Rod Johnson + * @author Juergen Hoeller + * @see AfterReturningAdvice + * @see MethodBeforeAdvice + */ +public interface ThrowsAdvice extends AfterAdvice { + +} +``` + +### 五、主要实现 + +暂无 + +### 六、最佳实践 + +使用`ThrowsAdvice`接口来处理方法抛出的异常。它创建了一个代理工厂,并将目标对象(`MyService`)和异常通知(`MyThrowsAdvice`)传递给代理工厂。然后,它通过代理工厂获取代理对象,并调用代理对象的方法`doSomethingException()`。 + +```java +public class ThrowsAdviceDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 创建通知 + proxyFactory.addAdvice(new MyThrowsAdvice()); + // 获取代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomethingException(); + } +} +``` + +`MyThrowsAdvice`类实现了`ThrowsAdvice`接口,并定义了`afterThrowing()`方法,用于处理方法抛出的异常。当目标方法抛出异常时,该方法将被调用,并打印出异常信息。 + +```java +public class MyThrowsAdvice implements ThrowsAdvice { + public void afterThrowing(Exception ex) throws Throwable { + System.out.println("Exception thrown: " + ex.getMessage()); + } +} +``` + +`MyService`类包含了一个名为`doSomethingException()`的方法,该方法执行某些操作,并故意引发了一个异常(通过除以零)。 + +```java +public class MyService { + + public void doSomethingException() { + System.out.println("Doing something exception..."); + int i = 1 / 0; + } +} +``` + +运行结果,当调用了`MyService`类的`doSomethingException()`方法,但在该方法中发生了除以零的错误,导致了`java.lang.ArithmeticException: / by zero`异常的抛出。 + +```java +Doing something exception... +Exception thrown: / by zero +Exception in thread "main" java.lang.ArithmeticException: / by zero + at com.xcs.spring.MyService.doSomethingException(MyService.java:7) + at com.xcs.spring.MyService$$FastClassBySpringCGLIB$$c768e93b.invoke() + at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) + at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779) + at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) + at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) + at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:113) + at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) + at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) + at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) + at com.xcs.spring.MyService$$EnhancerBySpringCGLIB$$abe9fbc2.doSomethingException() + at com.xcs.spring.ThrowsAdviceDemo.main(ThrowsAdviceDemo.java:15) +``` + +### 七、源码分析 + +暂无 + +### 八、常见问题 + +1. **异常处理的影响范围** + + + 异常通知对整个目标方法的异常都起作用,这可能不是所期望的行为。有时候可能只想针对特定类型的异常执行特定的处理逻辑。 + +2. **异常信息的捕获和处理** + + + 在异常通知中捕获到的异常信息可能不够详细,特别是对于大型应用程序中的复杂异常场景,可能需要更多的异常信息来进行适当的处理。 + +3. **异常类型的匹配** + + + 在异常通知中定义的异常类型需要与目标方法抛出的异常类型匹配,否则可能无法正确执行异常处理逻辑。 + +4. **对目标方法参数的访问** + + + 在异常通知中可以访问目标方法的参数,但需要小心处理参数可能为空的情况,以避免出现空指针异常。 \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-throwsAdvice/pom.xml b/spring-aop/spring-aop-advice-throwsAdvice/pom.xml new file mode 100644 index 0000000..1b3cc17 --- /dev/null +++ b/spring-aop/spring-aop-advice-throwsAdvice/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-aop + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-aop-advice-throwsAdvice + + \ No newline at end of file diff --git a/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyService.java new file mode 100644 index 0000000..fd73b0c --- /dev/null +++ b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +public class MyService { + + public void doSomethingException() { + System.out.println("Doing something exception..."); + int i = 1 / 0; + } +} diff --git a/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyThrowsAdvice.java b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyThrowsAdvice.java new file mode 100644 index 0000000..b26e565 --- /dev/null +++ b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/MyThrowsAdvice.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +import org.springframework.aop.ThrowsAdvice; + +public class MyThrowsAdvice implements ThrowsAdvice { + public void afterThrowing(Exception ex) throws Throwable { + System.out.println("Exception thrown: " + ex.getMessage()); + } +} diff --git a/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/ThrowsAdviceDemo.java b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/ThrowsAdviceDemo.java new file mode 100644 index 0000000..9d05b35 --- /dev/null +++ b/spring-aop/spring-aop-advice-throwsAdvice/src/main/java/com/xcs/spring/ThrowsAdviceDemo.java @@ -0,0 +1,17 @@ +package com.xcs.spring; + +import org.springframework.aop.framework.ProxyFactory; + +public class ThrowsAdviceDemo { + + public static void main(String[] args) { + // 创建代理工厂&创建目标对象 + ProxyFactory proxyFactory = new ProxyFactory(new MyService()); + // 创建通知 + proxyFactory.addAdvice(new MyThrowsAdvice()); + // 获取代理对象 + MyService proxy = (MyService) proxyFactory.getProxy(); + // 调用代理对象的方法 + proxy.doSomethingException(); + } +}