springboot-使用AOP日志拦截实现

一 前言

借助spring的AOP功能,我们可以将AOP应用至全局异常处理,全局请求拦截等,本篇文章的核心功能就是使用AOP实现日志记录,比如哪些用户进行了哪些操作,对于一个成功的项目这是必须记录的,故知识追寻者这边给出一个简单模型应用;

二 定义枚举

枚举定义的类型就是实现日志的哪些操作,如下所示,有些是登陆日志,有些是增删改查日志,不同的系统可以定义不同的日志,读者可以自由选择;

public enum LogEnum {

    UNOPERATE(0,"未定义操作"),
    SELECT(1,"查询"),
    INSERT(2,"添加"),
    UPDATE(3,"更新"),
    DELETE(4,"删除"),
    EXPORT(5,"excel导出"),
    LOGIN(6,"登陆"),
    LOGOUT(7,"登出"),
    ;

    LogEnum( Integer code, String operate) {
        this.operate = operate;
        this.code = code;
    }

    LogEnum(){

    }

    // 操作
    private String operate;
    // 操作码
    private Integer code;

    public String getOperate() {
        return operate;
    }

    public void setOperate(String operate) {
        this.operate = operate;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

三 注解

使用注解的目的就是在哪些方法上使用注解,也就是标记这属于哪些操作,通过注解操作类型结合已经定义的枚举就可以简单的实现哪些方法上进行了哪些操作;

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MonitorLog {

    /* *
     * @Author lsc
     * <p>日志内容 </p>
     * @Param []
     * @Return java.lang.String
     */
    String value() default "";

    /* *
     * @Author lsc
     * <p>日志操作类型 </p>
     * @Param []
     * @Return int
     */
    LogEnum operateType() default LogEnum.UNOPERATE;


}

四 AOP切面

将注解设置为切点。将切点织入切面后使用环绕通知增强已经被注解标注的方法,此时就可以获得所有的注解操作,然后将日志记录入库,就可以简单实现用户的日志操作监控,当前提是在AOP中获得用户名,常见的shiro框架就有getSubject的方法获得用户名,当然根据不同的读者使用的技术不同进行获取,知识追寻者这边为了简单实现功能就没有使用一堆繁琐的方法去实现一个用户登陆认证系统;

/**
 * @Author lsc
 * <p>日志aop切面 </p>
 */

@Aspect
@Component
public class LogAsp {

    /* *
     * @Author lsc
     * <p> 设置切点</p>
     * @Param []
     * @Return void
     */
    @Pointcut("@annotation(com.zszxz.annotation.MonitorLog)")
    public void logPointCut() {

    }

    // 环绕通知
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 开始时间
        LocalDateTime beginTime = LocalDateTime.now();
        // 执行方法
        Object result = point.proceed();
        // 结束时间
        LocalDateTime endTime = LocalDateTime.now();
        Duration duration = Duration.between(beginTime, endTime);
        // 操作时长
        long seconds = duration.getSeconds();
        // 保存日志
        saveLog(point,seconds);
        return result;
    }

    private void  saveLog(ProceedingJoinPoint point, long seconds){
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        // 获得注解
        MonitorLog monitorLog = method.getAnnotation(MonitorLog.class);
        if (monitorLog!=null){
            // 获得操作类型
            LogEnum operateType = monitorLog.operateType();
            // 获得操作内容
            String value = monitorLog.value();
            System.out.printf("获得操作类型: %s , 获得操作内容: %s ",operateType.getCode(),value);

            //请求的参数
            Object[] args = point.getArgs();
            try{
                List<Object> objects = Arrays.asList(args);
                System.out.println(objects);
            }catch (Exception e){

            }

        }
    }
}

五表现层

controller 所示,模拟进行查询用户操作的方法,并且在方法上标明注解这是个查询用户的方法,当访问此API时就会被AOP拦截;

/**
 * @Author lsc
 * <p> </p>
 */
@RestController
public class UserController {


    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @MonitorLog(value="查询用户",operateType = LogEnum.SELECT)
    @GetMapping("zszxz")
    public String getUser(String user){
        return "zszxz";
    }


}

如果哪些不懂可以参照一下知识追寻者的源码(专栏说明或者公主号都要知识追寻者github地址),

posted @ 2020-03-09 12:39  知识追寻者  阅读(478)  评论(0编辑  收藏  举报