springboot使用AOP收集操作日志信息

编写这篇文章的主要原因是因为最近有个这样的需求,需要记录用户的操作日志信息,所以使用AOP写了一个收集的小demo

1.首先先明确需求,了解你项目使用的技术,再对你使用的技术,决定怎么编写

      我这里没有使用swagger 所以只能手写注解,获取用户的操作内容和操作模块

2.使用自定义注解,引入@interface

package info.bitfx.model.enums;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperationLogger {
    /**
     * 操作模块
     *
     * @return
     */
    String modelName() default "";

    /**
     * 操作具体内容
     *
     * @return
     */
    String option();
}

3.我们开始编写AOP切面来进行收集具体的操作日志信息

package info.bitfx.aspect;

import info.bitfx.model.enums.OperationLogger;
import info.bitfx.model.po.CompanyLoggerInfo;
import info.bitfx.model.po.StaffPO;
import info.bitfx.model.utils.AddressUtils;
import info.bitfx.model.utils.IPUtils;
import info.bitfx.service.CompanyLoggerInfoService;
import info.bitfx.service.StaffService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
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.lang.reflect.Parameter;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 定义切面,处理日志收集
 *
 * @author xxx
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private StaffService staffService;

    @Autowired
    private CompanyLoggerInfoService companyLoggerInfoService;

    /**
     * 定义切面
     */
    @Pointcut("@annotation(info.bitfx.model.enums.OperationLogger)")
    public void controllerAspect() {
    }

    /**
     * 切入点位置
     * 在目标方法执行完成后获取目标方法执行操作
     *
     * @param joinPoint
     */
    @Before("controllerAspect()")
    public void doAfterReturn(JoinPoint joinPoint) {
        handleLog(joinPoint);
    }

    /**
     * 具体处理收集代理操作的日志信息
     *
     * @param joinPoint
     */
    private void handleLog(JoinPoint joinPoint) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ip;
            String address;
            try {
                ip = IPUtils.getIpAddr(request);
                address = AddressUtils.getAddressByIp(ip);
            } catch (Exception e) {
                ip = "未知";
                address = "未知";
            }
            StaffPO staff = null;
            Principal userPrincipal = request.getUserPrincipal();
            if (!Objects.isNull(userPrincipal)) {
                String name = request.getUserPrincipal().getName();
                staff = staffService.findByUserName(name);
            }
            //获取横切的方法名
            String methodName = joinPoint.getSignature().getName();
            //获取拦截的class
            String classType = joinPoint.getTarget().getClass().getName();
            //加载这个类
            Class clazz = Class.forName(classType);
            //获取这个类上的方法名
            Method[] methods = clazz.getDeclaredMethods();
            System.out.println("methodName:" + methodName);
            /**
             * 循环判断,对所有方法进行循环遍历
             */
            for (Method method : methods) {
                // 如果这个方法上面的注解是否含有自定义的注解
                // 并且方法名等于切点访问的方法名
                if (method.isAnnotationPresent(OperationLogger.class)
                        && method.getName().equals(methodName)) {
                    //获取method的注解
                    OperationLogger operationLogger = method.getAnnotation(OperationLogger.class);
                    CompanyLoggerInfo info = new CompanyLoggerInfo();
                    if (staff != null) {
                        info.setStaffId(staff.getId());
                        info.setCompanyId(staff.getCompanyId());
                    } else {
                        info.setStaffId(null);
                        info.setCompanyId(null);
                    }
                    info.setOptIp(ip);
                    info.setIpAddress(address);
                    info.setCreateTime(LocalDateTime.now());
                    info.setOptUrl(String.valueOf(request.getRequestURL()));
                    info.setOptContent(operationLogger.option());
                    info.setOptMethod(request.getMethod());
                    info.setOptModel(operationLogger.modelName());
                    info.setOptParams(String.valueOf(getParameter(request, method, joinPoint.getArgs())));
                    companyLoggerInfoService.save(info);
                }
            }
        } catch (Exception e) {
            log.error("记录操作日志失败");
        }
    }

    /**
     * 根据方法和传入的参数获取请求参数
     */
    private Object getParameter(HttpServletRequest request, Method method, Object[] args) {
        List argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestBody != null) {
                argList.add(args[i]);
            } else if (requestParam != null) {
                //将RequestParam注解修饰的参数作为请求参数
                Map map = new HashMap<>(8);
                String key;
                if (!StringUtils.isEmpty(requestParam.value()) && !Objects.isNull(args[i])) {
                    key = requestParam.value();
                    if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(args[i].toString())) {
                        map.put(key, args[i]);
                        argList.add(map);
                    }
                }
            }
            parameters[i].getType().getClass();
        }
        if (argList.size() == 0) {
            //当请求参数中没有使用@RequestParam or  @RequestBody ,使用request.getParameterMap()获取请求参数
            if (parameters.length > 0) {
                Map parameterMap = request.getParameterMap();
                Map map = new HashMap<>(8);
                parameterMap.forEach((k, v) -> {
                    List strings = Arrays.asList(v);
                    if (!StringUtils.isBlank(k) && strings.size() > 0) {
                        //只存代理商使用请求查询中不为空的参数和值
                        map.put(k, strings.get(0));
                    }
                });
                if (map.size() > 0) {
                    argList.add(map);
                }
            }
            if (argList.size() == 0) {
                return null;
            }
            return argList;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }

}

4.最后我们在controller中需要收集的方法上加上@OperationLogger注解,并且记录操作的模块和操作方法名称

springboot使用AOP收集操作日志信息_第1张图片

 5.使用postman测试下,功能是否生效,发现已成功记录用户的操作记录信息,该功能也就完成,是不是很简单。

你可能感兴趣的:(spring,boot,java)