SpringBoot整合`AOP`实现日志功能

SpringBoot整合AOP实现日志功能

1.引入依赖


<!--aop-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!--json-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.71</version>
</dependency>

2.创建自定义的工具类

标题2.1BusinessType枚举类


package com.yurui.manage_system_springboot.utils;

/**
 * @Author YurUi
 * @Version 1.0
 */
/**
 * 业务操作类型
 */
public enum BusinessType {
    /**
     * 其它
     */
    OTHER,

    /**
     * 查询
     */
    SELECT,

    /**
     * 新增
     */
    INSERT,

    /**
     * 修改
     */
    UPDATE,

    /**
     * 删除
     */
    DELETE,

    /**
     * 授权
     */
    ASSGIN,

    /**
     * 导出
     */
    EXPORT,

    /**
     * 导入
     */
    IMPORT,

    /**
     * 强退
     */
    FORCE,

    /**
     * 更新状态
     */
    STATUS,

    /**
     * 清空数据
     */
    CLEAN,
}


2.2`OperatorType`枚举类
package com.yurui.manage_system_springboot.utils;

/**
 * 操作人类别
 */
public enum OperatorType {
    /**
     * 其它
     */
    OTHER,

    /**
     * 后台用户
     */
    MANAGE,

    /**
     * 手机端用户
     */
    MOBILE
}

2.3获取IP地址工具类


package com.yurui.manage_system_springboot.utils;

import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 获取ip地址
 */
public class IpUtil {

    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return ipAddress;
    }

    public static String getGatwayIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }
}

3.自定义注解【重点】


package com.yurui.manage_system_springboot.utils;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * @Author YurUi
 * @Version 1.0
 */
/**
 * 自定义操作日志记录注解
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;
}


4.创建代理类【重点】LogAspect


package com.yurui.manage_system_springboot.log;


import java.util.Collection;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.yurui.manage_system_springboot.pojo.SysOperLog;
import com.yurui.manage_system_springboot.pojo.User;
import com.yurui.manage_system_springboot.service.SysDeptService;
import com.yurui.manage_system_springboot.service.SysOperLogService;
import com.yurui.manage_system_springboot.utils.IpUtil;
import com.yurui.manage_system_springboot.utils.Log;
import com.yurui.manage_system_springboot.utils.TokenUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

/**
 * @Author YurUi
 * @Version 1.0
 */

/**
 * 操作日志记录处理
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private SysOperLogService operLogService;
    @Autowired
    private SysDeptService deptService;


    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = sra.getRequest();

            // *========数据库日志=========*//
            SysOperLog operLog = new SysOperLog();
            // 设置状态
            operLog.setStatus(1);
            // 请求的地址
            String ip = IpUtil.getIpAddress(request);//IpUtil.getIpAddr(ServletUtils.getRequest());
            operLog.setOperIp(ip);
            operLog.setOperUrl(request.getRequestURI());

//            String token = request.getHeader("token");

            User user = TokenUtils.getUserByJwt();
            String username = user.getUsername();
            operLog.setCreatedUser(username);
            operLog.setCreatedTime(new Date());
            // 获取部门名称
            String name = deptService.getById(user.getDeptId()).getName();
            operLog.setDeptName(name);

            operLog.setOperName(username);

            if (e != null) {
                operLog.setStatus(0);
                operLog.setErrorMsg(e.getMessage());
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operLog.setRequestMethod(request.getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);

            System.err.println(operLog);

            // 保存数据库
            operLogService.saveSysLog(operLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log     日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception {
        // 设置action动作
        operLog.setBusinessType(log.businessType().name());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().name());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog);
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && !StringUtils.isEmpty(jsonResult)) {
            // 由于返回数据过大,先不保存
//            operLog.setJsonResult(JSON.toJSONString(jsonResult));
        }
    }

    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(params);
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!StringUtils.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;
    }
}

5.控制器

5.1接口

package com.yurui.manage_system_springboot.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yurui.manage_system_springboot.pojo.SysOperLog;
import com.yurui.manage_system_springboot.pojo.vo.SysOperLogVo;
import com.yurui.manage_system_springboot.service.SysOperLogService;
import com.yurui.manage_system_springboot.utils.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @Author YurUi
 * @Version 1.0
 */
