Spring AOP--前置通知和后置通知 、返回通知,异常通知和环绕通知

24 篇文章 0 订阅

Spring AOP
  AspectJ:Java社区里最完整最流行的AOP框架
  在Spring2.0以上的版本中,可以使用基于AspectJ注解或基于XML配置的AOP

在Spring中启用AspectJ注解支持
  要在Spring应用中使用AspectJ注解,必须在classpath下包含AspectJ类库:aopalliance.jar、aspectj.weaver.jar和spring-aspects.jar
  将aop Schema添加到根元素中。
  要在Spring IOC容器中启用AspectJ注解支持,只要早bean配置文件中定义一个空的XML元素
  当Spring IOC容器侦测到bean配置文件中的元素时,会自动为与AspectJ切面匹配的bean创建代理

用AspectJ注解声明切面
  要在Spring中声明AspectJ切面,只需要在IOC容器中将切面声明为bean实例。当在Spring IOC容器中初始化AspectJ切面之后,Spring IOC容器就会为那些与AspectJ切面相匹配的bean创建代理
  在AspectJ注解中,切面只是一个带有@AspectJ注解的Java类
  通知是标注有某种注解的简单的Java方法
  AspectJ支持5种类型的通知注解:
    @Before:前置通知,在方法执行之前返回
    @After:后置通知,在方法执行后执行
    @AfterRunning:返回通知,在方法返回结果之后执行
    @AfterThrowing:异常通知,在方法抛出异常之后
    @Around:环绕通知,围绕着方法执行

利用方法签名编写AspectJ切入点表达式
  最典型的切入点表达式时根据方法的签名来匹配各种方法:
    -execution * com.yl.spring.aop.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类型的方法

后置通知
  后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候,下面的后置通知记录了方法的终止。
  一个切面可以包括一个或者多个通知

LoggingAspect.java

package com.yl.spring.aop.impl;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//这个类声明为一个切面:需要把该类放入到IOC容器中;再声明为一个切面
@Aspect
@Component
public class LoggingAspect {

    //声明该方法是一个前置通知:在目标方法开始之前执行
    //@Before("execution(public int com.yl.spring.aop.impl.ArithmeticCalculatorImpl.add(int, int))")
    @Before("execution(* com.yl.spring.aop.impl.*.*(..))")
    public void beforeMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("The method " + methodName + " begins with " + args);
    }
    //后置通知:在目标方法执行后(无论是否发生异常),执行的通知
    //在后置通知中,还不能访问目标方法执行的结果
    @After("execution(* com.yl.spring.aop.impl.*.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("The method " + methodName + " end with " + args);
    }

}

配置文件applicationContext.xml:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        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.1.xsd">

    <!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.yl.spring.aop.impl"></context:component-scan>

    <!-- 使AspectJ注解起作用:自动为匹配的类生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

测试类:

package com.yl.spring.aop.impl;

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

public class Main {
    public static void main(String[] args) {

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

        ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class);

        int result = arithmeticCalculator.add(4, 6);
        System.out.println("result: " + result);

        result = arithmeticCalculator.mul(4, 6);
        System.out.println("result: " + result);
    } 
}

下面继续了解返回通知、异常通知和环绕通知。具体的含义详见代码注释

