Aop概念及其示例

一,入门程序

1.引入依赖


    org.springframework.boot
    spring-boot-starter-aop

2.代码示例

package com.yy.tilas.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect
public class TimeAspect {
  // 切入点表达式
  @Around("execution(* com.yy.tilas.service.*.*(..))")
  public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
    // 记录方法执行开始时间
    long begin = System.currentTimeMillis();
    // 执行原始方法
    Object result = joinPoint.proceed();
    // 记录方法执行结束时间
    long end = System.currentTimeMillis();
    // 计算方法执行耗时
    log.info(joinPoint.getSignature() + "执行耗时: {}毫秒", end - begin);
    // joinPoint.getSignature()获取签名,即执行方法
    return result;
  }
}

二,核心概念

1.通知类型

Spring中AOP的通知类型:

- @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
- @Before:前置通知,此注解标注的通知方法在目标方法前被执行
- @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
- @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
- @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

2.抽取公用的切入点表达式

    //切入点方法(公共的切入点表达式)
    @Pointcut("execution(* com.yy.tilas.service.*.*(..))")
    public void pt(){

    }
    //前置通知(引用切入点)
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("before ...");

    }


//当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候,具体的语法为:全类名.方法名()
@Aspect
public class MyAspect2 {
    //引用MyAspect1切面类中的切入点表达式
    @Before("com.yy.tilas.aspect.MyAspect1.pt()")
    public void before(){
        log.info("MyAspect2 -> before ...");
    }
}

3.通知顺序

在不同切面类中,默认按照切面类的类名字母排序:
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
通过@order()数字来改变执行顺序,前置通知数字越小越先执行,后置通知越小越后执行:
1. 不同的切面类当中,默认情况下通知的执行顺序是与切面类的类名字母排序是有关系的
2. 可以在切面类上面加上@Order注解,来控制不同的切面类通知的执行顺序

4.切入点表达式示例 

切入点表达式的语法规则:

  1. 方法的访问修饰符可以省略

  2. 返回值可以使用*号代替(任意返回值类型)

  3. 包名可以使用*号代替,代表任意包(一层包使用一个*

  4. 使用..配置包名,标识此包以及此包下的所有子包

  5. 类名可以使用*号代替,标识任意类

  6. 方法名可以使用*号代替,表示任意方法

  7. 可以使用 * 配置参数,一个任意类型的参数

  8. 可以使用.. 配置参数,任意个任意类型的参数

**切入点表达式示例**
- 省略方法的修饰符号 
  execution(void com.yy.service.impl.DeptServiceImpl.delete(java.lang.Integer))
- 使用`*`代替返回值类型
  execution(* com.yy.service.impl.DeptServiceImpl.delete(java.lang.Integer))
- 使用`*`代替包名(一层包使用一个`*`)
  execution(* com.yy.*.*.DeptServiceImpl.delete(java.lang.Integer))
- 使用`..`省略包名
  execution(* com..DeptServiceImpl.delete(java.lang.Integer))    
- 使用`*`代替类名
  execution(* com..*.delete(java.lang.Integer))   
- 使用`*`代替方法名
  execution(* com..*.*(java.lang.Integer))   
- 使用 `*` 代替参数
  execution(* com.yy.service.impl.DeptServiceImpl.delete(*))
- 使用`..`省略参数
execution(* com..*.*(..))

注意事项:

  • 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式

execution(* com.yy.service.DeptService.list(..)) || execution(* com.yy.service.DeptService.delete(..))

5.连接点

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

  • 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型

  • 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型

     //获取目标类名
            String name = pjp.getTarget().getClass().getName();
            log.info("目标类名:{}",name);
    
            //目标方法名
            String methodName = pjp.getSignature().getName();
            log.info("目标方法名:{}",methodName);
    
            //获取方法执行时需要的参数
            Object[] args = pjp.getArgs();
            log.info("目标方法参数:{}", Arrays.toString(args));
    
            //执行原始方法
            Object returnValue = pjp.proceed();
    
            return returnValue;

你可能感兴趣的:(springboot,java,spring,boot,mybatis)