后端——AOP异步日志

需求分析

在SpringBoot系统中,一般会对访问系统的请求做日志记录的需求,确保系统的安全维护以及查看接口的调用情况,可以使用AOP对controller层的接口进行增强,作日志记录。日志保存在数据库当中,为了避免影响接口的响应,降低用户体验度,采用异步的方式记录日志,避免日志记录阻塞接口请求

实现原理

通过定义AOP切面,访问接口之前,使用前置通知记录一些有用的数据,如ip地址、请求方式、操作人等等,接口执行完之后使用后置通知记录本次请求执行的实践、执行结果等等信息。

实现代码

AOP日志切面

定义切点表达式指向Controller的所有方法,即指向所有接口

java 复制代码
/**
 * @Description 日志切面类
 */
@Aspect
@Component
public class SysLogAspect {

    @Autowired
    SysLogDao sysLogDao;

    /**
     * 开始时间
     */
    private Long startTime;

    /**
     * ip
     */
    private String ip;

    /**
     * 请求接口名
     */
    private String interfaceName;

    /**
     * 请求方式
     */
    private String methodWays;

    /**
     * 请求方法路径
     */
    private String requestMethodUrl;

    /**
     * 请求类方法参数
     */
    private String requestArgs;

    //声明切点
    @Pointcut("execution(public * com.*.*.controller.*.*(..)) || execution(public * com.*.controller.*.*(..))"
    private void aspect() {
    }

    @Before("aspect()")
    public void before(JoinPoint joinPoint) throws NoSuchMethodException {
        //开始访问的时间
        startTime = System.currentTimeMillis();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求ip
        ip = request.getRemoteAddr();
        //获取请求方法名
        interfaceName = request.getRequestURI();
        //获取请求方式
        methodWays = request.getMethod();
        //获取请求方法地址
        requestMethodUrl = joinPoint.getSignature().toString();
        //获取请求参数
        requestArgs = Arrays.toString(joinPoint.getArgs());
    }

    @AfterReturning(pointcut = "aspect()")
    public void methodAfterReturning() {
        Long endTime = System.currentTimeMillis();
        //执行时长
        Double time = (endTime - startTime) / 1000.0;
        SysLog sysLog = new SysLog();
        //获取当前用户
        Subject currentUser = SecurityUtils.getSubject();
        UserLoginInfo subject = (UserLoginInfo) currentUser.getPrincipal();
        if (!ObjectUtils.isEmpty(subject)) {
            //用户名
            sysLog.setUserName(subject.getUserName());
        }
        //用户操作
        switch (methodWays) {
            case "GET":
                sysLog.setOperation(BusinessType.SELECT.getValue());
                break;
            case "POST":
                sysLog.setOperation(BusinessType.INSERT.getValue());
                break;
            case "PUT":
                sysLog.setOperation(BusinessType.UPDATE.getValue());
                break;
            case "DELETE":
                sysLog.setOperation(BusinessType.DELETE.getValue());
                break;
        }
        //请求IP
        sysLog.setIp(ip);
        //请求类方法参数
        sysLog.setParams(requestArgs);
        //执行时长
        sysLog.setTime(time.toString());
        //请求方法路径
        sysLog.setMethod(requestMethodUrl);
        //入库时间
        String createTime = DateTimeUtils.getNowDateTime();
        sysLog.setCreateTime(createTime);
        //获取系统接口路径信息列表
        List<ApiInfo> apiInfos = sysLogDao.selectApiInfos();
        for (ApiInfo apiInfo : apiInfos) {
            //调用接口路径与接口信息列表进行匹配
            if (apiInfo.getApiName().equals(interfaceName)) {
                //菜单模块
                sysLog.setMenuModel(apiInfo.getMenuOperation());
                break;
            }
        }
        //异步新增日志
        AsyncManager.me().execute(AsyncFactory.recordOper(sysLog));
    }
}

异步任务管理器(线程池)

定义一个单例的异步任务管理器,使用线程池完成异步操作

java 复制代码
/**
 * @description: 异步任务管理器
 **/
public class AsyncManager {
    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;

    /**
     * 异步操作任务调度线程池
     */
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    /**
     * 单例模式
     */
    private AsyncManager(){}

    private static AsyncManager me = new AsyncManager();

    public static AsyncManager me()
    {
        return me;
    }

    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(TimerTask task)
    {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

    /**
     * 停止任务线程池
     */
    public void shutdown()
    {
        ThreadsUtil.shutdownAndAwaitTermination(executor);
    }
}

异步工厂

这里定义异步工厂去创建日记记录的任务,同时也方便系统扩展其他的异步操作

java 复制代码
public class AsyncFactory {

    /**
     * 操作日志记录
     *
     * @param operLog 操作日志信息
     * @return 任务task
     */
    public static TimerTask recordOper(final SysLog operLog) {
        return new TimerTask() {
            @Override
            public void run() {
                // 远程查询操作地点
                SpringUtils.getBean(SysLogService.class).addSysLogInfo(operLog);
            }
        };
    }

}
相关推荐
purrrew4 分钟前
【Java ee 初阶】多线程(8)
java·java-ee
TPBoreas3 小时前
Jenkins 改完端口号启动不起来了
java·开发语言
金斗潼关3 小时前
SpringCloud GateWay网关
java·spring cloud·gateway
秋名RG4 小时前
深入解析建造者模式(Builder Pattern)——以Java实现复杂对象构建的艺术
java·开发语言·建造者模式
eternal__day4 小时前
Spring Boot 实现验证码生成与校验:从零开始构建安全登录系统
java·spring boot·后端·安全·java-ee·学习方法
陈大爷(有低保)5 小时前
swagger3融入springboot
java
weixin_376934637 小时前
JDK Version Manager (JVMS)
java·开发语言
月月大王7 小时前
easyexcel导出动态写入标题和数据
java·服务器·前端
大G哥9 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
行走__Wz9 小时前
计算机学习路线与编程语言选择(信息差)
java·开发语言·javascript·学习·编程语言选择·计算机学习路线