Java设计模式之外观模式示例详解
394
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
IPage
return Result.ok(sysSelectLogIPage);
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~