package com.yl.spring.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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 LoggingAspect {

    /**
     * 在com.yl.spring.aop.ArithmeticCalculator接口的每一个实现类的每一个方法开始之前执行一段代码
     */
    @Before("execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }

    /**
     * 在com.yl.spring.aop.ArithmeticCalculator接口的每一个实现类的每一个方法执行之后执行一段代码
     * 无论该方法是否出现异常
     */
    @After("execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("The method " + methodName + " ends with " + Arrays.asList(args));
    }

    /**
     * 方法正常结束后执行的代码
     * 返回通知是可以访问到方法的返回值的
     */
    @AfterReturning(value="execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))", returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " return with " + result);
    }

    /**
     * 在方法出现异常时会执行的代码
     * 可以访问到异常对象,可以指定在出现特定异常时在执行通知代码
     */
    @AfterThrowing(value="execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))", throwing="ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs exception: " + ex);
    }

    /**
     * 环绕通知需要携带ProceedingJoinPoint类型的参数
     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
     * 而且环绕通知必须有返回值,返回值即为目标方法的返回值
     */
    @Around("execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        Object result = null;
        String methodName = pjd.getSignature().getName();
        //执行目标方法
        try {
            //前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            result = pjd.proceed();
            //返回通知
            System.out.println("The method " + methodName + " ends with " + Arrays.asList(pjd.getArgs()));
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method " + methodName + " occurs expection : " + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }

}

切面的优先级  

  为项目增加一个新的切面类,负责验证功能,则需要指定切面执行的顺序。即切面的优先级。具体方法是给切面类增加@Order注解,并指定具体的数字,值越小优先级越高

package com.yl.spring.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 可以使用@Order注解指定切面的优先级,值越小优先级越高
 * @author yul
 *
 */
@Order(2)
@Component
@Aspect
public class ValidationAspect {

    @Before("execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))")
    public void vlidateArgs(JoinPoint joinPoint) {
        System.out.println("validate: " + Arrays.asList(joinPoint.getArgs()));
    }
}

切点表达式的重用:

  在LoggingAspect类中,切点的表达式可以先定义,在使用。

package com.yl.spring.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Component
@Aspect
public class LoggingAspect {

    /**
     * 定义一个方法,用于声明切入点表达式。一般的,该方法中再不需要添加其他的代码
     * 使用@Pointcut 来声明切入点表达式
     * 后面的其他通知直接使用方法名直接引用方法名即可
     */
    @Pointcut("execution(public int com.yl.spring.aop.ArithmeticCalculator.*(..))")
    public void declareJoinPointExpression() {

    }

    /**
     * 在com.yl.spring.aop.ArithmeticCalculator接口的每一个实现类的每一个方法开始之前执行一段代码
     */
    @Before("declareJoinPointExpression()")
    public void beforeMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
    }

    /**
     * 在com.yl.spring.aop.ArithmeticCalculator接口的每一个实现类的每一个方法执行之后执行一段代码
     * 无论该方法是否出现异常
     */
    @After("declareJoinPointExpression()")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("The method " + methodName + " ends with " + Arrays.asList(args));
    }

    /**
     * 方法正常结束后执行的代码
     * 返回通知是可以访问到方法的返回值的
     */
    @AfterReturning(value="declareJoinPointExpression()", returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " return with " + result);
    }

    /**
     * 在方法出现异常时会执行的代码
     * 可以访问到异常对象,可以指定在出现特定异常时在执行通知代码
     */
    @AfterThrowing(value="declareJoinPointExpression()", throwing="ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("The method " + methodName + " occurs exception: " + ex);
    }

    /**
     * 环绕通知需要携带ProceedingJoinPoint类型的参数
     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
     * 而且环绕通知必须有返回值,返回值即为目标方法的返回值
     */
    @Around("declareJoinPointExpression()")
    public Object aroundMethod(ProceedingJoinPoint pjd) {
        Object result = null;
        String methodName = pjd.getSignature().getName();
        //执行目标方法
        try {
            //前置通知
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
            result = pjd.proceed();
            //返回通知
            System.out.println("The method " + methodName + " ends with " + Arrays.asList(pjd.getArgs()));
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method " + methodName + " occurs expection : " + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method " + methodName + " ends");
        return result;
    }

}

当处于不同的类,甚至不同的包时,可以使用包名.类名.方法名

具体代码如下:

package com.yl.spring.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 可以使用@Order注解指定切面的优先级,值越小优先级越高
 * @author yul
 *
 */
@Order(2)
@Component
@Aspect
public class ValidationAspect {

    @Before("com.yl.spring.aop.LoggingAspect.declareJoinPointExpression()")
    public void vlidateArgs(JoinPoint joinPoint) {
        System.out.println("validate: " + Arrays.asList(joinPoint.getArgs()));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值