@Api(tags = "系统日志接口")
@RestController
@RequestMapping("/admin/system/log")
public class SysOperLogController {
    @Autowired
    private SysOperLogService operLogService;


    @ApiOperation("分页查询")
    @GetMapping("/page/{pageNum}/{pageSize}")
    public JsonResult page(
            @ApiParam(value = "当前页",name = "pageNum",required = true)
            @PathVariable("pageNum")Integer pageNum,
            @ApiParam(value = "每页显示大小",name = "pageSize",required = true)
            @PathVariable("pageSize")Integer pageSize,
            SysOperLogVo vo
    ){
        Page<SysOperLog> p = new Page<>(pageNum,pageSize);

        // 调用方法
        IPage<SysOperLog> page = operLogService.selectPageVo(p,vo);

        // 返回数据
        return JsonResult.ok(page).message("分页查询数据成功!");
    }

    // 根据id删除信息
    @ApiOperation("根据id删除日志信息")
    @DeleteMapping("/delete/{id}")
    public JsonResult delete(
            @ApiParam(value = "日志id",name = "id",required = true)
            @PathVariable("id")Integer id
    ){
        operLogService.deleteById(id);

        return JsonResult.ok().message("根据id删除日志信息成功!");
    }

    @ApiOperation("根据id批量删除")
    @DeleteMapping("/deletes")
    public JsonResult deletes(
            @ApiParam(value = "批量删除ids",name = "ids",required = true)
            @RequestBody List<Integer> ids
    ){
        operLogService.deleteBatchByIds(ids);

        return JsonResult.ok().message("批量删除成功!");
    }
}

6.添加日志的业务层

6.1业务层接口


package com.yurui.manage_system_springboot.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yurui.manage_system_springboot.pojo.SysOperLog;
import com.yurui.manage_system_springboot.pojo.vo.SysOperLogVo;

import java.util.List;

/**
 * @Author YurUi
 * @Version 1.0
 */
public interface SysOperLogService extends IService<SysOperLog> {

    // 保存数据
    void saveSysLog(SysOperLog operLog);

    // 分页查询
    IPage<SysOperLog> selectPageVo(Page<SysOperLog> p, SysOperLogVo vo);

    // 根据id删除日志信息
    void deleteById(Integer id);


    // 根据id批量删除
    void deleteBatchByIds(List<Integer> ids);

}



5.2业务接口实现类


package com.yurui.manage_system_springboot.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yurui.manage_system_springboot.exception.customexception.DeleteByIdException;
import com.yurui.manage_system_springboot.exception.customexception.FindAllException;
import com.yurui.manage_system_springboot.exception.customexception.SaveException;
import com.yurui.manage_system_springboot.mapper.SysOperLogMapper;
import com.yurui.manage_system_springboot.pojo.SysOperLog;
import com.yurui.manage_system_springboot.pojo.vo.SysOperLogVo;
import com.yurui.manage_system_springboot.service.SysOperLogService;
import com.yurui.manage_system_springboot.utils.ResultCodeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author YurUi
 * @Version 1.0
 */
@Service
public class SysOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog> implements SysOperLogService {
    @Autowired
    private SysOperLogMapper operLogMapper;

    // 保存数据
    @Override
    public void saveSysLog(SysOperLog operLog) {
        System.err.println(operLog);

        int result = operLogMapper.saveSysLog(operLog);

        if (result==0){
            throw new SaveException(ResultCodeEnum.SAVE_ERROR.getCode(), ResultCodeEnum.SAVE_ERROR.getMessage());
        }
    }

    // 分页查询
    @Override
    public IPage<SysOperLog> selectPageVo(Page<SysOperLog> p, SysOperLogVo vo) {

        IPage<SysOperLog> page = operLogMapper.selectPageVO(p,vo);

        if (page.getRecords().isEmpty()){
            throw new FindAllException(ResultCodeEnum.FIND_ALL_ERROR.getCode(), ResultCodeEnum.FIND_ALL_ERROR.getMessage());
        }

        return page;
    }

