1 业务需求:今日,公司要求对操作的业务和日志统一做处理,需要把业务表数据相关信息存入日志表中,比如表名,方法名,业务id,操作操作时间modifyTIme等等。

除了在业务主动插入日志数据之外,有个比较好的方法就是用面向切面aop处理,明确跟业务逻辑分开,把业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

2 业务开发,这边处理的方式是用方式1【两种方式 1 利用注解方式 2 通过xml配置】

  • 2.1 定义切入点接口类
package com.hec.dup.facade.mgr.annotation;import java.lang.annotation.*;/*** 标记需要做业务日志的方法*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OperateLogAnnotation {/*** 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id"*/String key() default "id";/*** 业务模块类型,例如:"01菜单管理"*/String moduleType()  default "";/*** 业务模块名称,例如:"菜单管理"*/String moduleName()  default "";/*** 业务模块功能名称,例如:"新增菜单功能"*/String functionName()  default "";/*** 操作日志内容,例如:"修改菜单"*/String operateContent() default "";/*** 业务模块表名,例如:"SYS_MENU"*/String tableName() default "";}
  • 2.2 定义日志记录切面类
package com.hec.dup.facade.mgr.aspect;import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hec.dup.beans.base.PrjUserEntity;
import com.hec.dup.beans.base.vo.PrjCurrUserInfo;
import com.hec.dup.beans.com.DupComOperateLogEntity;
import com.hec.dup.common.utils.UuidUtil;
import com.hec.dup.facade.base.IPrjUserService;
import com.hec.dup.facade.com.IDupComOperateLogService;
import com.hec.dup.facade.mgr.annotation.OperateLogAnnotation;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
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 java.lang.reflect.Method;
import java.util.Date;/*** 日志记录切面* @author*/
@Aspect
@Component
public class OperateLogAspect {private Logger log = LoggerFactory.getLogger(this.getClass());@Autowired(required = false)private IDupComOperateLogService operateLogService;@Autowired(required = false)private IPrjUserService prjUserService;/*** 通过AOP方式,拦解注解的日志     */@Pointcut(value = "@annotation(com.hec.dup.facade.mgr.annotation.OperateLogAnnotation)")public void logAspect() {}/*** 后置通知:如果需要访问其他建议类型的连接点上下文,则应使用JoinPoint参数类型而不是ProceedingJoinPoint。*/@After("logAspect()")public Object recordLog(JoinPoint point) throws Throwable {       try {this.handle(point);} catch (Exception e) {e.printStackTrace();log.error("日志记录出错!", e);}return result;}/*** 获取拦截方法参数,处理日志* @param point* @throws Exception*/private void handle(ProceedingJoinPoint point) throws Exception {//获取拦截的方法名Signature sig = point.getSignature();MethodSignature msig = null;if (!(sig instanceof MethodSignature)) {throw new IllegalArgumentException("该注解只能用于方法");}msig = (MethodSignature) sig;Object[] params = point.getArgs();Object target = point.getTarget();Method method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());//如果当前用户未登录,不做日志PrjCurrUserInfo userInfo = (PrjCurrUserInfo)SecurityUtils.getSubject().getPrincipal();if (null == userInfo) {return;}//获取拦截方法的参数,获取登录用户对象PrjUserEntity userEntity = userInfo.getUserEntity();/**// 拦截的实体类Object target = joinPoint.getTarget();// 拦截的方法名称String methodName = joinPoint.getSignature().getName();// 拦截的方法参数Object[] args = joinPoint.getArgs();// 拦截的参数类型Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();**///获取注解日志内容OperateLogAnnotation annotation = method.getAnnotation(OperateLogAnnotation.class);//字典,替换成用table查询表注释//Class dictClass = annotation.dict();//通过表名,动态获取表字段名和注释/* Map<String,String> metaMap = new HashMap<>();//MetaTableVo metaTableVo = metaTableService.getByTableName(table);if(StringUtil.isNotEmpty(table)){List<MetaColumVo> metaColumns = metaColumService.queryByTable(table);if(metaColumns!=null && metaColumns.size()>0){for(MetaColumVo vo:metaColumns){metaMap.put(vo.getColumnName(),vo.getColumnAlias());}}}*/StringBuilder sb = new StringBuilder();for (Object param : params) {sb.append(param);sb.append(" & ");}//如果涉及到修改,比对修改前后值的变化内容String logmsg ="";/*if (methodName.indexOf("update") != -1 || methodName.indexOf("modify") != -1|| methodName.indexOf("edit") != -1) {Map<String, String> newMap = HttpKit.getRequestParameters(); //对象被修改后的内容Object oldObj = redisSupport.getObject(HttpKit.getRequest().getSession().getId());//Object oldObj = LogObjectHolder.me().get();  //获取为nulllogmsg = ContrastObjFactory.contrastObj(table,key,metaMap,newMap,oldObj);} else {logmsg=content;}*///记录操作日志DupComOperateLogEntity operateLogEntity = getOperaLog( userEntity, target, method, logmsg, params );operateLogService.save(operateLogEntity);}/*** 封装操作日志实体对象** @Date 2017/3/30 18:45*/public static DupComOperateLogEntity getOperaLog(PrjUserEntity userEntity, Object target, Method method, String msg, Object[] params) {String classPath = target.getClass().getName(); //类名称,含路径String classMethod = method.getName(); //方法名(英文)OperateLogAnnotation annotation = method.getAnnotation( OperateLogAnnotation.class );DupComOperateLogEntity operaLog = new DupComOperateLogEntity();operaLog.setId( UuidUtil.getUuid() ); //主键operaLog.setClassPath( classPath ); //类名称,含路径operaLog.setClassMethod( classMethod ); //方法名(英文)operaLog.setIpHost( userEntity.getLastLoginIp() ); //IP地址operaLog.setOperateTime( new Date() ); //操作时间operaLog.setOperateUserId( userEntity.getId() );//用户IDoperaLog.setOperateUserName( userEntity.getUserName() ); //用户名称operaLog.setUserAccount( userEntity.getAccount() ); //用户帐号operaLog.setUserType( userEntity.getUserType() ); //帐号类型operaLog.setModuleType( annotation.moduleType() );//业务模块类型operaLog.setModuleName( annotation.moduleName() );//业务模块名称operaLog.setFunctionName( annotation.functionName() );//业务模块名称operaLog.setTableName( annotation.tableName() ); //操作表名if (params != null && params.length > 0) {Map<String, Object> paramMap = object2Map( params[0] );operaLog.setTableId( paramMap.get( "id" ).toString() );}operaLog.setOperateContent( annotation.operateContent() ); //操作内容return operaLog;}/*** 实体对象转成Map* @param obj 实体对象* @return*/public static Map<String, Object> object2Map(Object obj) {Map<String, Object> map = new HashMap<>();if (obj == null) {return map;}Class clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();try {for (Field field : fields) {field.setAccessible(true);map.put(field.getName(), field.get(obj));}} catch (Exception e) {e.printStackTrace();}return map;}}
  • 2.3 定义applicationContext.xml配置

