AOP细节
(aop所需的jar包http://www.cnblogs.com/TYDBLOG/p/7450059.html)
1 切入点表达式
1.1 作用
通过表达式的方式定位一个或多个具体的连接点。
1.2 语法细节
1.2.1切入点表达式的语法格式
execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表])) |
1.2.2举例说明
表达式 |
execution(* com.atguigu.spring.ArithmeticCalculator.*(..)) |
含义 |
ArithmeticCalculator接口中声明的所有方法。 第一个“*”代表任意修饰符及任意返回值。 第二个“*”代表任意方法。 “..”匹配任意数量、任意类型的参数。 若目标类、接口与该切面类在同一个包中可以省略包名。 |
表达式 |
execution(public * ArithmeticCalculator.*(..)) |
含义 |
ArithmeticCalculator接口的所有公有方法 |
表达式 |
execution(public double ArithmeticCalculator.*(..)) |
含义 |
ArithmeticCalculator接口中返回double类型数值的方法 |
表达式 |
execution(public double ArithmeticCalculator.*(double, ..)) |
含义 |
第一个参数为double类型的方法。 “..” 匹配任意数量、任意类型的参数。 |
表达式 |
execution(public double ArithmeticCalculator.*(double, double)) |
含义 |
参数类型为double,double类型的方法 |
1.2.3在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
表达式 |
execution (* *.add(int,..)) || execution(* *.sub(int,..)) |
含义 |
任意类中第一个参数为int类型的add方法或sub方法 |
2 通知
2.1 概述
- 在具体的连接点上要执行的操作。
- 一个切面可以包括一个或者多个通知。
- 通知所使用的注解的值往往是切入点表达式。
2.2 前置通知
- 前置通知:在方法执行之前执行的通知
- 使用@Before注解
2.3 后置通知
- 后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候
- 使用@After注解
2.4 返回通知
- 返回通知:无论连接点是正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知。
- 使用@AfterReturning注解
- 在返回通知中访问连接点的返回值
- 在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
- 必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值
- 原始的切点表达式需要出现在pointcut属性中
2.5 异常通知
- 异常通知:只在连接点抛出异常时才执行异常通知
- 将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。
- 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行
2.6 环绕通知
- 环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
- 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
- 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
- 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
2.7 代码示例
写一个类实现加减乘除,并生成 前置通知、后置通知、返回通知、异常通知 或环绕通知
2.7.1 配置文件:
创建文件时勾选aop bean context三个选项
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <!-- 配置扫描的包 --> <context:component-scan base-package="com.tyd.spring.aop"></context:component-scan> <!-- 使用aop要创建此标签 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
2.7.2 创建math接口以及方法实现类:
package com.tyd.spring.aop; public interface Math { public void add(int a,int b); public void sub(int a,int b); public void mul(int a,int b); public void div(int a,int b); public void add(int a, int b,int c); }
package com.tyd.spring.aop; import org.springframework.stereotype.Component; @Component public class MyEpressionMath implements Math{ @Override public void add(int a, int b) { int num=a+b; System.out.println(a+"+"+b+"="+num); } @Override public void sub(int a, int b) { System.out.println(a+"-"+b+"="+(a-b)); } @Override public void mul(int a, int b) { System.out.println(a+"*"+b+"="+(a*b)); } @Override public void div(int a, int b) { System.out.println(a+"/"+b+"="+(a/b)); } @Override public void add(int a, int b, int c) { System.out.println(a+"+"+b+c+"="+(a+b+c)); } }
2.7.3使用前置通知、后置通知、返回通知、异常通知实现日志
package com.tyd.spring.aop; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect //表明是切面类 public class CaculatorAspect { //value值为方法的全类名以及方法名:public void(访问权限及返回值类型)com.tyd.spring.aop.MyEpressionMath(全类名).*代表此类中所有方法(..)代表所有参数类型 @Before(value="execution(public void com.tyd.spring.aop.MyEpressionMath.*(..))") public void showBegin(){ System.out.println("【日志】【方法开始】"); } @AfterReturning(value="execution(public void com.tyd.spring.aop.MyEpressionMath.*(..))",returning="result") public void showAfter(){ System.out.println("【日志】【方法正常结束】"); } @AfterThrowing(value="execution(public void com.tyd.spring.aop.MyEpressionMath.*(..))") public void showexception(){ System.out.println("【日志】【方法出异常】"); } @After(value="execution(public void com.tyd.spring.aop.MyEpressionMath.*(..))") public void showAfterRe(){ System.out.println("【日志】【方法执行结束】"); } }
2.7.4环绕通知实现:
package com.tyd.spring.aop; import java.util.Arrays; import java.util.List; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect //表明是切面类 public class CaculatorAspect { @Around(value="execution(public void com.tyd.spring.aop.MyEpressionMath.*(..))") public Object around(ProceedingJoinPoint joinPoint){ //获取目标方法参数 Object[] args = joinPoint.getArgs(); System.out.println("args:"+args); //获取签名 Signature signature = joinPoint.getSignature(); System.out.println("signature:"+signature); //获取方法名 String methodName = signature.getName(); System.out.println("methodName:"+methodName); List<Object> list = Arrays.asList(args); Object result = null; try { try{ //目标方法之前要执行的操作 System.out.println("[环绕日志]【前置通知】"+methodName+"开始了,参数为:"+list); //调用目标方法 result = joinPoint.proceed(args); //目标方法正常执行之后的操作 }finally{ //方法最终结束时执行的操作! System.out.println("[环绕日志]【后置通知】"+methodName+"结束了!"); } //目标方法正常执行之后的操作 System.out.println("[环绕日志]【返回通知】"+methodName+"返回了,返回值为:"+result); } catch (Throwable e) { //目标方法抛出异常信息之后的操作 System.out.println("[环绕日志]【异常通知】"+methodName+"出异常了,异常对象为:"+e); throw new RuntimeException(e.getMessage()); }finally{ } return result; }
2.7.5测试代码
package com.tyd.spring.aop; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class text { @Test public void test() { ApplicationContext ioc=new ClassPathXmlApplicationContext("bean.xml"); Math m=(Math) ioc.getBean("myEpressionMath"); m.add(10, 10); System.out.println(); m.div(10, 2); System.out.println(); m.mul(20, 10); m.add(10, 10,10); //测试异常 m.div(10, 0); } }