Spring AOP使用实践

前言

一直想搞懂AOP的使用,网上大多数文章都是讲原理,什么使用了JDK代理或者cglib代理。对于初学者来说,其实更想知道AOP是怎么使用的,所以整理了这篇文章作为自己的笔记,不介绍原理,而仅仅介绍AOP如何在项目中实践。

概念

我只说几个重要的概念:

  • Aspect(切面):具体到代码的话,就是切面类,比如定义一个日志切面类,这个类叫做LoggerAspect,就是切面
  • Pointcut(切点):就是规则范围,比如你这个切面类的方法要包下所有的类都适用,还是只在包下某个类的方法适用。
  • Advice(增强):指的是方法的动作,比如你在切面类LoggerAspect定义了一个方法method,这个方法你指明了切点范围只对某个包的某个类的某个方法A适用,那是在方法A执行之前调用切面类的method还是在方法A执行之后再调用切面类的method,又或者是前后都执行。这分别对应了Advice里的三种动作,before、after 和 around,around最常用。
  • 我理解的AOP其实就是将散落的可复用的公共逻辑抽取出来,放在一起作为一个切面类,并且这个切面类的方法可自定义在什么时候执行,有点像拦截器。

场景

  • 我有一个UserController类,类中有个添加用户的方法
    @ApiOperation("添加用户")
    @PostMapping("/addUser")
    public ResultVo addUser(@Validated @RequestBody AddUserForm userForm){
        if(userService.addUser(userForm)){
            return ResultVoUtil.success();
        }else{
            return ResultVoUtil.error(ResultEnum.ADD_ERROR);
        }
   }
复制代码
  • 我想在这个方法调用前后都打印下日志,于是定义了一个日志切面类LoggerAspect,定义了两个方法,这两个方法的切点是UserController类的addUser方法(“execution(* com.hxh.basic.project.controller.UserController.addUser(..))”),Advice动作是在调用前 @Before和调用后 @After
@Aspect
@Component
public class LoggerAspect {

    //Advice的类型主要有before,after,around;around是最常用的 advice,意为在代码前后都执行
    //下面的before和after合在一起相当于around
    /**
     * addUser()执行之前执行
     */
    @Before("execution(* com.hxh.basic.project.controller.UserController.addUser(..))")
    public void callBefore() {
        System.out.println("before call method");
        System.out.println("begin........................");
    }

    /**
     * addUser()执行之后执行
     */
    @After("execution(* com.hxh.basic.project.controller.UserController.addUser(..))")
    public void callAfter() {
        System.out.println("after call method");
        System.out.println("end..............................");
    }
 }
复制代码
  • Advice如果使用了Around可以代替@Before和@After
@Aspect
@Component
public class LoggerAspect {

    //Advice的类型主要有before,after,around;around是最常用的 advice,意为在代码前后都执行
    //before和after合在一起相当于around

    //============使用了Around代替Before和After=======================
    /**
     * 记录执行时间
     * @param point 切点
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.hxh.basic.project.controller.UserController.addUser(..))")
    public Object getMethodExecuteTime(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        //这句代码是调用目标方法,比如调用UserController的addUser方法
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }
 }
复制代码
  • 如此便是AOP的简单使用,但这样其实还是不够灵活,如果想达到记录UserController中的多个方法的执行时间的目的,只能通过修改切点pointcut的规则来指定哪些方法。所以在企业工程中为了自定义地控制,大多是使用基于注解使用AOP。

基于注解的AOP

  • 首先自定义一个注解

这样以后如果想在哪个方法上使用AOP,则只要在那个方法上打上这个注解就可以了

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OpLog {
    /**
     * 方法描述,可使用占位符获取参数:{{tel}}
     */
    String value() default "";

    /**
     * 日志等级:自己定,此处分为1-9
     */
    int level() default 4;

    /**
     * 操作类型(enum):主要是select,insert,update,delete
     */
    OperationType operationType() default OperationType.UNKNOWN;

    /**
     * 被操作的对象(此处使用enum):可以是任何对象,如表名(user),或者是工具(redis)
     */
    String operationUnit() default "unknown";
}
复制代码

在LoggerAspect加入这样一个方法:

 /**
     * @param point
     * @return
     * @throws Throwable
     */
//    @Around("operationLog()")
    @Around("@annotation(com.hxh.basic.project.aop.annotation.OpLog)")
    public Object getMethodExecuteTimeForLogger(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }
复制代码

哪个方法需要记录执行时间就将@OpLog放在对应的方法上:

/**
     * 添加用户
     * @param userForm 表单数据
     * @return 成功或者失败
     * 使用了基于注解的AOP OpLog
     */
    @JwtIgnore
    @ApiOperation("添加用户")
    @PostMapping("/addUser")
    @OpLog(value = "添加用户", level = 8, operationUnit = "UserController", operationType = OperationType.INSERT)*
    public ResultVo addUser(@Validated @RequestBody AddUserForm userForm){
        if(userService.addUser(userForm)){
            return ResultVoUtil.success();
        }else{
            return ResultVoUtil.error(ResultEnum.ADD_ERROR);
        }
    }
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享