Spring的AOP

@Before

说明

作用: 
  被此注解修饰的方法为前置通知。前置通知的执行时间点是在切入点方法执行之前。
属性:
  value:
    用于指定切入点表达式。可以是表达式,也可以是表达式的引用。
  argNames:
    用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:
  在实际开发中,我们需要对切入点方法执行之前进行增强, 此时就用到了前置通知。在通知(增强的方法)中需要获取切入点方法中的参数进行处理时,就要配合切入点表达式参数来使用。

示例:

/**
 * 前置通知
 */
@Before(value = "pt1(user)",argNames = "user")
public void beforeLog(User user){
    System.out.println("执行切入点方法前记录日志"+user);
}

@AfterReturning

说明:

作用: 
  用于配置后置通知。后置通知的执行是在切入点方法正常执行之后执行。
  需要注意的是,由于基于注解的配置时,spring创建通知方法的拦截器链时,后置通知在最终通知之后,所以会先执行@After注解修饰的方法。
属性:
  value:
    用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
  pointcut:
    它的作用和value是一样的。
  returning:
    指定切入点方法返回值的变量名称。它必须和切入点方法返回值名称一致。
  argNames:
    用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:
  此注解是用于配置后置增强切入点方法的。被此注解修饰方法会在切入点方法正常
执行之后执行。在我们实际开发中,像提交事务,记录访问日志,统计方法执行效率等等都可
以利用后置通知实现。
    /**
     * 后置通知
     * 它是在切入点方法正常执行并返回之前执行(此时已经执行完最终通知了)
     */
    @AfterReturning(pointcut = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..))", returning = "obj")
    public Object afterReturningPrintLog(Object obj) {
        System.out.println("后置通知增强了" + obj);
        return obj;
    }

@AfterThrowing

说明:

作用: 
  用于配置异常通知。
属性:
  value:
    用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
  pointcut:
    它的作用和value是一样的。
  throwing:
    指定切入点方法执行产生异常时的异常对象变量名称。它必须和异常变量名称一致。
  argNames:
    用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:
  用此注解修饰的方法执行时机是在切入点方法执行产生异常之后执行。

示例:

    /**
     * 例外通知(异常通知)
     * 它是在切入点方法执行产生异常之后执行(此时已经执行完最终通知了)
     */
    @AfterThrowing(pointcut = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..))", throwing = "e")
    public void afterThrowingPrintLog(Throwable e) {
        System.out.println("异常通知增强了" + e);
    }

@After

说明:

作用: 
  用于指定最终通知。
属性:
  value:
    用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
  argNames:
    用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:
  最终通知的执行时机,是在切入点方法执行完成之后执行,无论切入点方法执行是否产生异常最终通知都会执行。所以被此注解修饰的方法,通常都是做一些清理操作

示例:

    /**
     * 最终通知
     * 它是在切入点方法执行之后执行(注解驱动开发)
     */
    @After(value = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void afterPrintLog(User user, String id) {
        System.out.println("最终通知增强了" + user + "," + id);
    }

@Around

说明:

作用: 
  用于指定环绕通知。
属性:
  value:
    用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
  argNames:
    用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:
  环绕通知有别于前面介绍的四种通知类型。它不是指定增强方法执行时机的,而是spring为我们提供的一种可以通过编码的方式手动控制增强方法何时执行的机制。
 @Around("execution(* com.itheima.service.impl.*.*(..))")
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        //定义返回值对象
        Object rtValue = null;
        try {
            //创建系统日志对象
            SystemLog log = new SystemLog();
            //设置主键
            String id = UUID.randomUUID().toString().replace("-","").toUpperCase();
            log.setId(id);
            //设置来访者ip,由于我们是java工程,没有请求信息
            log.setRemoteIP("127.0.0.1");
            //设置执行时间
            log.setTime(new Date());
            //设置当前执行的方法名称
            //1.使用ProceedingJoinPoint接口中的获取签名方法
            Signature signature = pjp.getSignature();
            //2.判断当前签名是否方法签名
            if(signature instanceof MethodSignature){
                //3.把签名转成方法签名
                MethodSignature methodSignature = (MethodSignature)signature;
                //4.获取当前执行的方法
                Method method = methodSignature.getMethod();
                //5.得到方法名称
                String methodName = method.getName();
                //6.给系统日志中方法名称属性赋值
                log.setMethod(methodName);

                //设置当前执行的方法说明
                //7.判断当前方法上是否有@Description注解
                boolean isAnnotated = method.isAnnotationPresent(Description.class);
                if(isAnnotated){
                    //8.得到当前方法上的Description注解
                    Description description = method.getAnnotation(Description.class);
                    //9.得到注解的value属性
                    String value = description.value();
                    //10.给系统日志的方法说明属性赋值
                    log.setAction(value);
                }
            }

            System.out.println("环绕通知执行了记录日志:"+log);



            //获取切入点方法的参数
            Object[] args = pjp.getArgs();
            //切入点方法执行
            rtValue = pjp.proceed(args);
            return rtValue;
        }catch (Throwable e){
            throw new RuntimeException(e);
        }

当切面里面有2个一模一样的通知方法的生活

    @Before(value="execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void beforePrintLogC(String id,User user){
        System.out.println("前置通知增强了beforePrintLoga"+id+","+user);
    }

    @Before(value="execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void beforePrintLogC(User user,String id){
        System.out.println("前置通知增强了beforePrintLogC"+user+","+id);
    }

优先级是根据方法名的ASCII码值来的,当一直都一样的话会判断到参数里。

image-20200923093645819

当通知方法限制参数,就算表达式满足,参数不满足也不会执行

@Component
@Aspect//表明当前类是一个切面类
public class LogUtil {

    @Before(value = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void beforePrintLogC(String id, User user) {
        System.out.println("前置通知增强了beforePrintLoga" + id + "," + user);
    }

    @Before(value = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void beforePrintLogC(User user, String id) {
        System.out.println("前置通知增强了beforePrintLogC" + user + "," + id);
    }


    /**
     * 最终通知
     * 它是在切入点方法执行之后执行(注解驱动开发)
     */
    @After(value = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..)) && args(user,id)")
    public void afterPrintLog(User user, String id) {
        System.out.println("最终通知增强了" + user + "," + id);
    }

    /**
     * 后置通知
     * 它是在切入点方法正常执行并返回之前执行(此时已经执行完最终通知了)
     */
    @AfterReturning(pointcut = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..))", returning = "obj")
    public Object afterReturningPrintLog(Object obj) {
        System.out.println("后置通知增强了" + obj);
        return obj;
    }

    /**
     * 例外通知(异常通知)
     * 它是在切入点方法执行产生异常之后执行(此时已经执行完最终通知了)
     */
    @AfterThrowing(pointcut = "execution(* com.dalianpai.spring5.aop.service.impl.*.*(..))", throwing = "e")
    public void afterThrowingPrintLog(Throwable e) {
        System.out.println("异常通知增强了" + e);
    }
}

@Service("userService")
public class UserServiceImpl implements UserService {

    @Override
    public void saveUser(User user,String id) {
        System.out.println("执行了保存用户" + user+","+id);
    }

    @Override
    public User findById(String id) {
        User user = new User();
        user.setId("1");
        user.setUsername("test");
        user.setNickname("泰斯特");
        System.out.println("准备返回的对象是"+user);
      //  int i=1/0;
        return user;
    }
}

image-20200923095158332

posted @ 2020-09-23 10:14  天宇轩-王  阅读(144)  评论(0编辑  收藏  举报