当前位置:   article > 正文

spring AOP+自定义注解,实现记录接口调用日志_调用外部服务怎么加自定义注解日志

调用外部服务怎么加自定义注解日志

spring AOP+自定义注解,实现记录接口调用日志


前言

提示:随意记录


提示:以下是本篇文章正文内容,下面案例可供参考

一、自定义注解

注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用的关键字有所不同@interface。在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。
代码如下(示例):

@Target(ElementType.METHOD)//@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义
@Retention(RetentionPolicy.RUNTIME)//@Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。
//注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。同样使用了
public @interface InterfaceLog {
    String value() default "";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

二、使用aop记录日志

1.切入点方法类,记录日志存入数据库

代码如下(示例):

import com.alibaba.fastjson.JSONObject;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.ServletActionContext;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


@Aspect
@Component
public class InterfaceLogAspect {

    //private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    InterfaceCallLogServiceImpl interfaceCallLogService;

    @Pointcut("@annotation(com.XXXX.XXXXX.common.InterfaceLog)")
    private void pointCut() {
    }

    /**
     * 环绕通知
     *
     * @param joinPoint 连接点
     * @return 切入点返回值
     * @throws Throwable 异常信息
     */
    @Around(value = "pointCut() && @annotation(around)") //around 与 下面参数名around对应
    public Object apiLog(ProceedingJoinPoint joinPoint, InterfaceLog around) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        ActionContext context = ActionContext.getContext();
        HttpServletRequest request = (HttpServletRequest) context.get(ServletActionContext.HTTP_REQUEST);
        String userAgent = request.getHeader("user-agent");
        String ip = IpUtil.getIp(request);
        String methodName = this.getMethodName(joinPoint);
        String params = LogAspectUtil.getMethodParams(joinPoint);
        MethodSignature msig = (MethodSignature) joinPoint.getSignature();
        Method pointMethod = joinPoint.getTarget().getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodPath = String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), pointMethod.getName());
        System.out.println("开始请求方法:[{"+methodName+"}] 服务:[{"+methodPath+"}] 参数:[{"+params+"}] IP:[{"+ip+"}] userAgent [{"+userAgent+"}]");
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        String outResult = LogAspectUtil.deleteSensitiveContent(result);
        System.out.println("结束请求方法:[{"+methodName+"}] 参数:[{"+params+"}] 返回结果[{"+outResult+"}] 耗时:[{"+(end - start)+"}]毫秒 ");
