Spring AOP结合注解实现接口层操作日志记录

目录
  • 1.表和实体设计
    • 1.实体设计
    • 2.表结构设计
  • 2.日志注解
  • 3.核心AOP类
    • 4.用到的工具类
    • 5.测试类
    • 6.测试结果

1.表和实体设计

1.实体设计

实体基类

@Data
//映射将仅应用于其子类
@MappedSuperclass
//指定要用于实体或映射超类的回调侦听器类。此注释可以应用于实体类或映射的超类。
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
    /**
     * @Id 注解标识
     * @GeneratedValue(generator = "snowflakeIdIDGenerator") 指定生成策略
     * @GenericGenerator(name = "snowflakeIdIDGenerator", strategy = "net.cqnews.base.idconfig.SnowflakeIdGenerator")
     * name 唯一的生成器名称
     * strategy 生成器策略可以是预定义的 Hibernate 策略或完全限定的类名。
     * parameters 可选的生成器参数。
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(columnDefinition = "bigint(20) COMMENT '主键Id'")
    private Long id;
    @CreatedBy
    @Column(columnDefinition = "bigint(20) COMMENT '创建人Id'")
    private Long createId;
    @CreatedDate
    @Column(columnDefinition = "datetime COMMENT '创建时间'")
    private Date createTime;
    @LastModifiedBy
    @Column(columnDefinition = "bigint(20) COMMENT '修改人Id'")
    private Long updateId;
    @LastModifiedDate
    @Column(columnDefinition = "datetime COMMENT '修改时间'")
    private Date updateTime;
    /**
     * 是否删除
     */
    @Column(columnDefinition = "tinyint default 0 COMMENT '是否删除,默认否'")
    private Boolean deleted = Boolean.FALSE;
}

操作日志表实体

@Entity
@Data
//指定表名
@Table(name = "sys_oper_log")
//表名注释
@org.hibernate.annotations.Table(appliesTo = "sys_oper_log", comment = "系统操作日志表")
public class SysOperLog extends BaseEntity {
    @Column(columnDefinition = "varchar(50) COMMENT '模块标题'")
    private String title;
    @Column(columnDefinition = "varchar(255) COMMENT '接口功能描述'")
    private String description;
    @Column(columnDefinition = "varchar(100) COMMENT '方法名称'")
    private String method;
    @Column(columnDefinition = "varchar(10) COMMENT '请求方式'")
    private String requestMethod;
    @Column(columnDefinition = "varchar(50) COMMENT '操作人员名称'")
    private String operatorName;
    @Column(columnDefinition = "varchar(255) COMMENT '请求URL'")
    private String requestUrl;
    @Column(columnDefinition = "varchar(255) COMMENT '主机地址'")
    private String requestIp;
    @Column(columnDefinition = "varchar(255) COMMENT '操作地点'")
    private String operationLocation;
    @Column(columnDefinition = "varchar(2000) COMMENT '请求参数'")
    private String operatorParam;
    @Column(columnDefinition = "varchar(2000) COMMENT '返回参数'")
    private String jsonResult;
    @Enumerated(EnumType.STRING)
    @Column(columnDefinition = "varchar(20) COMMENT '操作状态'")
    private StatusEnum status;
    @Column(columnDefinition = "varchar(2000) COMMENT '错误描述'")
    private String errorMsg;
}

状态枚举类

@AllArgsConstructor
@Getter
public enum StatusEnum {
    SUCCESS,
    FAIL
}

2.表结构设计

