1、spring aop
AOP :面向切面( Aspect )编程。 AOP 并不是 Spring 框架的特性,只是 Spring 很好的支持了 AOP。
如果需要在处理每个业务时,都执行特定的代码,则可以假设在整个数据处理流程中存在某个切
面,切面中可以定义某些方法,当处理流程执行到切面时,就会自动执行切面中的方法。最终实现的效果就是:只需要定义好切面方法,配置好切面的位置(连接点),在不需要修改原有数据处理流程的代码的基础之上,就可以使得若干个流程都执行相同的代码。
2、切面方法
1. 切面方法的访问权限是 public 。
2. 切面方法的返回值类型可以是 void 或 Object ,如果使用的注解是 @Around 时,必须使用 Object作为返回值类型,并返回连接点方法的返回值;如果使用的注解是 @Before 或 @After等其他注解时,则自行决定。
3. 切面方法的名称可以自定义。
4. 切面方法的参数列表中可以添加 ProceedingJoinPoint接口类型的对象,该对象表示连接点,也可以理解调用切面所在位置对应的方法的对象,如果使用的注解是 @Around时,必须添加该参数,反之则不是必须添加。
3、依赖
在使用 Spring AOP 编程时,需要先在 pom.xml 文件中添加两个关于 AOP 的依赖 aspectjweaver 和
aspectjtools。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
</dependency>
4、代码例子
4.1 基于aop实现打日志:
1、统一接口返回值类型,如果各个接口返回值类型不相同,会造成接口返回数据为空
package com.sll.hosptials.aop;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class LogAspect {
// @Pointcut("execution(* com.sll.hosptials.service.*.*(..))")
// public void pc(){
//
// }
// 注意*和com之间一定要有空格liquibase
// @Around("pc()")
@Around("execution(* com.sll.hosptials.service.*.*(..))")
public Object logAspect(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 方法所属类的类名
// String className = methodSignature.getDeclaringTypeName();
// 获取当前切点方法对象
Method method = methodSignature.getMethod();
// String args = Arrays.toString(joinPoint.getArgs()); // 存在问题,extends的实体类参数不能获取
Object[] args = joinPoint.getArgs();
Object o = JSON.toJSON(JSON.toJSONString(args));
log.info("测试根据aop获取方法名及入参信息" + "方法:" + method + "参数:" + o);
Object object = joinPoint.proceed();
return object;
}
}
2、适用于接口无返回值:
package com.sll.hosptials.aop;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class LogAspect {
// 注意*和com之间一定要有空格
@Around("execution(* com.sll.hosptials.service.*.*(..))")
public void logAspect(JoinPoint joinPoint) {
// 注意返回void,会造成接口返回数据为空
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 方法所属类的类名
String className = methodSignature.getDeclaringTypeName();
// 获取当前切点方法对象
Method method = methodSignature.getMethod();
// String args = Arrays.toString(joinPoint.getArgs()); // 存在问题,extends的实体类参数不能获取
Object[] args = joinPoint.getArgs();
Object o = JSON.toJSON(JSON.toJSONString(args));
log.info("测试根据aop获取方法名及入参信息" + "方法:" + method + "参数:" + o);
}
}
3、入参类型MultipartFile,解析失败,处理方式
package com.sll.hosptials.aop;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Method;
@Aspect
@Component
@Slf4j
public class LogAspect {
@Pointcut("execution(* com.sll.hosptials.service.*.*(..))")
public void pc() {
}
// 注意*和com之间一定要有空格liquibase
@Around("pc()")
// @Around("execution(* com.sll.hosptials.service.*.*(..))")
public Object logAspect(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 方法所属类的类名
// String className = methodSignature.getDeclaringTypeName();
// 获取当前切点方法对象
Method method = methodSignature.getMethod();
// String args = Arrays.toString(joinPoint.getArgs()); // 存在问题,extends的实体类参数不能获取
Object[] args = joinPoint.getArgs();
JSONObject jsonObject = new JSONObject();
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof MultipartFile) {
MultipartFile file = (MultipartFile) arg;
// 处理MultipartFile
jsonObject.set("type", "文件上传");
jsonObject.set("filename", file.getOriginalFilename());
jsonObject.set("param", "file");
} else {
jsonObject.set("param" + i, arg);
}
}
Object params = ObjectUtil.clone(jsonObject);
// JSON.toJSON(JSON.toJSONString(arg));
log.info("测试根据aop获取方法名及入参信息" + "方法:" + method + "参数:" + params);
Object object = joinPoint.proceed();
return object;
}
}
4.2 aop日志效果图:
点击方法旁m会出现切到的方法