本篇内容介绍了“怎么使用Aop的方式实现自动日志记录”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
使用Aop的方式实现自动日志记录
自动日志记录的实现的两种方式:
①通过监听器去监听,当访问到具体的类方法,通过aop切面去获取访问的方法,然后将日志记录下来
②通过拦截器,编写一个类去继承HandlerInterceptorAdapter,重写preHandle,postHandle,然后在里面进行日志记录,编写的类加到spring容器里
采用第一种方式:
1、第一步、定义一个注解:
Annotation 注解的作用:
@interface 表示这是一个注解类, 不是interface,是注解类 定义注解用的,是jdk1.5之后加入的,java没有给它新的关键字,所以就用@interface 这么个东西表示了
@Inherited //这个Annotation 可以被继承
@Documented //这个Annotation可以被写入javadoc
@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Retention(RetentionPolicy.RUNTIME) //可以用来修饰注解,是注解的注解,称为元注解。
public enum RetentionPolicy { SOURCE, // 编译器处理完Annotation后不存储在class中 CLASS, // 编译器把Annotation存储在class中,这是默认值 RUNTIME // 编译器把Annotation存储在class中,可以由虚拟机读取,反射需要 }
创建一个注解:
default 0 相当于set和get方法,添加一个默认值
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AutoLog {String value() default ""; * 日志类型 * @return 1:登录日志;2:操作日志;3:访问日志;4:异常日志;5:定时任务;int logType() default CommonConstant.LOG_TYPE_2; * 操作日志类型 * @return (1查询,2添加,3修改,4删除)int operateType() default 0;}
CommonConstant 相关的配置
public interface CommonConstant {public static final Integer STATUS_NORMAL = 0; * 禁用状态public static final Integer STATUS_DISABLE = -1; * 删除标志public static final Integer DEL_FLAG_DELETED = 1; * 未删除public static final Integer DEL_FLAG_UNDELETED = 0; * 系统日志类型: 登录public static final int LOG_TYPE_1 = 1; * 系统日志类型: 操作public static final int LOG_TYPE_2 = 2; public static final int LOG_TYPE_3 = 3; * 系统日志类型: 异常 public static final int LOG_TYPE_4 = 4; * 系统日志类型: 定时任务 public static final int LOG_TYPE_5 = 5; * 系统日志类型: 用户管理 public static final int LOG_TYPE_6 = 6; * 系统登陆日志:正常账户密码登录 public static final int OPERATE_TYPE_LT1_1 = 1; * 系统登陆日志:二维码登陆 public static final int OPERATE_TYPE_LT1_2 = 2; * 系统登陆日志:单点登陆 public static final int OPERATE_TYPE_LT1_3 = 3; * 系统登陆日志:登出 public static final int OPERATE_TYPE_LT1_4 = 4; * 系统登陆日志:模拟登陆 public static final int OPERATE_TYPE_LT1_5 = 5; * 操作日志类型: 查询public static final int OPERATE_TYPE_LT2_1 = 1; * 操作日志类型: 添加public static final int OPERATE_TYPE_LT2_2 = 2; * 操作日志类型: 更新public static final int OPERATE_TYPE_LT2_3 = 3; * 操作日志类型: 删除public static final int OPERATE_TYPE_LT2_4 = 4; * 操作日志类型: 导入public static final int OPERATE_TYPE_LT2_5 = 5; * 操作日志类型: 导出public static final int OPERATE_TYPE_LT2_6 = 6; * 访问日志类型: 进入 public static final int OPERATE_TYPE_LT3_1 = 1; * 异常日志类型: 普通操作即代码错误 public static final int OPERATE_TYPE_LT4_1 = 1; * 异常日志类型: 非法操作即越权操作 public static final int OPERATE_TYPE_LT4_2 = 2;public static final String CLIENT_TYPE_PC="0";public static final String CLIENT_TYPE_MOBILE="1"; public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500; public static final Integer SC_OK_200 = 200; public static final Integer SC_JEECG_NO_AUTHZ=510; public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.modules.shiro.authc.ShiroRealm.authorizationCache:"; public static final String PREFIX_USER_TOKEN = "prefix_user_token_"; public static final int TOKEN_EXPIRE_TIME = 3600; * 0:一级菜单 public static final Integer MENU_TYPE_0 = 0; public static final Integer MENU_TYPE_1 = 1; * 2:按钮权限 public static final Integer MENU_TYPE_2 = 2; public static final String MSG_TYPE_UESR = "USER"; public static final String MSG_TYPE_ALL = "ALL"; public static final String NO_SEND = "0"; public static final String HAS_SEND = "1"; public static final String HAS_CANCLE = "2"; public static final String HAS_READ_FLAG = "1"; public static final String NO_READ_FLAG = "0"; public static final String PRIORITY_L = "L"; public static final String PRIORITY_M = "M"; public static final String PRIORITY_H = "H"; * 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板 public static final String SMS_TPL_TYPE_0 = "0"; public static final String SMS_TPL_TYPE_1 = "1"; public static final String SMS_TPL_TYPE_2 = "2"; * 状态(0无效1有效) public static final String STATUS_0 = "0"; public static final String STATUS_1 = "1"; * 同步工作流引擎1同步0不同步 public static final String ACT_SYNC_0 = "0"; public static final String ACT_SYNC_1 = "1"; * 消息类型1:通知公告2:系统消息 public static final String MSG_CATEGORY_1 = "1"; public static final String MSG_CATEGORY_2 = "2"; * 是否配置菜单的数据权限 1是0否 public static final Integer RULE_FLAG_0 = 0; public static final Integer RULE_FLAG_1 = 1; * 用户状态 0冻结 1正常 2待定 public static final Integer USER_FREEZE = 0; public static final Integer USER_NORMAL = 1; * 用户删除标志位 0未删 1已删 public static final Integer USER_DELETE_NO=0; public static final Integer USER_DELETE_YES=1; public static final String DICT_TEXT_SUFFIX = "_dictText"; public static final String ITEM_DISPLAY = "_display"; * 表单设计器主表类型 public static final Integer DESIGN_FORM_TYPE_MAIN = 1; * 表单设计器子表表类型 public static final Integer DESIGN_FORM_TYPE_SUB = 2; * 表单设计器URL授权通过 public static final Integer DESIGN_FORM_URL_STATUS_PASSED = 1; * 表单设计器URL授权未通过 public static final Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2; * 表单设计器新增 Flag public static final String DESIGN_FORM_URL_TYPE_ADD = "add"; * 表单设计器修改 Flag public static final String DESIGN_FORM_URL_TYPE_EDIT = "edit"; * 表单设计器详情 Flag public static final String DESIGN_FORM_URL_TYPE_DETAIL = "detail"; * 表单设计器复用数据 Flag public static final String DESIGN_FORM_URL_TYPE_REUSE = "reuse"; * 表单设计器编辑 Flag (已弃用) public static final String DESIGN_FORM_URL_TYPE_VIEW = "view"; * online参数值设置(是:Y, 否:N) public static final String ONLINE_PARAM_VAL_IS_TURE = "Y"; public static final String ONLINE_PARAM_VAL_IS_FALSE = "N"; * 文件上传类型(本地:local,Minio:minio,阿里云:alioss) public static final String UPLOAD_TYPE_LOCAL = "local"; public static final String UPLOAD_TYPE_MINIO = "minio"; public static final String UPLOAD_TYPE_OSS = "alioss"; * 员工身份 (1:普通员工 2:上级) public static final Integer USER_IDENTITY_1 = 1; public static final Integer USER_IDENTITY_2 = 2; * 日期格式 public static final String TIME_FORMAT_YMD = "yyyy-MM-dd"; public static final String TIME_FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss"; public static final String TIME_FORMAT_YMDHMSSZ = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";}
2、第二步、编写一个切面
@Aspect 表示这是一个切面
@Component 告诉spring 这是一个bean ,注入
@annotation 获取定义的注解
@Pointcut 切点,
@Pointcut("@annotation(xx.AutoLog)") 表示,使用了这个注解的,就是切入点
@Around的作用
既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
虽然Around功能强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before、AfterReturing增强方法就可以解决的事情,就没有必要使用Around增强处理了。
ProceedingJoinPoint 环绕通知,主要作用找到程序执行中的可识别的点,当aop的切入点
环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
简单理解,环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的.
@Aspect@Componentpublic class AutoLogAspect { @Autowired private ISysLogService sysLogService; @Pointcut("@annotation(xx.AutoLog)") public void logPointCut() { } @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 saveSysLog(point, time); return result; } private void saveSysLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog sysLog = new SysLog(); AutoLog syslog = method.getAnnotation(AutoLog.class); if (syslog != null) { //注解上的描述,操作日志内容 sysLog.setLogContent(syslog.value()); sysLog.setLogType(syslog.logType()); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); sysLog.setMethod(className + "." + methodName + "()"); //设置操作类型 if (sysLog.getLogType() == CommonConstant.LOG_TYPE_2) { sysLog.setOperateType(getOperateType(methodName, syslog.operateType())); } //请求的参数 Object[] args = joinPoint.getArgs(); try { String params = JSONObject.toJSONString(args); sysLog.setRequestParam(params); } catch (Exception e) { } try { //获取request HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); //设置IP地址 sysLog.setIp(IPUtils.getIpAddr(request)); } catch (Exception e) { } //获取登录用户信息 LoginUser sysUser = null; try { sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); } catch (Exception e) { } if (sysUser != null) { sysLog.setUserId(sysUser.getId()); sysLog.setUserName(sysUser.getUserName()); sysLog.setRealName(sysUser.getRealName()); sysLog.setOrgId(sysUser.getNowOrgId()); sysLog.setOrgName(sysUser.getNowOrgName()); } //耗时 sysLog.setCostTime(time); sysLog.setCreateTime(new Date()); //保存系统日志 sysLogService.save(sysLog); } private int getOperateType(String methodName, int operateType) { if (operateType > 0) { return operateType; } if (methodName.startsWith("list")) { return CommonConstant.OPERATE_TYPE_LT2_1; } if (methodName.startsWith("add")) { return CommonConstant.OPERATE_TYPE_LT2_2; } if (methodName.startsWith("edit")) { return CommonConstant.OPERATE_TYPE_LT2_3; } if (methodName.startsWith("delete")) { return CommonConstant.OPERATE_TYPE_LT2_4; } if (methodName.startsWith("import")) { return CommonConstant.OPERATE_TYPE_LT2_5; } if (methodName.startsWith("export")) { return CommonConstant.OPERATE_TYPE_LT2_6; } return CommonConstant.OPERATE_TYPE_LT2_1; } @AfterThrowing(pointcut = "logPointCut()", throwing = "ex") public void afterThrowing(JoinPoint joinPoint, Throwable ex) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog sysLog = new SysLog(); StackTraceElement[] stackTraceElements = ex.getStackTrace(); String rootExceptionName = ex.getClass().getName(); StringBuilder resultContent = new StringBuilder("异常类:" + rootExceptionName); int count = 0; int maxTrace = 3; for (StackTraceElement stackTraceElement : stackTraceElements) { if (stackTraceElement.getClassName().contains("com.lingxu") && count < maxTrace) { resultContent.append("\n出现于").append(stackTraceElement.getClassName()) .append("类中的").append(stackTraceElement.getMethodName()) .append("方法中 位于该类文件的第").append(stackTraceElement.getLineNumber()) .append("行)"); count++; if (count == maxTrace) { break; } } } sysLog.setExceptionContent(resultContent.toString()); AutoLog syslog = method.getAnnotation(AutoLog.class); if (syslog != null) { //注解上的描述,操作日志内容 sysLog.setLogContent(syslog.value() + "出现异常"); sysLog.setLogType(CommonConstant.LOG_TYPE_4); } //请求的方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); sysLog.setMethod(className + "." + methodName + "()"); //设置操作类型 sysLog.setOperateType(CommonConstant.OPERATE_TYPE_LT4_1); //请求的参数 Object[] args = joinPoint.getArgs(); try { String params = JSONObject.toJSONString(args); sysLog.setRequestParam(params); } catch (Exception e) { } try { //获取request HttpServletRequest request = SpringContextUtils.getHttpServletRequest(); //设置IP地址 sysLog.setIp(IPUtils.getIpAddr(request)); } catch (Exception e) { } try { //获取登录用户信息 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); if (sysUser != null) { sysLog.setUserId(sysUser.getId()); sysLog.setUserName(sysUser.getUserName()); sysLog.setRealName(sysUser.getRealName()); sysLog.setOrgId(sysUser.getNowOrgId()); sysLog.setOrgName(sysUser.getNowOrgName()); } } catch (Exception e) { } //保存系统日志 sysLogService.save(sysLog); }}
3、使用自定义注解
可以在controller或者实现类上进行注解的加入
@AutoLog(value = "sss",logType = CommonConstant.LOG_TYPE_3, operateType = 2)@ApiOperation(value="记录查询日志-分页列表查询", notes="记录查询日志-分页列表查询")@PostMapping(value = "/queryPage")public Result<?> queryPage(@RequestBody SysSelectLog sysSelectLog){Page<SysSelectLog> page = new Page<>(sysSelectLog.getPageNo(),sysSelectLog.getPageSize());IPage<SysSelectLog> sysSelectLogIPage = sysSelectLogService.queryPage(page,sysSelectLog);return Result.ok(sysSelectLogIPage);}
“怎么使用Aop的方式实现自动日志记录”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!