需求分析
在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);}};}}