一 概述Aop
1.1 自定义注解和Aop
通过自定义注解和AOP的组合使用,可以实现一些通用能力的抽象 ,可以实现登录权限拦截、日志收集、方法统计耗时等。避免在每个方法都写一份同样的功能,代码的重复冗余。
使用AOP同时结合自定义注解实现接口的切面编程,在需要进行通用逻辑处理的接口或者类中增加对应的注解即可。
二 操作案例
2.1 概述
2.1.1 工程结构
2.1.2 配置pom内容
<!--spring boot的启动类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<scope>provided </scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.0</version>
</dependency>
2.2 实现日志收集案例
2.2.1 自定义枚举
1.业务枚举
package com.ljf.aopdemo.log.enums;
public enum BusinessType {
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
ASSGIN,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 更新状态
*/
STATUS,
/**
* 清空数据
*/
CLEAN,
}
2.功能类型枚举
package com.ljf.aopdemo.log.enums;
/**
* 操作人类别
*/
public enum OperatorType {
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}
3.注解
package com.ljf.aopdemo.log.annotation;
import com.ljf.aopdemo.log.enums.BusinessType;
import com.ljf.aopdemo.log.enums.OperatorType;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
}
2.2.2 切面类
代码
package com.ljf.aopdemo.log.aspect;
import com.alibaba.fastjson.JSON;
import com.ljf.aopdemo.log.annotation.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
@Aspect
@Component
public class LogAspect {
// @Autowired
//private OperLogService operLogService;
@AfterReturning(pointcut = "@annotation(sbLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log sbLog, Object jsonResult) {
handleLog(joinPoint, sbLog, null, jsonResult);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
// *========数据库日志=========*//
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
System.out.println("calssname:"+className+" methodname:"+methodName);
} catch (Exception exp) {
exp.printStackTrace();
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
String params = "";
if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) {
if (!StringUtils.isEmpty(o) && !isFilterObject(o)) {
try {
Object jsonObj = JSON.toJSON(o);
params += jsonObj.toString() + " ";
} catch (Exception e) {
}
}
}
}
return params.trim();
}
/**
* 判断是否需要过滤的对象。
*
* @param o 对象信息。
* @return 如果是需要过滤的对象,则返回true;否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}
2.2.3 触发收集日志的配置
@RestController
public class RoleController {
@GetMapping("/testjson")
@Log(title = "角色管理testjson",businessType = BusinessType.INSERT)
public Object testjson(){
Map<String,String> map = new HashMap<>();
return "ok成功" ;
}
}
2.2.4测试验证
1.页面访问
2.日志打印
2.3 实现统计耗时案例
2.3.1 自定义注解
SpringBoot 自定义注解和AOP实现统计接口耗时_springboot aop 统计接口调用时长-CSDN博客
package com.ljf.aopdemo.log.annotation;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CrosTimeLog {
}
2.3.2 切面类
package com.ljf.aopdemo.log.aspect;
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.springframework.stereotype.Component;
/**
* @ClassName: CrosTimeAspect
* @Description: TODO
* @Author: admin
* @Date: 2024/03/23 16:58:15
* @Version: V1.0
**/
@Aspect
@Component
@Slf4j
public class CrosTimeAspect {
@Pointcut(value = "@annotation(com.ljf.aopdemo.log.annotation.CrosTimeLog)")
public void costTime() {
}
@Around("costTime()")
public Object costTimeAround(ProceedingJoinPoint joinPoint) {
Object obj = null;
try {
long beginTime = System.currentTimeMillis();
obj = joinPoint.proceed();
//获取方法名称
String method = joinPoint.getSignature().getName();
//获取类名称
String className=joinPoint.getSignature().getDeclaringTypeName();
//计算耗时
long cost = System.currentTimeMillis() - beginTime;
log.info("类:[{}],方法:[{}] 接口耗时:[{}]", className,method, cost + "毫秒");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return obj;
}
}