博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于注解@Aspect的AOP实现
阅读量:4292 次
发布时间:2019-05-27

本文共 8480 字,大约阅读时间需要 28 分钟。

 

Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,这个坑把我给坑惨了。
  •  

使用步骤如下:

1、引入相关jar包

这里写图片描述

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

  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">

  8.  
  9. <!-- 配置自动扫描的包 -->

  10. <context:component-scan base-package="com.qcc.beans.aop"></context:component-scan>

  11. <!-- 自动为切面方法中匹配的方法所在的类生成代理对象。 -->

  12. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

  13. </beans>

注意: <aop:aspectj-autoproxy />有一个proxy-target-class属性,

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

当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。

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

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

  1. package com.qcc.beans.aop;

  2.  
  3. public interface ArithmeticCalculator {

  4.  
  5. int add(int i, int j);

  6.  
  7. int sub(int i, int j);

  8.  
  9. int mul(int i, int j);

  10.  
  11. int div(int i, int j);

  12. }

  •  
 
  1. package com.qcc.beans.aop;

  2.  
  3. import org.springframework.stereotype.Component;

  4.  
  5. //将实现类加入Spring的IOC容器进行管理

  6. @Component("arithmeticCalculator")

  7. public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

  8.  
  9. @Override

  10. public int add(int i, int j) {

  11. int result = i + j;

  12. return result;

  13. }

  14.  
  15. @Override

  16. public int sub(int i, int j) {

  17. int result = i - j;

  18. return result;

  19. }

  20.  
  21. @Override

  22. public int mul(int i, int j) {

  23. int result = i * j;

  24. return result;

  25. }

  26.  
  27. @Override

  28. public int div(int i, int j) {

  29. int result = i / j;

  30. return result;

  31. }

  32.  
  33. }

  •  

4、现在想在实现类中的每个方法执行前、后、以及是否发生异常等信息打印出来,需要把日志信息抽取出来,写到对应的切面的类中 LoggingAspect.java 中 。

要想把一个类变成切面类,需要两步, 
① 在类上使用 @Component 注解 把切面类加入到IOC容器中 
② 在类上使用 @Aspect 注解 使之成为切面类

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

  1. package com.qcc.beans.aop;

  2.  
  3. import java.util.Arrays;

  4.  
  5. import org.aspectj.lang.JoinPoint;

  6. import org.aspectj.lang.ProceedingJoinPoint;

  7. import org.aspectj.lang.annotation.After;

  8. import org.aspectj.lang.annotation.AfterReturning;

  9. import org.aspectj.lang.annotation.AfterThrowing;

  10. import org.aspectj.lang.annotation.Around;

  11. import org.aspectj.lang.annotation.Aspect;

  12. import org.aspectj.lang.annotation.Before;

  13. import org.springframework.stereotype.Component;

  14.  
  15. /**

  16. * 日志切面

  17. *

  18. * @author XiaoYe

  19. * @date 2017年1月1日 下午4:14:34

  20. */

  21. @Component

  22. @Aspect

  23. public class LoggingAspect {

  24.  
  25. /**

  26. * 前置通知:目标方法执行之前执行以下方法体的内容

  27. * @param jp

  28. */

  29. @Before("execution(* com.qcc.beans.aop.*.*(..))")

  30. public void beforeMethod(JoinPoint jp){

  31. String methodName = jp.getSignature().getName();

  32. System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));

  33. }

  34.  
  35. /**

  36. * 返回通知:目标方法正常执行完毕时执行以下代码

  37. * @param jp

  38. * @param result

  39. */

  40. @AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")

  41. public void afterReturningMethod(JoinPoint jp, Object result){

  42. String methodName = jp.getSignature().getName();

  43. System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");

  44. }

  45.  
  46. /**

  47. * 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。

  48. * @param jp

  49. */

  50. @After("execution(* com.qcc.beans.aop.*.*(..))")

  51. public void afterMethod(JoinPoint jp){

  52. System.out.println("【后置通知】this is a afterMethod advice...");

  53. }

  54.  
  55. /**

  56. * 异常通知:目标方法发生异常的时候执行以下代码

  57. */

  58. @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")

  59. public void afterThorwingMethod(JoinPoint jp, NullPointerException e){

  60. String methodName = jp.getSignature().getName();

  61. System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);

  62. }

  63.  
  64. // /**

  65. // * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码

  66. // * @return

  67. // */

  68. // @Around(value="execution(* com.qcc.beans.aop.*.*(..))")

  69. // public Object aroundMethod(ProceedingJoinPoint jp){

  70. // String methodName = jp.getSignature().getName();

  71. // Object result = null;

  72. // try {

  73. // System.out.println("【环绕通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));

  74. // //执行目标方法

  75. // result = jp.proceed();

  76. // System.out.println("【环绕通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);

  77. // } catch (Throwable e) {

  78. // System.out.println("【环绕通知中的--->异常通知】:the method 【" + methodName + "】 occurs exception " + e);

  79. // }

  80. //

  81. // System.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------");

  82. // return result;

  83. // }

  84.  
  85. }

  •  

5、编写Main方法进行测试

 
  1. package com.qcc.beans.aop;

  2.  
  3. import org.springframework.context.ApplicationContext;

  4. import org.springframework.context.support.ClassPathXmlApplicationContext;

  5.  
  6. public class Main {

  7.  
  8. public static void main(String[] args) {

  9. ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

  10. ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");

  11. System.out.println(arithmeticCalculator.getClass());

  12. int result = arithmeticCalculator.add(3, 5);

  13. System.out.println("result: " + result);

  14.  
  15. result = arithmeticCalculator.div(5, 0);

  16. System.out.println("result: " + result);

  17.  
  18. }

  19. }

运行结果:

  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)

  •  

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

你可能感兴趣的文章
jq使用教程07_ JQData HTTP 接口正式上线
查看>>
jq使用教程08_基于估值波动周期的择时策略
查看>>
海龟交易法则14_掌控心魔
查看>>
海龟交易法则15_万事俱备
查看>>
海龟交易法则16_附原版海龟交易法则
查看>>
克罗谈投资策略01_期货交易中的墨菲法则
查看>>
克罗谈投资策略02_赢家和输家
查看>>
克罗谈投资策略03_你所期望的赌博方式
查看>>
克罗谈投资策略04_感觉与现实
查看>>
克罗谈投资策略05_涨势买入,跌势卖出
查看>>
通向财务自由之路01_导读
查看>>
通向财务自由之路02_成功的决定因素:你
查看>>
通向财务自由之路03_判断之偏好:掌握市场为何对多数人来说如此之难
查看>>
通向财务自由之路04_设定你的目标
查看>>
通向财务自由之路05_选择一个有效的理念
查看>>
通向财务自由之路06_适应大环境的交易策略
查看>>
通向财务自由之路07_利用方案设法启动你的系统
查看>>
通向财务自由之路08_入市或市场时机选择
查看>>
通向财务自由之路09_知道何时收手:如何保护你的资本
查看>>
通向财务自由之路10_沉着应对
查看>>