    @Override
    public void deleteById(Integer id) {

        int result = operLogMapper.removeById(id);

        if (result==0){
            throw new DeleteByIdException(ResultCodeEnum.DELETE_BY_ID_ERROR.getCode(), ResultCodeEnum.DELETE_BY_ID_ERROR.getMessage());
        }
    }

    @Override
    public void deleteBatchByIds(List<Integer> ids) {

        int result = operLogMapper.removeBatchByIds(ids);

        if (result==0){
            throw new DeleteByIdException(ResultCodeEnum.DELETE_BY_ID_ERROR.getCode(), ResultCodeEnum.DELETE_BY_ID_ERROR.getMessage());
        }
    }
}

7.Mapper

7.1Mapper接口


package com.yurui.manage_system_springboot.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yurui.manage_system_springboot.pojo.SysOperLog;
import com.yurui.manage_system_springboot.pojo.vo.SysOperLogVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @Author YurUi
 * @Version 1.0
 */
@Mapper
public interface SysOperLogMapper extends BaseMapper<SysOperLog> {

    // 保存数据
    int saveSysLog(SysOperLog operLog);

    // 分页查询
    IPage<SysOperLog> selectPageVO(@Param("page") Page<SysOperLog> p,@Param("vo") SysOperLogVo vo);

    // 根据id删除
    int removeById(@Param("id") Integer id);

    // 根据id批量删除
    int removeBatchByIds(List<Integer> ids);
}




7.2xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yurui.manage_system_springboot.mapper.SysOperLogMapper">

    <resultMap id="SysOperLogMap" type="sysOperLog">
        <id property="id" column="id"/>
        <result property="businessType" column="business_type"/>
        <result property="requestMethod" column="request_method"/>
        <result property="operatorType" column="operator_type"/>
        <result property="operName" column="oper_name"/>
        <result property="deptName" column="dept_name"/>
        <result property="operUrl" column="oper_url"/>
        <result property="operIp" column="oper_ip"/>
        <result property="operParam" column="oper_param"/>
        <result property="jsonResult" column="json_result"/>
        <result property="errorMsg" column="error_msg"/>
        <result property="operTime" column="oper_time"/>
        <result property="isDelete" column="is_delete"/>
        <result property="createdUser" column="created_user"/>
        <result property="createdTime" column="created_time"/>
        <result property="modifiedUser" column="modified_user"/>
        <result property="modifiedTime" column="modified_time"/>
    </resultMap>


    <!--    selectPageVO分页查询  -->
    <select id="selectPageVO" resultMap="SysOperLogMap">
        select * from sys_oper_log
        <where>
            <if test="vo.title!=null and vo.title!=''">
                `title` like "%"#{vo.title}"%"
            </if>
            <if test="vo.businessType!=null and vo.businessType!=''">
                 and `business_type` like "%"#{vo.businessType}"%"
            </if>
            <if test="vo.requestMethod!=null and vo.requestMethod!=''">
                and `request_method` like "%"#{vo.requestMethod}"%"
            </if>
            <if test="vo.deptName!=null and vo.deptName!=''">
                and `dept_name` like "%"#{vo.deptName}"%"
            </if>
            and is_delete = 0
        </where>
        order by  id desc
    </select>

    <!--    saveSysLog 保存数据  -->
    <insert id="saveSysLog" keyProperty="id" useGeneratedKeys="true">
        insert into sys_oper_log(
                                 `title`,`business_type`,`method`,`request_method`,`operator_type`,`oper_name`,`dept_name`,
                                 `oper_url`,`oper_ip`,`oper_param`,`json_result`,`status`,`error_msg`,`oper_time`,
                                 `created_user`,`created_time`
        )
        values (
                    #{title},#{businessType},#{method},#{requestMethod},#{operatorType},#{operName},#{deptName},
                    #{operUrl},#{operIp},#{operParam},#{jsonResult},#{status},#{errorMsg},#{operTime},
                    #{createdUser},#{createdTime}
                )
    </insert>

    <update id="removeById">
        update sys_oper_log set is_delete = 1 where id=#{id}
    </update>

    <update id="removeBatchByIds">
        update sys_oper_log set is_delete = 1
        <where>
            id in (
                <foreach collection="ids" item="id" separator=",">
                    #{id}
                </foreach>
            )
        </where>
    </update>
</mapper>

效果
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值