使用Aop的方式实现自动日志记录的方式详细介绍(aop实现日志记录和异常处理)

网友投稿 360 2022-08-10


使用Aop的方式实现自动日志记录的方式详细介绍(aop实现日志记录和异常处理)

目录34、使用Aop的方式实现自动日志记录采用第一种方式:1、第一步、定义一个注解:2、第二步、编写一个切面3、使用自定义注解

34、使用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(NjfPeenElementType.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)

@Documented

public @interface AutoLog {

/**

* 日志内容

*

* @return

*/

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";

/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */

public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;

/** {@code 200 OK} (HTTP/1.0 - RFC 1945) */

public static final Integer SC_OK_200 = 200;

/**访问权限认证未通过 510*/

public static final Integer SC_JEECG_NO_AUTHZ=510;

/** 登录用户Shiro权限缓存KEY前缀 */

public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.modules.shiro.authc.ShiroRealm.authorizationCache:";

/** 登录用户Token令牌缓存KEY前缀 */

public static final String PREFIX_USER_TOKEN = "prefix_user_token_";

/** Token缓存时间:3600秒即一小时 */

public static final int TOKEN_EXPIRE_TIME = 3600;

* 0:一级菜单

public static final Integer MENU_TYPE_0 = 0;

/**

* 1:子菜单

*/

public static final Integer MENU_TYPE_1 = 1;

* 2:按钮权限

public static final Integer MENU_TYPE_2 = 2;

/**通告对象类型(USER:指定用户,ALL:全体用户)*/

public static final String MSG_TYPE_UESR = "USER";

public static final String MSG_TYPE_ALL = "ALL";

/**发布状态(0未发布,1已发布,2已撤销)*/

public static final String NO_SEND = "0";

public static final String HAS_SEND = "1";

public static final String HAS_CANCLE = "2";

/**阅读状态(0未读,1已读)*/

public static final String HAS_READ_FLAG = "1";

public static final String NO_READ_FLAG = "0";

/**优先级(L低,M中,H高)*/

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 finalNjfPeen 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

@Component

public 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 signahttp://ture = (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 (Exceptiohttp://n 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 page = new Page<>(sysSelectLog.getPageNo(),sysSelectLog.getPageSize());

IPage sysSelectLogIPage = sysSelectLogService.queryPage(page,sysSelectLog);

return Result.ok(sysSelectLogIPage);

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java集合去重导致的线上问题(java去重队列)
下一篇:Java中如何保证缓存一致性问题(缓存一致性如何解决)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~