基于注解@Aspect的AOP实现

偏执的太偏执、 2022-03-12 17:56 430阅读 0赞
  1. Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar aopalliance-1.0.jar aspectjweaver.jar,这个坑把我给坑惨了。

使用步骤如下:

1、引入相关jar包

这里写图片描述

2、Spring的配置文件 applicationContext.xml 中引入context、aop对应的命名空间;配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生成代理对象。

  1. <?xml version="1.0" encoding="UTF-8"?>
  1. <beans xmlns=”http://www.springframework.org/schema/beans“
  2. xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance“
  3. xmlns:aop=”http://www.springframework.org/schema/aop“
  4. xmlns:context=”http://www.springframework.org/schema/context“
  5. xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
  7. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

注意: 有一个proxy-target-class属性,

默认为 false,表示使用jdk动态代理织入增强,

当配为时,表示使用CGLib动态代理技术织入增强。

不过即使proxy-target-class设置为 false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

3、创建简单计算器的接口ArithmeticCalculator.java及实现类ArithmeticCalculatorImpl.java

  1. package com.qcc.beans.aop;
  2. public interface ArithmeticCalculator {
  3. int add(int i, int j);
  4. int sub(int i, int j);
  5. int mul(int i, int j);
  6. int div(int i, int j);
  7. }

  1. package com.qcc.beans.aop;
  2. import org.springframework.stereotype.Component;
  3. //将实现类加入Spring的IOC容器进行管理
  4. @Component("arithmeticCalculator")
  5. public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
  6. @Override
  7. public int add(int i, int j) {
  8. int result = i + j;
  9. return result;
  10. }
  11. @Override
  12. public int sub(int i, int j) {
  13. int result = i - j;
  14. return result;
  15. }
  16. @Override
  17. public int mul(int i, int j) {
  18. int result = i * j;
  19. return result;
  20. }
  21. @Override
  22. public int div(int i, int j) {
  23. int result = i / j;
  24. return result;
  25. }
  26. }

4、现在想在实现类中的每个方法执行前、后、以及是否发生异常等信息打印出来,需要把日志信息抽取出来,写到对应的切面的类中 LoggingAspect.java 中 。
要想把一个类变成切面类,需要两步,
① 在类上使用 @Component 注解 把切面类加入到IOC容器中
② 在类上使用 @Aspect 注解 使之成为切面类

下面直接上完整代码,用@Aspect注解方式来实现前置通知、返回通知、后置通知、异常通知、环绕通知。

  1. package com.qcc.beans.aop;
  2. import java.util.Arrays;
  3. import org.aspectj.lang.JoinPoint;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.After;
  6. import org.aspectj.lang.annotation.AfterReturning;
  7. import org.aspectj.lang.annotation.AfterThrowing;
  8. import org.aspectj.lang.annotation.Around;
  9. import org.aspectj.lang.annotation.Aspect;
  10. import org.aspectj.lang.annotation.Before;
  11. import org.springframework.stereotype.Component;
  12. /**
  13. * 日志切面
  14. *
  15. * @author XiaoYe
  16. * @date 2017年1月1日 下午4:14:34
  17. */
  18. @Component
  19. @Aspect
  20. public class LoggingAspect {
  21. /**
  22. * 前置通知:目标方法执行之前执行以下方法体的内容
  23. * @param jp
  24. */
  25. @Before("execution(* com.qcc.beans.aop.*.*(..))")
  26. public void beforeMethod(JoinPoint jp){
  27. String methodName = jp.getSignature().getName();
  28. System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
  29. }
  30. /**
  31. * 返回通知:目标方法正常执行完毕时执行以下代码
  32. * @param jp
  33. * @param result
  34. */
  35. @AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
  36. public void afterReturningMethod(JoinPoint jp, Object result){
  37. String methodName = jp.getSignature().getName();
  38. System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
  39. }
  40. /**
  41. * 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
  42. * @param jp
  43. */
  44. @After("execution(* com.qcc.beans.aop.*.*(..))")
  45. public void afterMethod(JoinPoint jp){
  46. System.out.println("【后置通知】this is a afterMethod advice...");
  47. }
  48. /**
  49. * 异常通知:目标方法发生异常的时候执行以下代码
  50. */
  51. @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
  52. public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
  53. String methodName = jp.getSignature().getName();
  54. System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
  55. }
  56. // /**
  57. // * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
  58. // * @return
  59. // */
  60. // @Around(value="execution(* com.qcc.beans.aop.*.*(..))")
  61. // public Object aroundMethod(ProceedingJoinPoint jp){
  62. // String methodName = jp.getSignature().getName();
  63. // Object result = null;
  64. // try {
  65. // System.out.println("【环绕通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
  66. // //执行目标方法
  67. // result = jp.proceed();
  68. // System.out.println("【环绕通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);
  69. // } catch (Throwable e) {
  70. // System.out.println("【环绕通知中的--->异常通知】:the method 【" + methodName + "】 occurs exception " + e);
  71. // }
  72. //
  73. // System.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------");
  74. // return result;
  75. // }
  76. }

