java程序中AOP实现日志功能优化

java程序中AOP实现日志功能优化 在这里插入图片描述
今天我们通过切面来实现日志记录。

第一点:概念
要理解切面编程,就需要先理解什么是切面。

AOP = Aspect Oriental Programing,即面向切面编程。什么概念,我们看如下的图片:

三个方法中,重复使用了代码A和代码B,典型的场景比如“开启事务,数据处理,提交事务”。这些重复的代码大多是所谓的权限管理、日志登陆、事务管理等必需却又污染了业务逻辑代码的内容,我们自然希望将它们提取出来,还业务逻辑一个清新的世界。
你知道Servlet过滤器,可我们目前对象的粒度已经不是整个方法了,而是更加细化到了方法中的代码片段。你当然可以曲线救国地使用匿名内部类来抽取重复代码,但是它并不怎么优雅,而AOP,就可以通过横向切割的方式来抽取代码,达到我们的目的。

spring中的几个概念
在这里插入图片描述

#Spring#

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

Target(目标对象):织入 Advice 的目标对象.。

Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

首先我们知道, 在 Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point, 通过 point cut, 我们就可以确定哪些 Joint point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, Joint point 就相当于 爪哇的小县城里的百姓,pointcut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问。

源码如下:


package com.itheima.xiaotuxian.aspect;

import java.lang.annotation.Annotation;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSON;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
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 lombok.extern.slf4j.Slf4j;

/**
* 日志切面类
* 参考https://blog.csdn.net/qq_41611676/article/details/105943836
*/
@Aspect
@Component
@Slf4j
public class LogAspect {

   /**
    * ..表示包及子包 该方法代表controller层的所有方法
    * 路径需要根据自己项目定义
    */
   @Pointcut("execution(public * com.itheima.xiaotuxian.controller..*.*(..))" +
           "|| execution(public * com.itheima.xiaotuxian.exception..*.*(..))")
   public void controllerMethod() {
   }

   /**
    * 方法环绕执行
    *
    * @param joinPoint
    * @throws Exception
    */
   @Around("controllerMethod()")
   public Object handle(ProceedingJoinPoint joinPoint) {
       HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
               .getRequest();
       // IP地址
       String ipAddr = getRemoteHost(request);
       String url = request.getRequestURL().toString();
       String reqParam = preHandle(joinPoint, request);
       log.info("请求源IP:【{}】,请求URL:【{}】,请求参数:【{}】", ipAddr, url, reqParam);

       Object result = null;
       try {
           result = joinPoint.proceed();
       } catch (Throwable e) {
           e.printStackTrace();
           log.error("请求源IP:【{}】,请求URL:【{}】,报错信息:【{}】", ipAddr, url, e.getMessage());
       }
       String respParam = postHandle(result);
       log.info("请求源IP:【{}】,请求URL:【{}】,返回参数:【{}】", ipAddr, url, respParam);
       return result;
   }

   /**
    * 返回数据
    *
    * @param retVal
    * @return
    */
   private String postHandle(Object retVal) {
       if (null == retVal) {
           return "";
       }
       return JSON.toJSONString(retVal);
   }

   /**
    * 获取目标主机的ip
    *
    * @param request
    * @return
    */
   private String getRemoteHost(HttpServletRequest request) {
       String ip = request.getHeader("x-forwarded-for");
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
           ip = request.getHeader("Proxy-Client-IP");
       }
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
           ip = request.getHeader("WL-Proxy-Client-IP");
       }
       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
           ip = request.getRemoteAddr();
       }
       return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
   }

   private String preHandle(ProceedingJoinPoint joinPoint, HttpServletRequest request) {

       StringBuilder reqParam = new StringBuilder();
       Signature signature = joinPoint.getSignature();
       MethodSignature methodSignature = (MethodSignature) signature;
       java.lang.reflect.Method targetMethod = methodSignature.getMethod();

       Object[] args = joinPoint.getArgs();

       Annotation[][] parameterAnnotations = targetMethod.getParameterAnnotations();
       int i = 0;
       for (Annotation[] annotations : parameterAnnotations) {
           for1: for (Annotation annotation : annotations) {
               if (annotation instanceof RequestParam) {
                   reqParam.append(((RequestParam) annotation).name()).append(":").append(JSON.toJSONString(args[i]))
                           .append(",");
                   i++;
                   break for1;
               } else if (annotation instanceof PathVariable) {
                   reqParam.append(JSON.toJSONString(args[i])).append(",");
                   i++;
                   break for1;
               } else if (annotation instanceof RequestBody) {
                   reqParam.append(JSON.toJSONString(args[i])).append(",");
                   i++;
                   break for1;
               }
           }
       }
       return reqParam.toString();
   }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小途马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值