首页 > 技术文章 > Spring注解 - AOP 面向切面编程

songjilong 2020-03-19 14:09 原文

基本概念:

AOP: Aspect Oriented Programming,即面向切面编程

指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式

  • 前置通知(@Before):在目标方法运行之前执行
  • 后置通知(@After):在目标方法运行结束之后执行(无论方法执行成功,还是出现异常,都执行)
  • 返回通知(@AfterReturning):在目标方法正常返回之后执行
  • 异常通知(@AfterThrowing):在目标方法出现异常后执行
  • 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())

返回通知和异常通知只能同时执行一个

基本使用:

  1. 导入aop依赖:Spring AOP(spring-aspects)

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    
  2. 定义一个业务逻辑类

    public class MathCalculator {
        public int div(int a, int b){
            return a / b;
        }
    }
    
  3. 定义一个日志切面类,告诉 Spring 这个类是切面类(在切面类加上:@Aspect),并且,给目标方法标注“何时何地”执行(通知注解)

    • 切入点基本格式:权限修饰符 方法返回值 全方法名(参数列表类型) 异常类型,加粗的是必填项

    • 在@Before注解上引用,如果引用类外的切入点表达式,需要使用全方法名

    @Aspect
    public class LogAspects {
    
        //抽取公共的切入点表达式
        @Pointcut("execution(* com.spring.aop.MathCalculator.*(..))")
        public void pointCut(){
        }
    
        @Before("pointCut()")
        public void logStart() {
            System.out.println("方法运行");
        }
    
        @After("pointCut()")
        public void logEnd() {
            System.out.println("方法结束");
        }
    
        @AfterReturning("pointCut()")
        public void logReturn() {
            System.out.println("方法正常返回");
        }
    
        @AfterThrowing("pointCut()")
        public void logException() {
            System.out.println("方法出现异常");
        }
    }
    
  4. 将切面类和业务逻辑类(目标方法所在类)都注册到ioc容器中,注册的几种方式可以参考前面的博客Spring注解 - 组件的注册

    @Bean
    public MathCalculator mathCalculator(){
        return new MathCalculator();
    }
    
    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
    
  5. 开启基于注解的AOP模式(在配置类加上:@EnableAspectJAutoProxy

    @Configuration
    @EnableAspectJAutoProxy
    public class SpringConfiguration
    
  6. 测试一把

    @Test
    public void test1(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration6.class);
        MathCalculator mathCalculator = (MathCalculator) applicationContext.getBean("mathCalculator");
        mathCalculator.div(2, 1);
    }
    

    运行结果:

    -----正常运行-----
    方法运行
    方法结束
    方法正常返回
    -------异常------
    方法运行
    方法结束
    方法出现异常
    

但上述输出的内容太简单了,如果想要得到方法具体信息怎么办?我们对切面类做一些改造

@Aspect
public class LogAspects {

    @Pointcut("execution(* com.spring.aop.MathCalculator.*(..))")
    public void pointCut() {
    }
   
    @Before(value = "pointCut()")
    public void logStart(JoinPoint joinPoint) {
        //参数列表
        Object[] args = joinPoint.getArgs();
     //方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法运行,参数列表:" + Arrays.toString(args));
    }
   
    @After("pointCut()")
 public void logEnd(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法结束");
    }
   
    @AfterReturning(value = "pointCut()", returning = "returnInfo")
 public void logReturn(JoinPoint joinPoint, Object returnInfo) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法正常返回,返回值:" + returnInfo);
    }
   
    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + "方法出现异常,异常信息:" + exception);
    }
}

运行结果:

-----正常运行-----
div方法运行,参数列表:[2, 1]
div方法结束
div方法正常返回,返回值:2
-------异常------
div方法运行,参数列表:[2, 0]
div方法结束
div方法出现异常,异常信息:java.lang.ArithmeticException: / by zero

注意:参数joinPoint一定要放在参数表的第一位

推荐阅读