/**
        InterfaceCallLog interfaceCallLog = new InterfaceCallLog();
        interfaceCallLog.setIlInterfaceName(methodName);
        JSONObject jsonObject = JSONObject.parseObject(params);
        interfaceCallLog.setIlCaller(jsonObject.get("caller")!=null?jsonObject.get("caller").toString():"");
        interfaceCallLog.setIlCallerIp(ip);
        interfaceCallLog.setIlInParameter(params);
        interfaceCallLog.setIlOutParameter(outResult);
        interfaceCallLogService.save(interfaceCallLog); */
        return result;
    }

    private String getMethodName(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        String shortMethodNameSuffix = "(..)";
        if (methodName.endsWith(shortMethodNameSuffix)) {
            methodName = methodName.substring(0, methodName.length() - shortMethodNameSuffix.length());
        }
        return methodName;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

2.获取访问ip地址工具类

代码如下(示例):

import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;

/**
 * @description:
 * @projectName:sydj0112
 * @see:com.XXX.XXX.util
 * @author:gongsi
 * @createTime:2021/4/26 9:06
 * @version:1.0
 */
public class IpUtil {
    public static String getIp(HttpServletRequest request){
        String ip = null;
        //X-Forwarded-For:Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");

        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();
        }

        // ip配置
        if (ip.equals("127.0.0.1") || ip.endsWith("0:0:0:0:0:0:1")) {
            // 根据网卡取本机配置的IP
            ip = "127.0.0.1";
        }
        return ip;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

2.参数脱敏

代码如下(示例):

import net.sf.json.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.web.multipart.MultipartFile;
import org.testng.v6.Lists;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *  AOP记录日志的一些共用方法
 * @author LErry.li
 * Date: 2018-06-17
 * Time: 15:19
 */
public class LogAspectUtil {

    private LogAspectUtil(){

    }

    /**
     * 获取需要记录日志方法的参数,敏感参数用*代替
     * @param joinPoint 切点
     * @return 去除敏感参数后的Json字符串
     */
    public static String getMethodParams(ProceedingJoinPoint joinPoint){
        Object[] arguments = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        if(arguments ==null || arguments.length <= 0){
            return sb.toString();
        }
        for (Object arg : arguments) {
            //移除敏感内容
            String paramStr;
            if (arg instanceof HttpServletResponse) {
                paramStr = HttpServletResponse.class.getSimpleName();
            } else if (arg instanceof HttpServletRequest) {
                paramStr = HttpServletRequest.class.getSimpleName();
            } else if (arg instanceof MultipartFile) {
                long size = ((MultipartFile) arg).getSize();
                paramStr = MultipartFile.class.getSimpleName() + " size:" + size;
            } else {
                paramStr = deleteSensitiveContent(arg);
            }
            sb.append(paramStr).append(",");
        }
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    /**
     * 删除参数中的敏感内容
     * @param obj 参数对象
     * @return 去除敏感内容后的参数对象
     */
    public static String deleteSensitiveContent(Object obj) {
        JSONObject jsonObject = new JSONObject();
        if (obj == null || obj instanceof Exception) {
            return jsonObject.toString();
        }
        List<String> sensitiveFieldList = getSensitiveFieldList();
        if(obj instanceof Map<?,?>||obj instanceof Vector<?>||obj instanceof List<?>||obj instanceof Set<?>){
            try {
                jsonObject = JSONObject.fromObject(obj);
            }catch (Exception e) {
                return String.valueOf(obj);
            }
        }else{
            Class cls = obj.getClass();
            Field[] fields = cls.getDeclaredFields();
            for(int i=0; i<fields.length; i++){
                Field f = fields[i];
                f.setAccessible(true);
                try {
                    if(f.get(obj)!=null){
                        jsonObject.put(f.getName(),f.get(obj));
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        for (String sensitiveField : sensitiveFieldList) {
            if (jsonObject.containsKey(sensitiveField)) {
                String value=jsonObject.get(sensitiveField).toString();
                value = desensitization(value);
                jsonObject.put(sensitiveField,value);
            }
        }
        return jsonObject.toString();
    }


    /**
     * 敏感字段列表(当然这里你可以更改为可配置的)
     */
    private static List<String> getSensitiveFieldList() {
        List<String> sensitiveFieldList = Lists.newArrayList();
        sensitiveFieldList.add("pwd");
        sensitiveFieldList.add("password");
        return sensitiveFieldList;
    }
    /**
     * 敏感字段列表(当然这里你可以更改为可配置的)
     */
    private static String desensitization(String value) {
        if (value.length() < 1) {
            value = "****";
        } else if (value.length() == 1) {
            value = "*" + value;
        } else if (value.length() == 2) {
            value = "*" + value.substring(1);
        } else if (value.length() == 3) {
            value = "**" + value.substring(2);
        } else if(value.length() > 3 && value.length() < 7) {
            value = value.substring(0,2) + "****" + value.substring(3);
        }else{
            value = value.substring(0,2) + "****" + value.substring(value.length()-4);
        }
        return value;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128

注意

注意:1.使用spring boot时aop注解@Component会自动生效。
2.使用springMvc 时,aop无效;

查询发现Spring与SpringMVC是2个不同的父子容器, @Aspect如果被spring容器加载的话,而@Controller注解的这些类的实例化以及注入却是由SpringMVC来完成。 @Aspect如果被spring容器加载的时候,可能Spring MVC容器还未初始化, Controller类还未初始化,所以无法正常织入。

解决:把 aop:aspectj-autoproxy 从applicationContext.xml移入springmvc配置文件中,并定义bean,如下:在这里插入图片描述
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                         http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.3.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd
                         http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

                        ">

<!-- 这个配置一定要配置在component-scan以后 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
    <bean id="paramValidAspect" class="com.gs.log.LogAspect"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

总结

注意:做一次记录
create table INTERFACE_CALL_LOG
(
il_cdate DATE,
il_udate DATE,
il_status VARCHAR2(8),
il_ord NUMBER,
il_interface_name VARCHAR2(800),
il_caller_ip VARCHAR2(48),
il_caller VARCHAR2(200),
il_in_parameter CLOB,
il_out_parameter CLOB
)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/615656
推荐阅读
相关标签
  

闽ICP备14008679号