Spring AOP 实现业务和异常日志记录实战
2018-09-19 15:04
1006 查看
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方式,拦解注解的日志 * 后置通知:如果需要访问其他建议类型的连接点上下文,则应使用JoinPoint参数类型而不是ProceedingJoinPoint。 */ @Pointcut(value = "@annotation(com.hec.dup.facade.mgr.annotation.OperateLogAnnotation)") public void logAspect() { } @After("logAspect()") public Object recordLog(JoinPoint point) throws Throwable { //先执行业务 Object result = point.proceed(); 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()); } } }*/ Str 5ac ingBuilder 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(); //获取为null logmsg = 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( Operat 3900 eLogAnnotation.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() );//用户ID operaLog.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() ); //操作表名 JSONArray paramArray = JSONObject.parseArray( params.toString() ); //获取第一个参数的id operaLog.setTableId( paramArray.getJSONObject( 0 ).getString( "id" ) ); operaLog.setOperateContent( annotation.operateContent() ); //操作内容 return operaLog; } }
2.3 定义applicationContext.xml配置
2.4 接口添加注解,接口被调用的时候会调用被aop处理
@OperateLogAnnotation(moduleType = "01") //这边添加切入点接口的注解 @RequestMapping(value = "testAop01") @ResponseBody public 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 的执行顺序
相关文章推荐
- Spring AOP 实现业务日志记录
- Spring AOP 实现业务日志记录
- 利用Spring AOP实现业务和异常日志记录
- WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)
- 从壹开始前后端分离 [.netCore 不定期更新 ] 三十五║ 完美实现全局异常日志记录
- Spring AOP 实现业务日志记录 (注解)
- C++实现一个简单的异常日志记录类
- spring Aop 之用户操作日志以及异常处理日志记录
- SpringBoot+Maven项目实战(6):整合Log4j和Aop,实现简单的日志记录
- Android实现将日志、异常记录到本地的方法
- MVC4.0 利用HandleErrorAttribute和log4net实现记录异常日志功能
- spring aop 实现用户操作日志记录功能(转)
- Asp.Net Core 2.0 项目实战(9) 日志记录,基于Nlog或Microsoft.Extensions.Logging的实现及调用实例
- springAOP实现的异常日志记录+异常邮件发送+权限控制
- Spring + Aop+注解 集成使用 Log4j,实现异常日志记录
- Spring AOP 基于注解的AOP 实现 记录日志,异常捕获
- spring Aop 之用户操作日志以及异常处理日志记录
- 全局异常处理器实现系统异常日志记录到数据库
- 请教.Net平台大型业务系统中“日志记录” 的较好解决办法,类似Java下用AOP方式实现的最好
- Spring+Log4j+ActiveMQ实现远程记录日志——实战+分析