springboot使用aop切面做用户操作日志记录

用户操作日志不过就是记录访问用户的ip,访问了啥,访问时间而已。然后想想每访问一个接口就要记录一次,难道是在每个接口都要new一个日志类,然后设置值,之后就save?要是有几十个接口,就在这几十个接口里都写上日志入库逻辑?而且是日志入库逻辑都是一样的。那想想,我可不可以就写个通用的日志入库逻辑方法,然后在每个接口执行之前都先执行这个日志入库逻辑方法,那就不需要每个接口都写上日志入库逻辑类了,只需要每次执行接口之前都调用那个日志入库方法就行。

然后这种思维就是aop切面编程。切面就是日志入库逻辑方法,切点就是接口。意思就是在切点那里插入切面,就是在接口那里执行日志入库方法。下面是使用aop记录用户操作日志实例:

在xml导入aop包:

首先自定义一个注解类

然后就是Aspect了:

@Aspect
@Component
public class LoggerAspect {

 

    @Autowired
    private LoggerService loggerService;
    @Autowired
    private EmployeeService employeeService;


    // 定义切点 @Pointcut。是面前注解类的地址。
    @Pointcut("@annotation(com.gzydt.oa.auth.utils.LoggerOperator)")
    public void controllerAspect() {

    }

    /**
     * @Description 前置通知 ,就是在有LoggerOperator注解的接口执行前执行这个doBefore()方法。
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {

        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                    .getRequest();
            String employeeId = request.getHeader("employeeId");
            String ip = IpUtil.getIpAdrress(request);
            String method = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();
            String role = employeeService.get(employeeId).getPosition();
            String desc = getControllerMethodDescription(joinPoint);
            Logger logger = new Logger();
            logger.setIp(ip);
            logger.setOperator(employeeId);
            logger.setRemark(method);
            logger.setRole(role);
            logger.setCreateTime(new Date());
            logger.setDescription(desc);
            loggerService.add(logger);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description 获取注解中对方法的描述信息 用于Controller层注解
     */
    public static String getControllerMethodDescription(JoinPoint joinPoint) throws ClassNotFoundException {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();// 目标方法名
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(LoggerOperator.class).description();
                    break;
                }
            }
        }
        return description;
    }

}

下面这个是ip获取工具类:

/**
 * 获取用户真实的ip地址
 */
public class IpUtil {
     public static String getIpAdrress(HttpServletRequest request) {
         String ip = null;

         //X-Forwarded-For:Squid 服务代理
         String ipAddresses = request.getHeader("X-Forwarded-For");
         String unknown = "unknown";
         if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
             //Proxy-Client-IP:apache 服务代理
             ipAddresses = request.getHeader("Proxy-Client-IP");
         }

         if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
             //WL-Proxy-Client-IP:weblogic 服务代理
             ipAddresses = request.getHeader("WL-Proxy-Client-IP");
         }

         if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
             //HTTP_CLIENT_IP:有些代理服务器
             ipAddresses = request.getHeader("HTTP_CLIENT_IP");
         }

         if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
             //X-Real-IP:nginx服务代理
             ipAddresses = request.getHeader("X-Real-IP");
         }

         //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
         if (ipAddresses != null && ipAddresses.length() != 0) {
             ip = ipAddresses.split(",")[0];
         }

         //还是不能获取到,最后再通过request.getRemoteAddr();获取
         if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
             ip = request.getRemoteAddr();
         }
         return ip;
   }
}
 

然后在接口上加上注解就表示在接口执行前执行这个切面了:

是不是很方便了?只在接口上加上注解就可以了,不需要每个接口都写一遍日志入库逻辑了。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值