分享

SpringBoot使用AOP(环绕通知)完成对用户操作的日志记录

 jackeyqing 2020-06-05

什么是AOP?

AOP为Aspect Oriented Programming的缩写,是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

AOP的五种通知方式?

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 前置通知(Before):在目标方法或者说连接点被调用前执行的通知;
  2. 后置通知(After):指在某个连接点完成后执行的通知;
  3. 返回通知(After-returning):指在某个连接点成功执行之后执行的通知;
  4. 异常通知(After-throwing):指在方法抛出异常后执行的通知;
  5. 环绕通知(Around):指包围一个连接点通知,在被通知的方法调用之前和之后执行自定义的方法。

代码搞起

pom文件添加依赖包

<!--spring切面aop依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!-- json解析依赖 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.47</version>
</dependency>

新建自定义注解类

/**
 * 自定义注解类  OperLog
 * Created by LMD on 2019/3/22.
 */
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface OperLog {
        String message();  // 介绍
        String operation();  // 日志类型
}

新建日志实体类

/**
 * 日志实体类 SysLog 
 * Created by LMD on 2019/3/22.
 */
public class SysLog implements Serializable {

    private Long id;  //

    private String userId;  // 操作用户 ID

    private String message;  // 消息

    private String operation;  // 日志类型

    private String method;  // 请求方法

    private String params;  // 请求参数

    private String ip;  // 请求IP

    private Date createDate;// 请求时间

    private Long totalTime; //总耗时长(毫秒)
    
   //添加set和get方法
}

新建操作类型类

/**
 * 操作类型类  OperationType 
 * Created by LMD on 2019/3/22.
 */
public class OperationType {

    /**
     * 添加
     */
    public static final String ADD = "ADD";
    /**
     * 删除
     */
    public static final String DELETE = "DELETE";
    /**
     * 更新
     */
    public static final String UPDATE = "UPDATE";
    /**
     * 查询
     */
    public static final String QUERY = "QUERY";

    /**
     * 登录
     */
    public static final String LOGIN = "LOGIN";

    /**
     * 退出登录
     */
    public static final String LOGOUT = "LOGOUT";

}

新建切面处理类

注意这句:proceedingJoinPoint.proceed() 环绕通知一定要加上这句代码,没调用proceed()方法,代码就会不会执行业务代码。

import com.alibaba.fastjson.JSON;
import com.stcn.companycms.util.ConnUtil;
import com.stcn.companycms.util.ResultInfo;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 *
 * 系统日志:切面处理类
 *
 * Created by LMD on 2019/3/22.
 */
@Aspect
@Component
public class SysLogAspect {


    private static Logger LOG = LoggerFactory.getLogger(SysLogAspect.class);
    
    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation( com.xxxxx.xxxxx.xxxxx.xxxxx.OperLog)")
    public void logPoinCut() {
    }
   
    //@Around:环绕通知
    @Around("logPoinCut()")
    public Object saveSysLog(ProceedingJoinPoint proceedingJoinPoint) {

        System.out.println("环绕通知开始。。。。。");
        //保存日志
        SysLog sysLog = new SysLog();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
       //获取操作
        OperLog myLog = method.getAnnotation(OperLog.class);
        if (myLog != null) {
            String value = myLog.message();
            sysLog.setMessage(value);//保存获取的操作
        }
        //获取请求的类名
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);

        //请求的参数
        Object[] args = proceedingJoinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = JSON.toJSONString(args);
        sysLog.setParams(params);

        sysLog.setCreateDate(new Date());
        //获取用户名
        //获取用户ip地址
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        LOG.info("URL : " + request.getRequestURL().toString());
        LOG.info("HTTP_METHOD : " + request.getMethod());
        LOG.info("IP : " + request.getRemoteAddr());
        sysLog.setIp(request.getRemoteAddr());

        //开始调用时间
        // 计时并调用目标函数
        long start = System.currentTimeMillis();
        Long time = System.currentTimeMillis() - start;
        sysLog.setTotalTime(time);

        //调用service保存SysLog实体类到数据库
        //sysLogService.save(sysLog);
        try {
             Object result = proceedingJoinPoint.proceed();
            System.out.println("    "+result.toString());
            System.out.println("环绕通知结束。。。。。");
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return   new ResultInfo<Object>(ConnUtil.ERROR,"系统异常");
    }
}

将注解加到需要记录用户操作的方法体上

在业务代码的Controller方法上添加我们自己定义的注解就可以了:
@OperLog(message = “日志描述”,operation = 日志类型)

 /**
     * web用户登录
     * @param webLoginPojo 账号  密码  验证码
     * @param
     * @return
     */
    @OperLog(message = "用户登录",operation = OperationType.LOGIN)
    @RequestMapping(value = "/webLogin")
    @ResponseBody
    public ResultInfo<Object> webLogin(@RequestBody WebLoginPojo webLoginPojo){
       	try {
            System.out.println("欢迎登录");
            return new ResultInfo<Object>(ConnUtil.SUCCESS,"登录成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ResultInfo<Object>(ConnUtil.ERROR,"服务器异常");
    }

最后

谢谢大家的参考、查看;
可能大家在实际写代码的过程中有不一样的异常出错,大家可以留言一起讨论学习。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多