基于注解@Aspect的AOP实现

 

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

使用步骤如下:

1、引入相关jar包

这里写图片描述

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

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

  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)

  •  

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值