自定义注解+Spring AOP实现记录用户操作日志
2018-11-03 14:53
543 查看
版权声明:本博客为本人工作遇到问题解决方案或学习笔记,若有错误或不足,欢迎留言交流。 https://blog.csdn.net/zorro_jin/article/details/83687264
一、背景
项目中需要对用户的各种操作做详细的操作日志记录,需要记录用户操作的操作模块、具体操作以及操作的数据记录ID等。
若写一个方法去保存操作,则需要每次手动去调用。由于是非业务性的操作,并且大量的重复操作,Spring AOP就能很好的解决这个问题。
由于用户操作的实现方法并不在同一个类中,而且每个操作的说明也不一样,所以用自定义注解作为切入点,来记录用户不同操作及其操作说明。
二、配置
2.1、导入jar包
- spring-aop.jar
- aspectjrt.jar
- aspectjweaver.jar
- aopalliance-1.0.jar
2.2、Spring的ApplicationContext.xml配置文件
2.2.1 配置头文件
[code]xmlns:aop="http://www.springframework.org/schema/aop" <!-- 在xsi:schemaLocation中添加 --> http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
注:若没添加则会报 ==》
java错误-The prefix "aop" for element "aop:aspectj-autoproxy" is not bound.
2.2.2 配置注解扫描和AOP自动代理
[code]<!-- 配置组件扫描功能 --> <context:component-scan base-package="com.test"/> <!-- 配置自动代理功能 --> <aop:aspectj-autoproxy /> <aop:config proxy-target-class="true"></aop:config>
三、创建自定义注解
[code]package com.test.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 品目操作日志注解 * @author zhoujin */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperationLogAnno { /** 模块 */ String module(); /** 具体操作 */ String operate(); /** 品目编号 */ String pmbh(); /** 备注说明 */ String remarks() default ""; }
注:注解中定义的方法若没有给默认值,则写该注解的时候必须给该方法赋值!
四、Spring AOP切面类
[code]package com.test.common.aop; import java.lang.reflect.Method; import java.util.Date; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.test.common.annotation.OperationLogAnno; import com.test.modules.sys.entity.User; import com.test.modules.sys.utils.UserUtils; import com.test.modules.zxztb.entity.operationLog.OperationLog; import com.test.modules.zxztb.service.operationLog.OperationLogService; /** * 品目操作日志切面类 * @author zhoujin * @date 2018-10-23 */ @Aspect @Component("operationLogAspect") public class OperationLogAspect { private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class); @Autowired private OperationLogService operationLogService; // 配置织入点(以OperationLog注解为标志) @Pointcut("@annotation(com.test.common.annotation.OperationLogAnno)") public void logPointCut() { } /** * 前置通知 用于拦截操作,在方法返回后执行 * @param joinPoint 切点 */ @AfterReturning(pointcut = "logPointCut()") public void doBefore(JoinPoint joinPoint) { handleLog(joinPoint, null); } /** * 拦截异常操作,有异常时执行 * @param joinPoint * @param e */ @AfterThrowing(value = "logPointCut()", throwing = "e") public void doAfter(JoinPoint joinPoint, Exception e) { handleLog(joinPoint, e); } private void handleLog(JoinPoint joinPoint, Exception e) { try { // 获得注解 OperationLogAnno controllerLog = getAnnotationLog(joinPoint); if (controllerLog == null) { return; } // 品目编号 Object pmbh = controllerLog.pmbh(); // 模块详情 Object module = controllerLog.module(); // 具体操作 Object operate = controllerLog.operate(); // 备注说明 Object remarks = controllerLog.remarks(); // 操作用户信息 User currentUser = UserUtils.getUser(); // 访问类名 String className = joinPoint.getTarget().getClass().getName(); // 访问方法名 String methodName = joinPoint.getSignature().getName(); // 保存日志 log.info("\n====================================== 保存操作日志 ======================================"); OperationLog operationLog = new OperationLog(); operationLog.setPmbh(pmbh.toString()); operationLog.setModule(module.toString()); operationLog.setOperate(operate.toString()); operationLog.setOperate(remarks.toString()); operationLog.setOperatorLoginname(currentUser.getLoginName()); operationLog.setOperatorName(currentUser.getName()); operationLog.setOperateDate(new Date()); operationLog.setClassname(className); operationLog.setMethodname(methodName); operationLogService.save(operationLog); log.info("\n=====================================================================================\n"); } catch (Exception exp) { // 记录本地异常日志 log.error("\n====================================== 异常信息通知 ======================================"); log.error("异常信息:{}", exp.getMessage()); log.error("\n====================================================================================\n"); exp.printStackTrace(); } } /** * 是否存在注解,如果存在就获取 */ private static OperationLogAnno getAnnotationLog(JoinPoint joinPoint) throws Exception { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(OperationLogAnno.class); } return null; } }
五、Controller上使用
[code]@ResponseBody @RequestMapping(value = "kaiQicb") @RequiresPermissions("zxztb:dljgKbkzt:kqcb") @OperationLogAnno(module="控制台", operate="开启流程", pmbh="1111111") public AjaxJson kaiQicb(String id) { AjaxJson j = new AjaxJson(); String message = "开启流程成功"; Zfcgpmbxm zfcgpmbxm = zfcgpmbxmService.get(id); try { zfcgpmbxm.setFlowstatus("7"); zfcgpmbxmService.save(zfcgpmbxm); } catch (Exception e) { e.printStackTrace(); j.setSuccess(false); message = "开启流程失败"; } j.setMsg(message); return j; }
注:此处注解上的pmbh是写死的,没有什么作用。但实际情况是,此处pmbh应取前端页面传来的参数id的值,那么怎么才能使注解中能获取到参数的值呢?
六、下载上面node2017博客中的AnnotationResolver,便可获取前端页面传来的参数值
6.1 Controller中
[code]@OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{id}") public AjaxJson kaiQiKaiBiao(String id){ ... } // 这样也可以 @OperationLogAnno(module="控制台", operate="开启流程", pmbh="#{pmxx.id}") public AjaxJson kaiQiKaiBiao(Pmxx pmxx){ ... }
6.2 Spring AOP切面类中
[code] private void handleLog(JoinPoint joinPoint, Exception e) { try { // 获得注解 OperationLogAnno controllerLog = getAnnotationLog(joinPoint); if (controllerLog == null) { return; } AnnotationResolver annotationResolver = AnnotationResolver.newInstance(); // 品目编号 Object pmbh = annotationResolver.resolver(joinPoint, controllerLog.pmbh()); // 模块详情 Object module = annotationResolver.resolver(joinPoint, controllerLog.module()); // 具体操作 Object operate = annotationResolver.resolver(joinPoint, controllerLog.operate()); // 备注说明 Object remarks = annotationResolver.resolver(joinPoint, controllerLog.remarks()); // 操作用户信息 User currentUser = UserUtils.getUser(); // 访问类名 String className = joinPoint.getTarget().getClass().getName(); // 访问方法名 String methodName = joinPoint.getSignature().getName(); // 保存日志 ... } catch (Exception exp) { exp.printStackTrace(); }
6.4 AnnotationResolver代码(来自:node2017 ==》java在注解中绑定方法参数的解决方案)
[code]package com.jeeplus.common.annotation; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; /** * 该类的作用可以把方法上的参数绑定到注解的变量中,注解的语法#{变量名} * 能解析类似#{user}或者#{user.id}或者{user.createBy.id} */ public class AnnotationResolver { private static AnnotationResolver resolver ; public static AnnotationResolver newInstance(){ if (resolver == null) { return resolver = new AnnotationResolver(); }else{ return resolver; } } /** * 解析注解上的值 * @param joinPoint * @param str 需要解析的字符串 * @return */ public Object resolver(JoinPoint joinPoint, String str) { if (str == null) return null ; Object value = null; if (str.matches("#\\{\\D*\\}")) {// 如果name匹配上了#{},则把内容当作变量 String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", ""); if (newStr.contains(".")) { // 复杂类型 try { value = complexResolver(joinPoint, newStr); } catch (Exception e) { e.printStackTrace(); } } else { value = simpleResolver(joinPoint, newStr); } } else { //非变量 value = str; } return value; } private Object complexResolver(JoinPoint joinPoint, String str) throws Exception { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] names = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); String[] strs = str.split("\\."); for (int i = 0; i < names.length; i++) { if (strs[0].equals(names[i])) { Object obj = args[i]; Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null); Object value = dmethod.invoke(args[i]); return getValue(value, 1, strs); } } return null; } private Object getValue(Object obj, int index, String[] strs) { try { if (obj != null && index < strs.length - 1) { Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null); obj = method.invoke(obj); getValue(obj, index + 1, strs); } return obj; } catch (Exception e) { e.printStackTrace(); return null; } } private String getMethodName(String name) { return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase()); } private Object simpleResolver(JoinPoint joinPoint, String str) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] names = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); for (int i = 0; i < names.length; i++) { if (str.equals(names[i])) { return args[i]; } } return null; } }
阅读更多
相关文章推荐
- Spring自定义注解+Aop记录用户操作日志
- springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)
- Spring+SpringMVC+Mybatis 利用AOP自定义注解实现可配置日志快照记录
- Spring 自定义注解实现操作日志记录功能
- 使用Spring AOP使用注解记录用户操作日志
- springboot 自定义注解+AOP 实现日志记录
- [EntLib]微软企业库5.0 学习之路——第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录
- spring aop 实现用户操作日志记录功能
- Spring AOP自定义注解实现系统日志记录管理
- Spring AOP实现复杂的日志记录(自定义注解)
- 使用自定义注解+Spring AOP 实现日志记录
- Spring AOP 自定义注解记录操作日志
- 使用SpringAop与自定义注解实现日志记录
- Spring+SpringMVC+Mybatis 利用AOP自定义注解实现可配置日志快照记录
- springMVC +Mybatis +spring aop 实现用户系统操作日志记录
- 微软企业库5.0 学习之路——第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录
- Spring+SpringMVC+Mybatis 利用AOP自定义注解实现可配置日志快照记录
- 基于SSM利用SpringAOP切面及自定义注解 记录每次操作记录(操作日志 同理)
- spring aop自定义注解实现日志记录
- SpringAOP拦截Controller,Service实现日志管理(自定义注解的方式)