环绕通知

环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点。甚至可以控制是否执行连接点。类似于动态代理。
环绕通知 连接点的参数类型必须是 ProceedingJoinPoint ,它是 JoinPoint 的子接口,,允许控制何时执行, 是否执行连接点。
环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法,如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
环绕通知的方法需要有返回值,返回目标方法执行之后的结果, 即调用 joinPoint.proceed() 的返回值, 否则会出现空指针异常。
四大通知(前置,后置,异常,最终)在用注解的时候会有顺序问题,所以尽量不要用注解方式配置四大通知,注解配置AOP时候应该用环绕的通知.
示例:

  1. /**
  2. * 定义注解切点
  3. */
  4. //@Pointcut("@annotation(AroundDemo.annotation.InterceptClass)") 拦截注解标注的方法的(如果注解放在类上面就失效)
  5. @Pointcut("@within(AroundDemo.annotation.InterceptClass)")//拦截注解标注的类上的所有方法
  6. public void tokenIntercept() {
  7. }
  8. //@Around(value="execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))")
  9. @Around("tokenIntercept()")
  10. public Object aroundlogging(ProceedingJoinPoint pjd) {
  11. Object result = null;
  12. String methodName = pjd.getSignature().getName();
  13. List<Object> args = Arrays.asList(pjd.getArgs());
  14. try {
  15. // 前置通知
  16. System.out.println("前置通知" + methodName + " begin with" + args);
  17. result = pjd.proceed();
  18. // 返回通知
  19. System.out.println("返回通知 " + methodName + " end with " + result);
  20. } catch (Throwable e) {
  21. System.out.println("异常通知 " + methodName + " occurs excetion: " + e);
  22. // 异常通知
  23. e.printStackTrace();
  24. }
  25. //后置通知
  26. System.out.println("后置通知 " + methodName + " end");
  27. return result;
  28. }

@Around注解用法

传递@Pointcut

可以给多个环绕通知来使用

  1. /**
  2. * 拦截类的切点
  3. */
  4. @Pointcut("@within(aop.aroundDemo.annotation.InterceptClass)")
  5. public void InterceptClass() {
  6. }
  7. @Around(value = "InterceptClass()")
  8. public Object InterceptClassAround(ProceedingJoinPoint pjd) {
  9. @Around(value = "InterceptClass()")
  10. public Object InterceptClassAround(ProceedingJoinPoint pjd) {


两个环绕通知的执行顺序:


我是第二个环绕通知的前置通知
第一个环绕通知前置通知ceui begin with[]

TestController.ceui (目标方法)

第一个环绕通知返回通知 ceui end with null
第一个环绕通知后置通知 ceui end

我是第二个环绕通知的返回通知
我是第二个环绕通知的后置通知


传递表达式

  1. /**
  2. * 切入点表达式
  3. */
  4. @Around("execution(* aop.aroundDemo.controller.execution.*.*(..))")
  5. public Object InterceptMethodAround(ProceedingJoinPoint pjd) {

传递注解
@Around(value = “@annotation(aop.aroundDemo.annotation.InterceptMethod)”)

当方法上面或者类上面有这个注解就可以被拦截,详细情况需要参考自定义注解的Target属性




@Around参数可以写@PointCut定义的注解

@Around是环绕通知注解
可以传入表达式
@Around(“execution( com.company.controller..*(..))”)

ProceedingJoinPoint


ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法:

  1. public interface ProceedingJoinPoint extends JoinPoint {
  2. public Object proceed() throws Throwable; //执行目标方法
  3. public Object proceed(Object[] args) throws Throwable;//传入的心的参数去指定目标方法
  4. }

其它四大通知

前置通知

前置通知:在目标 方法开始之前进行执行的通知。
前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值。
value属性值:切入点表达式,匹配与之对应的目标。利用【】可以进行那匹配不同的目标,参数只需要传入类型即可。
方法体JoinPoint 参数:用来连接当前连接点的连接细节,该参数可以获取目标对象的信息,如类名称,方法参数,方法名称等。【org.aspectj.lang.JoinPoint】包

表达式写法
@Before( value=**”execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.
(int , int))”**)

  1. /**
  2. * 前置通知
  3. * @param joinPoint
  4. */
  5. @Before("@within(aop.TheBigFourNotice.annotation.InterceptClass)")
  6. public void before(JoinPoint joinPoint) { //注意 JoinPoint 的包
  7. String methodName = joinPoint.getSignature().getName();
  8. List<Object> args = Arrays.asList(joinPoint.getArgs());
  9. System.out.println("The 前置通知 方法" + methodName + " begin with" + args);
  10. }


返回通知@AfterReturning

返回通知: 在目标方法正常结束时,才执行的通知 ,如果方法发生异常则没有返回通知

返回通知使用@AfterReturning注解,并将切入点表达式的值作为注解值。

返回通知可以获取到目标函数最终的返回值returnVal,当目标函数没有返回值时,returnVal将返回null,必须通过returning = “returnVal”注明参数的名称而且必须与通知函数的参数名称相同。请注意,在任何通知中这些参数都是可选的,需要使用时直接填写即可,不需要使用时,可以完成不用声明出来。如下

returning属性值:声明该方法可以存在返回值,该属性的值即为用来传入返回值的参数名称。
方法体Object参数 :需要使用与returning同名参数名称,用来接收方法的返回值。

表达式写法
@AfterReturning(value=“execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))”,returning=“result”)

  1. /**
  2. * 返回通知
  3. * @param joinPoint
  4. * @param result
  5. */
  6. @AfterReturning(value = "@within(aop.TheBigFourNotice.annotation.InterceptClass))", returning = "result")
  7. public void afterReturning(JoinPoint joinPoint, Object result) {
  8. String methodName = joinPoint.getSignature().getName();
  9. System.out.println("The 返回通知方法" + methodName + " end with " + result);
  10. }

