目录

  • 一、介绍
  • 二、流程分析
  • 2.1 异步工厂(产生任务用)
  • 2.2 异步任务管理器
  • schedule()方法:计划在将来的某个时刻执行任务
  • 三、使用方式


一、介绍

在实际工作中,记录用户的操作行为还是重要的,经常会有客户来问查一下我这个数据是什么时候改的?是谁改的?类似这样的问题。这个时候就需要吧用户所有的操作行为记录下来。可以通过注解+切面实现,也可以通过异步的定时任务来实现。本章来分析一下若依管理系统是怎么通过异步定时任务来实现日志记录的。

注解+切面实现日志记录:
若依登陆接口分析:

在登录接口的时候我们发现,代码会将登录的状态记录下来:

若依 sys_confg redis 若依管理系统_java


用户登陆记录:

若依 sys_confg redis 若依管理系统_若依 sys_confg redis_02


用户操作行为记录:

若依 sys_confg redis 若依管理系统_新星计划_03

二、流程分析

创建一个返回TimerTask的工厂类,用来生成各种的任务。

2.1 异步工厂(产生任务用)

/**
 * 异步工厂(产生任务用)
 * 
 * @author ruoyi
 */
public class AsyncFactory {
    private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");

    /**
     * 记录登录信息
     * 
     * @param username 用户名
     * @param status 状态
     * @param message 消息
     * @param args 列表
     * @return 任务task
     */
    public static TimerTask recordLogininfor(final String username, final String status, final String message, final Object... args) {
        final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        return new TimerTask() {
            @Override
            public void run() {
                String address = AddressUtils.getRealAddressByIP(ip);
                StringBuilder s = new StringBuilder();
                s.append(LogUtils.getBlock(ip));
                s.append(address);
                s.append(LogUtils.getBlock(username));
                s.append(LogUtils.getBlock(status));
                s.append(LogUtils.getBlock(message));
                // 打印信息到日志
                sys_user_logger.info(s.toString(), args);
                // 获取客户端操作系统
                String os = userAgent.getOperatingSystem().getName();
                // 获取客户端浏览器
                String browser = userAgent.getBrowser().getName();
                // 封装对象
                SysLogininfor logininfor = new SysLogininfor();
                logininfor.setUserName(username);
                logininfor.setIpaddr(ip);
                logininfor.setLoginLocation(address);
                logininfor.setBrowser(browser);
                logininfor.setOs(os);
                logininfor.setMsg(message);
                // 日志状态
                if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
                    logininfor.setStatus(Constants.SUCCESS);
                }
                else if (Constants.LOGIN_FAIL.equals(status)) {
                    logininfor.setStatus(Constants.FAIL);
                }
                // 插入数据
                SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
            }
        };
    }

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

这个类有两个方法,一个是用来记录登录操作的,一个是记录日志操作的。返回的是一个TimeTask对象,TimerTask 实现了 Runnable 接口。

2.2 异步任务管理器

创建一个异步

/**
 * 异步任务管理器
 * 
 * @author ruoyi
 */
public class AsyncManager {
    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;

    /**
     * 异步操作任务调度线程池
     * 此处也可以使用ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 创建ScheduledExecutorService 对象
     */
    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() {
        Threads.shutdownAndAwaitTermination(executor);
    }
}

ScheduledExecutorService接口是java线程池中最重要的几个接口之一。它除了支持原生线程池的功能之外,同时支持定时任务处理的功能。

Executor
						↑
				 ExecutorService
						↑
			 ScheduledExecutorService

在JDK中为它提供了一个默认的实现类:ScheduledThreadPoolExecutor

schedule()方法:计划在将来的某个时刻执行任务

若依 sys_confg redis 若依管理系统_若依 sys_confg redis_04

若依 sys_confg redis 若依管理系统_若依 sys_confg redis_05

三、使用方式

在需要异步执行的地方调用:

AsyncManager.me().execute(asyncFactory.testAsynTask(LocalDateTime.now()));

到此就可以实现异步的日志记录入库的操作。