需求分析
在SpringBoot系统中,一般会对访问系统的请求做日志记录的需求,确保系统的安全维护以及查看接口的调用情况,可以使用AOP对controller层的接口进行增强,作日志记录。日志保存在数据库当中,为了避免影响接口的响应,降低用户体验度,采用异步的方式记录日志,避免日志记录阻塞接口请求
实现原理
通过定义AOP切面,访问接口之前,使用前置通知记录一些有用的数据,如ip地址、请求方式、操作人等等,接口执行完之后使用后置通知记录本次请求执行的实践、执行结果等等信息。
实现代码
AOP日志切面
定义切点表达式指向Controller的所有方法,即指向所有接口
/**
* @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));
}
}
异步任务管理器(线程池)
定义一个单例的异步任务管理器,使用线程池完成异步操作
/**
* @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() {
@Override
public void run() {
// 远程查询操作地点
SpringUtils.getBean(SysLogService.class).addSysLogInfo(operLog);
}
};
}
}