1、AOP介绍
AOP(Aspect Orient Programming) : 面向切面编程,是一种编程技术,底层通过动态代理实现
1.1、使用场景
当你的某些方法需要增加一些相同的功能时,给业务方法增加非业务功能。可以使用AOP实现。
比如:日志、事务管理、安全等
1.2、作用
减少代码冗余,复用性强;
提高开发效率,使开发人员专注业务逻辑;
代码易维护;
1.3、相关术语
Aspect : 切面,给业务方法增加的功能。
JoinPoint : 连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能。
Pointcut : 切入点,是一个或多个连接点的集合。表示这些方法执行时,都能增加切面的功能。表示切面执行 的位置。
Advice : 通知,表示切面的执行时间,在目标方法之前执行切面还是目标方法之后执行切面。
1、@Before(前置通知):在方法执行前执行
2、@After(后置/最终通知):在方法执行之后执行
3、@Around(环绕通知):在方法执行之前和之后调用的通知
4、@AfterThrowing(异常通知):在方法抛出异常之后。
5、@AfterRunning (后置返回通知):在方法正常返回结果之后执行
target : 目标对象,给哪个对象增加切面的功能,这个对象就是目标对象。
2、自定义注解实现
1、注解定义
创建自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface SysLog {
//操作描述
String action() default "";
//操作类型
String operateType();
//记录响应
boolean printResponse() default true;
//记录错误
boolean printError() default true;
}
2、AOP实现
@Aspect
@Configuration
public class SysLogAspect {
@Autowired
private AsyncOperation asyncOperation;
@Autowired
private Environment env;
private static final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);
/**
* @return void
*/
@Pointcut("@annotation(com.test.common.annotation.SysLog)")
public void cutMethod() {
}
/**
* @param proceedingJoinPoint
* @return java.lang.Object
* @throws
*/
@Around("cutMethod()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
SysLogEntity sysLogEntity = new SysLogEntity();
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
SysLog dbLog = method.getAnnotation(SysLog.class);
try {
sysLogEntity.setClassName(proceedingJoinPoint.getTarget().getClass().getName());
//记录归属哪个服务模块
sysLogEntity.setModuleName(env.getProperty("spring.application.name"));
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
sysLogEntity.setMethodName(methodSignature.getMethod().getName());
Object[] args = proceedingJoinPoint.getArgs();
int bodyIndex = -1;
List<Integer> paramIndexList = new ArrayList<>();
List<String> paramNameList = new ArrayList<>();
int tmpIndex = 0;
for (Annotation[] parameterAnnotation : method.getParameterAnnotations()) {
for (Annotation annotation : parameterAnnotation) {
if (annotation instanceof RequestBody) {
bodyIndex = tmpIndex;
} else if (annotation instanceof RequestParam) {
RequestParam requestParam = (RequestParam) annotation;
String paramName = StringUtils.isEmpty(requestParam.name()) ? requestParam.value() : requestParam.name();
paramIndexList.add(tmpIndex);
paramNameList.add(StringUtils.isEmpty(paramName) ? "param" + tmpIndex : paramName);
} else if (annotation instanceof PathVariable) {
PathVariable requestParam = (PathVariable) annotation;
String paramName = StringUtils.isEmpty(requestParam.name()) ? requestParam.value() : requestParam.name();
paramIndexList.add(tmpIndex);
paramNameList.add(StringUtils.isEmpty(paramName) ? "param" + tmpIndex : paramName);
}
}
tmpIndex++;
}
StringBuffer requestBody = new StringBuffer();
//记录请求参数
//body参数
if (bodyIndex > -1) {
requestBody.append("RequestBody:").append(JSON.toJSONString(args[bodyIndex]));
if (!paramIndexList.isEmpty()) {
requestBody.append("\n");
}
}
//表单参数
if (!paramIndexList.isEmpty()) {
requestBody.append("RequestParam:");
for (int i = 0; i < paramIndexList.size(); i++) {
if (i > 0) {
requestBody.append("&");
}
requestBody.append(paramNameList.get(i)).append("=").append(args[paramIndexList.get(i)]);
}
}
sysLogEntity.setRequestData(requestBody.toString());
String actionDesc = dbLog.action();
if (StringUtils.isEmpty(actionDesc)) {
ApiOperation annotation = methodSignature.getMethod().getAnnotation(ApiOperation.class);
if (annotation != null) {
actionDesc = annotation.value();
}
}
sysLogEntity.setUserId(AuthUtil.getUser().getId() + "");
sysLogEntity.setRequestIp(WebUtil.getIP());
sysLogEntity.setOperateType(dbLog.operateType());
sysLogEntity.setActionDesc(actionDesc);
Object result = proceedingJoinPoint.proceed();
if (dbLog.printResponse()) {
sysLogEntity.setResponseData(result.toString());
}
sysLogEntity.setStatus(HttpStatus.HTTP_OK);
return result;
} catch (Exception ex) {
if (dbLog.printError()) {
sysLogEntity.setErrMsg(ExceptionUtils.getErrMsg(ex));
}
sysLogEntity.setStatus(HttpStatus.HTTP_INTERNAL_ERROR);
throw ex;
} finally {
sysLogEntity.setCreateTime(new Date());
//调用外部接口,将记录发送过去记录(异步发送)
asyncOperation.addLog(sysLogEntity);
}
}
}
3、应用示例
@ApiOperation("查询系统用户详情(不脱敏)")
@GetMapping(value = "/getDetailUserInfo")
@SysLog(operateType = "查看敏感信息")
public Result<SimpleUserInfo> getDetailUserInfo(@RequestParam String userId) {
return Result.succeed(sysUserInfoService.getDetailUserInfo(userId));
}
最终可以看到操作记录已经保存到数据库