异常通知@AfterThrowing

异常通知 :在目标方法出现异常时才会进行执行的代码,如果方法没有异常,则不执行异常通知
异常通知使用@AfterThrowing注解,并将切入点表达式的值作为注解值。
throwing属性:访问连接点抛出的异常。
方法体Exception参数:用来接收连接点抛出的异常。Exception类匹配所有的异常,可以指定为特定的异常 例如NullPointerException类等

表达式写法
@AfterThrowing(value=“execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))”,throwing=“ex”)

  1. /**
  2. * 异常通知
  3. * @param joinPoint
  4. * @param ex
  5. */
  6. @AfterThrowing(value = "@within(aop.TheBigFourNotice.annotation.InterceptClass))", throwing = "ex")
  7. public void afterThrowing(JoinPoint joinPoint, Exception ex) {
  8. String methodName = joinPoint.getSignature().getName();
  9. System.out.println("The 异常通知方法" + methodName + " occurs excetion: " + ex);
  10. }



后置通知

后置通知: 在目标方法执行之后,无论是否发生异常,都进行执行的通知。 该通知有点类似于finally代码块,只要应用了无论什么情况下都会执行。


后置通知使用@After注解, 并将切入点表达式的值作为注解值。

在后置通知中,不能访问目标方法的执行结果。原因可能在执行过程中发生异常而无法得到结果。

@After(“execution(public int com.wf.springaopImpl.ArithmeticCalculatorImpl.*(int , int)))”)

  1. /**
  2. * 后置通知
  3. * @param joinPoint
  4. */
  5. @After("@within(aop.TheBigFourNotice.annotation.InterceptClass)")
  6. public void after(JoinPoint joinPoint) {
  7. String methodName = joinPoint.getSignature().getName();
  8. System.out.println("The 后置通知方法 " + methodName + " end");
  9. }

五大通知执行时机

要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理,AOP的原理其实就是利用了动态代理,将动态代理进行了封装。

在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类,通知则是标注有某种注解的简单的 Java 方法。

AspectJ 支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行 (如果方法发生异常则没有返回通知)
@AfterThrowing: 异常通知, 在方法抛出异常之后 (如果方法没有异常,则不抛出异常通知)
@Around: 环绕通知, 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。