  • 2.4 接口添加注解,接口被调用的时候会调用被aop处理
    @OperateLogAnnotation(moduleType = "01")  //这边添加切入点接口的注解@RequestMapping(value = "testAop01")@ResponseBodypublic JsonResult testAop(BaseEntity baseEntity) {JsonResult jr = new JsonResult();baseEntity.setCreateTime( new Date() );baseEntity.setDbUser( "123456789," );baseEntity.setExport( true );jr.setData( baseEntity );return jr;}

3 注意事项:该方式主要是通过注解的方式,个人觉得比较便利,当然也可以通过另外一种方式xml,比如 AOP实现方式3——通过<aop:config>来配置 ,需要注意的是aop的执行顺序,可参考Spring AOP @Before @Around @After 等 advice 的执行顺序

转载于:https://blog.51cto.com/12066352/2177101

Spring AOP 实现业务和异常日志记录实战相关推荐

  1. 【经典】Spring aop切面实现异步添加日志—完整版

    系统开发中我们常遇到要处理系统日志等信息的,在此我分享一篇 利用spring aop切面来异步添加日志的操作,其中用到了 队列和多线程,前面的博客有写. 第一步:创建log实体,根据自己业务而定, p ...

  2. spring AOP自定义注解方式实现日志管理

    转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接 ...