5、编写Main方法进行测试

  1. package com.qcc.beans.aop;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. public class Main {
  5. public static void main(String[] args) {
  6. ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  7. ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
  8. System.out.println(arithmeticCalculator.getClass());
  9. int result = arithmeticCalculator.add(3, 5);
  10. System.out.println("result: " + result);
  11. result = arithmeticCalculator.div(5, 0);
  12. System.out.println("result: " + result);
  13. }
  14. }

运行结果:

  1. class com.sun.proxy.$Proxy10
  2. 【前置通知】the method 【add】 begins with [3, 5]
  3. 【后置通知】this is a afterMethod advice...
  4. 【返回通知】the method 【add】 ends with 【8】
  5. result: 8
  6. 【前置通知】the method 【div】 begins with [5, 0]
  7. 【后置通知】this is a afterMethod advice...
  8. Exception in thread "main" java.lang.ArithmeticException: / by zero
  9. at com.qcc.beans.aop.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:28)
  10. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  11. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  12. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  13. at java.lang.reflect.Method.invoke(Method.java:606)
  14. at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
  15. at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
  16. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
  17. at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
  18. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  19. at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
  20. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  21. at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
  22. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  23. at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
  24. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  25. at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
  26. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
  27. at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
  28. at com.sun.proxy.$Proxy10.div(Unknown Source)
  29. at com.qcc.beans.aop.Main.main(Main.java:15)


把其它代码都注释掉,把环绕通知的方法释放出来,测试结果如下:

  1. 【环绕通知中的--->前置通知】:the method 【add】 begins with [3, 5]
  2. 【环绕通知中的--->返回通知】:the method 【add】 ends with 8
  3. 【环绕通知中的--->后置通知】:-----------------end.----------------------
  4. result: 8
  5. 【环绕通知中的--->前置通知】:the method 【div】 begins with [5, 0]
  6. 【环绕通知中的--->异常通知】:the method 【div】 occurs exception java.lang.ArithmeticException: / by zero
  7. 【环绕通知中的--->后置通知】:-----------------end.----------------------
  8. Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract int com.qcc.beans.aop.ArithmeticCalculator.div(int,int)
  9. at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219)
  10. at com.sun.proxy.$Proxy7.div(Unknown Source)
  11. at com.qcc.beans.aop.Main.main(Main.java:15)

从以上发现,返回通知和异常通知不会同时出现;不管是否发生异常,后置通知都会正常打印。

发表评论

表情:
评论列表 (有 0 条评论,430人围观)

还没有评论,来说两句吧...

相关阅读

    相关 基于注解实现AOP

    动态代理分为JDK动态代理和cglib动态代理,当目标类有接口的情况使用JDK动态代理和cglib动态代理,没有接口时只能使用cglib动态代理。 JDK动态代理动态生成的代