前言
一直想搞懂AOP的使用,网上大多数文章都是讲原理,什么使用了JDK代理或者cglib代理。对于初学者来说,其实更想知道AOP是怎么使用的,所以整理了这篇文章作为自己的笔记,不介绍原理,而仅仅介绍AOP如何在项目中实践。
概念
- 首先需要知道AOP的概念,涉及到的概念比较多,如果需要了解清楚,可以查阅下这篇文章,讲的很详细,也比喻得很生动。细说Spring——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);
}
}
复制代码
-
以上就是基于注解的AOP方式,如果想在哪个方法调用AOP就只要加上注解
-
参考博客链接:spring中基于注解使用AOP
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END