  3. JPOM - AOP+自定义注解实现操作日志记录

    文章目录 地址 版本 源码解析-AOP+自定义注解实现操作日志记录 地址 Gitee: https://gitee.com/dromara/Jpom 官网: https://jpom.io/ 一款简而 ...

  4. Log4Net异常日志记录在asp.net mvc3.0的应用

    前言 log4net是.Net下一个非常优秀的开源日志记录组件.log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介.本文主要是简单的介绍如何在Visual ...

  5. mysql aop_aop: 使用spring aop实现业务层mysql 读写分离

    使用spring aop实现业务层mysql 读写分离 现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据 ...

  6. Spring Boot 如何使用 JUL 进行日志记录

    Spring Boot 如何使用 JUL 进行日志记录 在 Spring Boot 中,我们可以使用多种日志框架进行日志记录.其中,JUL (Java Util Logging) 是 Java 平台自 ...

  7. SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务

    本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.AOP切面编程 1.什么是AOP编程 在软件业,AOP为Asp ...

  8. springboot 利用aop实现系统日志和操作日志记录

    1.目的 通过aop及注解的方式,记录异常信息和特定的操作日志到数据库. 2.引入依赖 <dependency><groupId>org.springframework.boo ...

  9. 使用spring aop实现业务层mysql 读写分离

    2019独角兽企业重金招聘Python工程师标准>>> 1.使用spring aop 拦截机制现数据源的动态选取. import java.lang.annotation.Eleme ...

最新文章

  1. python错误提示:TypeError: ‘builtin_function_or_method‘ object is not subscriptable
  2. Java实现算法导论中KMP字符串匹配算法
  3. ubuntu虚拟机卡住开机_虚拟机Ubuntu开机问题及解决方法
  4. EventBus设计与实现分析——特性介绍
  5. 是前端类库还是前端框架?
  6. 【数据库】SQL查询强化篇
  7. 双十一囤点知识干货!
  8. 性能测试系列:高可用测试linux常用命令
  9. esxi6.7封装nvme驱动
  10. java实现文件夹的复制(包括子文件夹以及子文件)
  11. 线性判别分析(Linear Discriminant Analysis)
  12. 三阶魔方还原步骤图_三阶魔方的还原步骤
  13. centos7搭建GRE隧道进行通信
  14. 千锋Unity学习笔记
  15. 厦门大学的【软件工程专业】被撤销!
  16. nacos 单机部署_使用Docker部署Nacos-Server(单机版)
  17. 什么是链接?(动态链接库和静态链接库的对比)
  18. GitFlow使用笔记
  19. mysql 增删改查时的错误解决方法大全
  20. Mysql的IFNULL 和NULLIF用法小结

热门文章

  1. boost::hana::fold_right用法的测试程序
  2. boost::gil模块实现带默认参数的框过滤器的测试程序
  3. ITK:创建拉普拉斯内核
  4. DCMTK:基本工作清单管理服务类提供程序基于一组文件作为数据源
  5. OpenCV gPhoto2 VideoCapture的用法(附完整代码)
  6. QDoc分组事物Grouping Things
  7. OpenGL无边界的纹理实例
  8. QML基础类型之matrix4x4
  9. 经典C语言程序100例之九四
  10. 经典C语言程序100例之八