记录折腾的那点事
在折腾的道路上永不止步

Springboot 基于AOP记录操作日志

现在凡是企业级的或者稍微大点项目,基本都需要日志管理. 我这边在springboot基础上做了个日志信息记录到数据库的功能,在这里备份一下,以后有需要就省的再重写了.
首先我们得准备好所需要的jar,当然了这里是pom.xml:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

这里的web是最基本的,后面也会用到的.
其实日志管理实现的方式有很多种,拦截器,aop切面等等,我这边用的就是aop切面实现的.既然要用到切面实现,那就必须要有切点,我这边是以自定义注解为切点(当然也可以切到指定路径下的文件夹哦)
下面为自定义切点:

package com.jshh.busness.LogAOP;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)     // 这个注解是用来规定注解的作用范围的,这里定义为method方法级别.
@Retention(RetentionPolicy.RUNTIME)    // 这个注解可以理解为定义注解的生命周期,这里标识一直存在(编译和运行之后)
public @interface LogAnnotation {
// 定义注解参数
    public  String operateContent() default  "";
    public  String operateType() default  "";
}

这边简单的介绍下@Target和@Retention注解吧.
@Target 用来取值 注解使用范围:

METHOD  可用于方法上
    TYPE    可用于类或者接口上
    ANNOTATION_TYPE 可用于注解类型上(被@interface修饰的类型)
    CONSTRUCTOR 可用于构造方法上
    FIELD   可用于域上
    LOCAL_VARIABLE  可用于局部变量上
    PACKAGE 用于记录java文件的package信息
    PARAMETER   可用于参数上

@Retention

1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

回到正题,注解弄完了,现在要弄一个实体类来封装所需要的日志信息:

import com.jshh.entity.CommenEntity;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class LogEntity implements CommenEntity {
    private String uuid;
    private String userid;
    private String username;
    private String ip;
    private String status;
    private String operatetype;
    private String detail;
    private String operatetime;
    private String operatecontent;
    private String subsystemid;
}

基础准备都弄完了,现在就要去做切面的实现类了.废话不多说代码附上:

package com.jshh.busness.LogAOP;

import com.alibaba.fastjson.JSON;
import com.jshh.client.DataCommenServerClient;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
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.HashMap;
import java.util.Map;
import java.util.UUID;



/**
 * @author  一只会飞的猪
 * @desc    日志录入aop实现类
 * @time    2018/8/28
 * */
@Component
@Aspect
public class LogAspectClass {
    @Autowired
    LogAddService logAddService;

    // 这里定义下切点的位置,也就是刚才我们自定义的注解.
    @Pointcut("@annotation(com.jshh.busness.LogAOP.LogAnnotation)")
    public  void mypointcut(){}

    //消息通知 @AfterReturning,在切点方法运行之后触发returning 为目标函数返回值
    @AfterReturning(returning = "result",value = "mypointcut()")
    public  void addlog(JoinPoint joinPoint,Object result){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        String operatetype ="";         //  定义操作方式
        String operatecontent="";       //  定义操作内容

        // 获取注解中的操作方式
        if(method!=null&&!"".equals(method)){
            // 获取自定义注解操作
            LogAnnotation  logAnnotation = method.getAnnotation(LogAnnotation.class);
            // 获取用户操作方式
             operatetype = logAnnotation.operateType();
            // 获取用户操作内容
             operatecontent = logAnnotation.operateContent();
        }

        // 获取请求的类名
        String classname = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodname = classname+"."+method.getName();
        // 获取请求方式
        String Method = request.getMethod();
        // 获取请求url
        String URL = request.getRequestURI().toString();
        // 获取请求的ip地址
        String IP  = request.getRemoteAddr();
        // 获取userid
        String userid = request.getParameter("userid");
        // 获取子系统id
        String subsystemid = request.getParameter("subsystemid");
        // 生成uuid
        String uuid = UUID.randomUUID().toString();
        // 获取请求的参数
        String argsname[] = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        Map<String,Object> parammap = new HashMap<>();
        if(argsname.length>0){
            parammap= getParam(joinPoint,argsname,methodname);
        }
        String detail = JSON.toJSONString(parammap);
        // 获取操作状态
        Map<String,Object> statusmap = new HashMap<>();
        String status="";
        statusmap = (Map<String, Object>) result;
        Integer code= (Integer)statusmap.get("code");
        if(code==0){
            status = "失败";
        }else{
            status = "成功";
        }

        // 日志实体类封装
        LogEntity logEntity = new LogEntity();
        logEntity.setUserid(userid);
        logEntity.setUuid(uuid);
        logEntity.setIp(IP);
        logEntity.setStatus(status);
        logEntity.setOperatetype(operatetype);
        logEntity.setOperatecontent(operatecontent);
        logEntity.setDetail(detail);
        logEntity.setSubsystemid(subsystemid);

        logAddService.addLogInfo(logEntity);

        System.out.println("aop+++++++++++++++++++++切面++++++++++++++++++++");
        System.out.println("用户操作方式:----------"+operatetype);
        System.out.println("用户操作内容:----------"+operatecontent);
        System.out.println("请求方式:-------------"+Method);
        System.out.println("请求地址url:----------"+IP+URL);
        System.out.println("请求ip地址:------------"+IP);
        System.out.println("请求参数:------------"+request.getParameterNames().toString());
        System.out.println("请求参数:============"+request.getQueryString());
        System.out.println("请求方法名:============"+methodname);
        System.out.println("请求userid:============"+userid);
        System.out.println("返回参数:============"+detail);
        System.out.println("返回结果状态:============"+status);
        System.out.println("返回结果:============"+logEntity.toString());

    }


    // 处理参数格式,并返回需要的参数
    public static Map<String, Object> getParam(JoinPoint joinPoint,String argsname[],String methodname) {
        Map<String,Object> detailmap = new HashMap<>();
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> mapCODE = new HashMap<>();
        // 获取参数值
        Object args[] = joinPoint.getArgs();
        // 获取参数名
        argsname = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        String paramsString = "";
           for(int i=0; i < argsname.length; i++) {
               if (!argsname[i].equals("model")) {
                   map.put(argsname[i], args[i]);
               }
           }
        detailmap.put("method",methodname);
        detailmap.put("params",map);
        System.out.println("detail:====="+detailmap.toString());
        return  detailmap;
    }
}

至于保存数据的方法这个就不用多说了吧,该咋保存就咋保存,除了这个方法,其他代码拷贝就能用的哦.
整个操作,不需要编写什么配置文件的,以前的ssm的xml配置,现在都是几个注解搞定的事.

最后添加一个controller:

@GetMapping("queryRoleByUserid")
    @LogAnnotation(operateType = "角色",operateContent = "根据用户id查询角色相关信息")
    public Model queryRoleByUserid(Model model, @RequestParam("userid") String userid) throws Exception {
        boolean ret = true;
        String msg = "";
        Object result = null;
        try {
            result = userService.queryRoleByUserid(userid);
        } catch (Exception e) {
            ret = false;
            msg = e.getMessage();
            e.printStackTrace();
        }
        model = buildModel(model, ret, result, msg);
        return model;
    }

至于保存数据的方法这个就不用多说了吧,该咋保存就咋保存,除了这个方法,其他代码拷贝就能用的哦.
整个操作,不需要编写什么配置文件的,以前的ssm的xml配置,现在都是几个注解搞定的事.

赞(0)
未经允许不得转载:ghMa » Springboot 基于AOP记录操作日志
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址