CREATE TABLE `sys_oper_log` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `create_id` bigint DEFAULT NULL COMMENT '创建人Id',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `deleted` tinyint DEFAULT '0' COMMENT '是否删除,默认否',
  `update_id` bigint DEFAULT NULL COMMENT '修改人Id',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `description` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '接口功能描述',
  `error_msg` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '错误描述',
  `json_result` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '返回参数',
  `method` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '方法名称',
  `operation_location` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作地点',
  `operator_name` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作人员名称',
  `operator_param` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求参数',
  `request_ip` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主机地址',
  `request_method` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求方式',
  `request_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求URL',
  `status` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作状态',
  `title` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '模块标题',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统操作日志表';

2.日志注解

参考下面这段swagger接口文档属性的方式,在controller层日志记录主要包含:接口所属模块、接口功能描述、请求参数、响应参数等

当然请求参数和响应参数不是每个接口都会包含的,因此这里注解设计了isSaveRequestData是否保存请求参数、isSaveResponseData是否保存响应的参数两个字段便于灵活控制,当然我们在做日志记录处理时也需要考虑为空的情况。

@Log(title = "测试模块",description = "更新配置")
@ApiOperation(value = "更新配置")
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public BaseResult update(@Validated @RequestBody AuthConfigUpdateRequest request) {
    return BaseResult.judgeOperate(authConfigService.update(request));
}
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 接口所属模块
     */
    public String title() default "";
    /**
     * 接口功能描述
     */
    public String description() default "";
    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;
    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;
}

3.核心AOP类

我们设计的@Log注解主要用于修饰controller层,MyLogAspect主要就是对被@Log修饰的目标方法做增强处理,包含处理完请求后执行:记录目标方法正在执行的日志、处理请求异常后执行:记录目标方法执行错误的日志

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class MyLogAspect {
    private final SysOperLogService sysOperLogService;
    /**
     * 处理完请求后执行
     *
     * @param joinPoint     切入点对象
     * @param controllerLog @Log注解对象
     * @param jsonResult    返回值对象
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, jsonResult, null);
    }
    /**
     * 处理请求异常后执行
     *
     * @param joinPoint
     * @param controllerLog
     * @param e
     */
    @AfterThrowing(pointcut = "@annotation(controllerLog)", throwing = "e")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Throwable e) {
        handleLog(joinPoint, controllerLog, null, e);
    }
    /**
     * 记录日志
     *
     * @param joinPoint
     * @param controllerLog
     * @param e
     * @param jsonResult
     */
    private void handleLog(JoinPoint joinPoint, Log controllerLog, Object jsonResult, Throwable e) {
        try {
            //获取HttpServletRequest对象
            HttpServletRequest request = getRequest();
            //获取ip地址
            String requestIp = IpUtils.getIpAddr(request);
            //获取请求地址
            String requestUrl = request.getRequestURI();
            //获取类全名
            String classFullName = joinPoint.getTarget().getClass().getName();
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //方法名称
            String methodName = methodSignature.getMethod().getName();
            //POST、GET、PUT、DELETE
            String requestMethod = request.getMethod();
            String operationLocation = AddressUtils.getRealAddressByIP(requestIp);
            //保存日志
            SysOperLog sysOperLog = new SysOperLog();
            sysOperLog.setStatus(StatusEnum.SUCCESS);
            sysOperLog.setOperationLocation(operationLocation);
            sysOperLog.setRequestIp(requestIp);
            sysOperLog.setRequestUrl(requestUrl);
            sysOperLog.setMethod(classFullName + "." + methodName);
            sysOperLog.setRequestMethod(requestMethod);
            //获取注解中对方法的描述信息 用于Controller层注解 请求参数、响应参数设置
            getControllerMethodDescription(joinPoint, controllerLog, sysOperLog, jsonResult);
            if (e != null) {
                //操作状态(0正常 1异常)
                sysOperLog.setStatus(StatusEnum.FAIL);
                sysOperLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            sysOperLogService.saveOperatorLog(sysOperLog);
        } catch (Exception err) {
            if (e != null) {
                log.error("方法执行后异常通知,执行错误:{}", err.getMessage());
            } else {
                log.error("方法执行成功返回通知,执行错误:{}", err.getMessage());
            }
        }
    }
    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * 请求参数、响应参数设置
     *
     * @param joinPoint
     * @param controllerLog
     * @param sysOperLog
     * @param jsonResult
     */
    private void getControllerMethodDescription(JoinPoint joinPoint, Log controllerLog, SysOperLog sysOperLog, Object jsonResult) {
        //接口所属模块
        String title = controllerLog.title();
        //接口功能描述
        String description = controllerLog.description();
        sysOperLog.setDescription(description);
        sysOperLog.setTitle(title);
        //判断是否需要保存请求参数
        if (controllerLog.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, sysOperLog);
        }
        //判断是否需要保存响应数据
        if (controllerLog.isSaveResponseData() && !ObjectUtils.isEmpty(jsonResult)) {
            sysOperLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
        }
    }
    /**
     * 保存请求参数
     *
     * @param joinPoint
     * @param sysOperLog
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog sysOperLog) {
        //获取请求参数
        String requestMethod = sysOperLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            sysOperLog.setOperatorParam(StringUtils.substring(params, 0, 2000));
        } else if (HttpMethod.GET.name().equals(requestMethod)) {
            Map<?, ?> paramsMap = (Map<?, ?>) getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            log.info("paramsMap:{}", paramsMap);
            Map<String, String[]> parameterMap = getRequest().getParameterMap();
            sysOperLog.setOperatorParam(StringUtils.substring(JSON.toJSONString(parameterMap), 0, 2000));
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            log.info("paramsMap:{}", paramsMap);
            sysOperLog.setOperatorParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }

    }
    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!ObjectUtils.isEmpty(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }
    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
    /**
     * 获取request对象
     *
     * @return
     */
    private HttpServletRequest getRequest() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        return request;
    }
}

4.用到的工具类

获取ip地址

/**
 * 获取IP方法
 *
 */
public class IpUtils {
    /**
     * 获取客户端IP
     *
     * @param request 请求对象
     * @return IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        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("X-Forwarded-For");
        }
        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.getHeader("X-Real-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" : getMultistageReverseProxyIp(ip);
    }
    /**
     * 检查是否为内部IP地址
     *
     * @param ip IP地址
     * @return 结果
     */
    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }
    /**
     * 检查是否为内部IP地址
     *
     * @param addr byte地址
     * @return 结果
     */
    private static boolean internalIp(byte[] addr) {
        if (addr == null || addr.length < 2) {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }
    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text) {
        if (text.length() == 0) {
            return null;
        }
        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try {
            long l;
            int i;
            switch (elements.length) {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L)) {
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L)) {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException e) {
            return null;
        }
        return bytes;
    }
    /**
     * 获取IP地址
     *
     * @return 本地IP地址
     */
    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
        }
        return "127.0.0.1";
    }
    /**
     * 获取主机名
     *
     * @return 本地主机名
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
        }
        return "未知";
    }
    /**
     * 从多级反向代理中获得第一个非unknown IP地址
     *
     * @param ip 获得的IP地址
     * @return 第一个非unknown IP地址
     */
    public static String getMultistageReverseProxyIp(String ip) {
        // 多级反向代理检测
        if (ip != null && ip.indexOf(",") > 0) {
            final String[] ips = ip.trim().split(",");
            for (String subIp : ips) {
                if (false == isUnknown(subIp)) {
                    ip = subIp;
                    break;
                }
            }
        }
        return ip;
    }
    /**
     * 检测给定字符串是否为未知,多用于检测HTTP请求相关
     *
     * @param checkString 被检测的字符串
     * @return 是否未知
     */
    public static boolean isUnknown(String checkString) {
        return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
    }
    public static void main(String[] args) {
        String hostIp = IpUtils.getHostIp();
        System.out.println(hostIp);
    }
}

根据ip地址获取位置信息

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
/**
 * 根据ip获取地理位置信息
 */
public class AddressUtils {
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
    // IP地址查询,太平洋ip地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    // 未知地址
    public static final String UNKNOWN = "XX XX";
    /**
     * UTF-8 字符集
     */
    public static final String UTF8 = "UTF-8";
    /**
     * GBK 字符集
     */
    public static final String GBK = "GBK";
    public static String getRealAddressByIP(String ip) {
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        try {
            String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true", GBK);
            if (StringUtils.isNotBlank(rspStr)) {
                log.error("获取地理位置异常 {}", ip);
                return UNKNOWN;
            }
            JSONObject obj = JSON.parseObject(rspStr);
            String region = obj.getString("pro");
            String city = obj.getString("city");
            return String.format("%s %s", region, city);
        } catch (Exception e) {
            log.error("获取地理位置异常 {}", ip);
        }
        return UNKNOWN;
    }
    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url         发送请求的 URL
     * @param param       请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @param contentType 编码类型
     * @return 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param, String contentType) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
            log.info("sendGet - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
}

5.测试类

在需要记录日志的目标方法添加我们自定义的@Log注解,通过aop记录日志

@Api(tags = "测试模块")
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/test")
public class AuthController {
    private final AuthConfigService authConfigService;
    @Log(title = "测试模块",description = "更新配置")
    @ApiOperation(value = "更新配置")
    @RequestMapping(value = "/update", method = RequestMethod.PUT)
    public BaseResult update(@Validated @RequestBody AuthConfigUpdateRequest request) {
        return BaseResult.judgeOperate(authConfigService.update(request));
    }
    @Log(title = "测试模块",description = "新增配置")
    @ApiOperation(value = "新增配置")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public BaseResult create(@Validated @RequestBody AuthConfigRequest request) {
        return BaseResult.judgeOperate(authConfigService.create(request));
    }
    @Log(title = "测试模块",description = "删除配置")
    @ApiOperation(value = "删除配置")
    @DeleteMapping(value = "/delete/{ids}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ids", value = "配置id,多个id用逗号分隔", required = true)
    })
    public BaseResult delete(@PathVariable("ids") List<Long> ids) {
        return BaseResult.judgeOperate(authConfigService.delete(ids));
    }
    @Log(title = "测试模块",description = "配置分页")
    @ApiOperation(value = "配置分页")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public BaseResult<PageResponse<AuthConfigListResponse>> list(@RequestBody AuthConfigListRequest request) {
        return BaseResult.success(PageResponse.getInstance(authConfigService.list(request)));
    }
    @Log(title = "测试模块",description = "根据appId获取配置信息")
    @ApiOperation(value = "根据appId获取配置信息")
    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    public BaseResult<EduAuthConfig> detail(String appId) {
        return BaseResult.success(authConfigService.getAuthConfig(appId));
    }
}

6.测试结果

到此这篇关于Spring AOP结合注解实现接口层操作日志记录的文章就介绍到这了,更多相关Spring AOP接口层内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springMVC自定义注解,用AOP来实现日志记录的方法

    需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中. 为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求. 准备工作 自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号. 自定义注解 在项目下单独建立了一个log包,来存放日志相关的内容 **.common.log.annotation //自

  • SpringAop实现操作日志记录

    前言 大家好,这里是经典鸡翅,今天给大家带来一篇基于SpringAop实现的操作日志记录的解决的方案.大家可能会说,切,操作日志记录这么简单的东西,老生常谈了.不! 网上的操作日志一般就是记录操作人,操作的描述,ip等.好一点的增加了修改的数据和执行时间.那么!我这篇有什么不同呢!今天这种不仅可以记录上方所说的一切,还增加记录了操作前的数据,错误的信息,堆栈信息等.正文开始~~~~~ 思路介绍 记录操作日志的操作前数据是需要思考的重点.我们以修改场景来作为探讨.当我们要完全记录数据的流向的时候,

  • 在springboot中使用AOP进行全局日志记录

    目录 前言 1. spring AOP 是什么? 2.spring AOP 能做什么? 3.spring AOP 我能用 AOP 解决什么问题? 一.引入依赖,增加自定义注解 1.引入 maven 依赖 2.增加自定义注解 OperationLog 二.为自定义注解编写切面实现 三.使用自定义日志注解 前言 此前项目上需要对用户的操作进行日志记录,以便后续追踪问题,所以就学习了使用 spring AOP 来进行日志记录. 1. spring AOP 是什么? spring 的两大核心就是 IOC

  • 运用Spring Aop+注解实现日志记录

    目录 1. 介绍 2. 实践 2.1 定义注解 2.2 切面类 2.3 编写测试方法 2.4 运行结果 3. 总结 4. 参考文章 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到的.在日常项目中,我们也会经常使用IOC控制反转,但是却感觉AOP很少会运用到.其实AOP大有用处,甚至可以让你偷偷懒. 举一个例子,假如现在要让你记录每一个请求的请求IP,请求的方法,请求路径,请求的参数,返回参数

  • Spring Boot2集成AOPLog来记录接口访问日志

    前言 日志是一个Web项目中必不可少的部分,借助它我们可以做许多事情,比如问题排查.访问统计.监控告警等.一般通过引入slf4j的一些实现框架来做日志功能,如log4j,logback,log4j2,其性能也是依次增强.在springboot中,默认使用的框架是logback. 我们经常需要在方法开头或结尾加日志记录传入参数或返回结果,以此来复现当时的请求情况.但是手动添加日志,不仅繁琐重复,也影响代码的美观简洁.本文引入一个基于AOP实现的日志框架,并通过spring-boot-starter

  • Spring AOP实现复杂的日志记录操作(自定义注解)

    目录 Spring AOP复杂的日志记录(自定义注解) 第一步 第二步 第三步 第四步 多个注解可以合并成一个,包括自定义注解 比如说SpringMVC的注解 Spring AOP复杂的日志记录(自定义注解) 做项目中,业务逻辑要求只要对数据库数据进行改动的都需要记录日志(增删改),记录的内容有操作者.操作的表名及表名称.具体的操作,以及操作对应的数据. 首先想到的就是Spring 的AOP功能.可是经过一番了解过后,发现一般的日志记录,只能记录一些简单的操作,例如表名.表名称等记录不到. 于是

  • Spring AOP实现记录操作日志

    本文实例为大家分享了Spring AOP实现记录操作日志的具体代码,供大家参考,具体内容如下 1 添加maven依赖 <dependency>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-starter-aop</artifactId> </dependency> 2 自定义操作日志注解 @Target(ElementT

  • SpringBoot开发技巧之使用AOP记录日志示例解析

    目录 为什么要用AOP? 常用的工作场景 必须知道的概念 AOP 的相关术语 Spring 中使用注解创建切面 实战应用-利用AOP记录日志 定义日志信息封装 统一日志处理切面 为什么要用AOP? 答案是解耦! Aspect Oriented Programming 面向切面编程.解耦是程序员编码开发过程中一直追求的.AOP也是为了解耦所诞生. 具体思想是:定义一个切面,在切面的纵向定义处理方法,处理完成之后,回到横向业务流. AOP 主要是利用代理模式的技术来实现的.具体的代理实现可以参考这篇

  • SpringBoot使用AOP记录接口操作日志的方法

    目录 一.操作日志简介 1.1.系统日志和操作日志的区别 1.2.操作日志记录实现方式 二.AOP面向切面编程 2.1.AOP简介 2.2.AOP作用 2.3.AOP相关术语 2.4.JointPoint和ProceedingJoinPoint 2.5.AOP相关注解 三.AOP切面实现接口日志记录 3.1.引入AOP依赖 3.2.创建日志信息封装类WebLog 3.3.创建切面类WebLogAspect 3.4.调用接口进行测试 四.AOP切面+自定义注解实现接口日志记录 4.1.自定义日志注

  • SpringBoot使用AOP记录接口操作日志详解

    SpringBoot 使用 AOP 记录接口操作日志,供大家参考,具体内容如下 一.AOP简介 1.什么是AOP AOP:Aspect Oriented Programming 面向切面编程 AOP关注不是某一个类或某些方法:控制大量资源,关注的是大量的类和方法. 2.AOP应用场景以及常用术语 权限控制.缓存控制.事务控制.分布式追踪.异常处理等 Target:目标类,即需要被代理的类.例如:UserService Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法.例如:所有

随机推荐