后端——AOP异步日志
需求分析
在SpringBoot系统中,一般会对访问系统的请求做日志记录的需求,确保系统的安全维护以及查看接口的调用情况,可以使用AOP对controller层的接口进行增强,作日志记录。日志保存在数据库当中,为了避免影响接口的响应,降低用户体验度,采用异步的方式记录日志,避免日志记录阻塞接口请求
实现原理
通过定义AOP切面,访问接口之前,使用前置通知记录一些有用的数据,如ip地址、请求方式、操作人等等,接口执行完之后使用后置通知记录本次请求执行的实践、执行结果等等信息。
实现代码
AOP日志切面
定义切点表达式指向Controller的所有方法,即指向所有接口
/*** @Description 日志切面类*/
@Aspect
@Component
public class SysLogAspect {@AutowiredSysLogDao 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();//获取请求ipip = 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;}//请求IPsysLog.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));}
}
异步任务管理器(线程池)
定义一个单例的异步任务管理器,使用线程池完成异步操作
/*** @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);}
}
异步工厂
这里定义异步工厂去创建日记记录的任务,同时也方便系统扩展其他的异步操作
public class AsyncFactory {/*** 操作日志记录** @param operLog 操作日志信息* @return 任务task*/public static TimerTask recordOper(final SysLog operLog) {return new TimerTask() {@Overridepublic void run() {// 远程查询操作地点SpringUtils.getBean(SysLogService.class).addSysLogInfo(operLog);}};}}