时间:2021-05-19
需求背景
最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中。
为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求。
准备工作
自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号。
自定义注解
在项目下单独建立了一个log包,来存放日志相关的内容
**.common.log.annotation //自定义注解存放位置**.common.log.aop //aop工具类存放位置在annotation包下面新建自定义注解类:
package **.common.log.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)public @interface XXXOperateLog { /** * 操作类型描述 * @return */ String operateTypeDesc() default ""; /** * 操作类型 * @return */ long operateType() default -1; /** * 模块编码 * @return */ String moudleCode() default "M30"; /** * 模块名称 * @return */ String moudleName() default "XX模块"; /** * 业务类型 * @return */ String bussType() default ""; /** * 业务类型描述 * @return */ String bussTypeDesc() default "";}在aop包下新建XXXOperateLogAop
package **.common.log.aop;import ** ;//省略@Aspect@Componentpublic class XXXOperateLogAop{ @Autowired SystemLogService systemLogService; HttpServletRequest request = null; Logger logger = LoggerFactory.getLogger(XXXOperateLogAop.class); ThreadLocal<Long> time = new ThreadLocal<Long>(); //用于生成操作日志的唯一标识,用于业务流程审计日志调用 public static ThreadLocal<String> tag = new ThreadLocal<String>(); //声明AOP切入点,凡是使用了XXXOperateLog的方法均被拦截 @Pointcut("@annotation(**.common.log.annotation.XXXOperateLog)") public void log() { System.out.println("我是一个切入点"); } /** * 在所有标注@Log的地方切入 * @param joinPoint */ @Before("log()") public void beforeExec(JoinPoint joinPoint) { time.set(System.currentTimeMillis()); info(joinPoint); //设置日志记录的唯一标识号 tag.set(UUID.randomUUID().toString()); request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); } @After("log()") public void afterExec(JoinPoint joinPoint) { MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); logger.debug("标记为" + tag.get() + "的方法" + method.getName() + "运行消耗" + (System.currentTimeMillis() - time.get()) + "ms"); } //在执行目标方法的过程中,会执行这个方法,可以在这里实现日志的记录 @Around("log()") public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable { Object ret = pjp.proceed(); try { Object[] orgs = pjp.getArgs(); SystemLog valueReturn = null; for (int i = 0; i < orgs.length; i++) { if(orgs[i] instanceof SystemLog){ valueReturn = (SystemLog) orgs[i]; } } if(valueReturn==null){ valueReturn = new SystemLog(); } if(valueReturn!=null&&request!=null){ MethodSignature ms = (MethodSignature) pjp.getSignature(); Method method = ms.getMethod(); //获取注解的操作日志信息 XXXOperateLog log = method.getAnnotation(XXXOperateLog.class); String businessType = log.bussType(); String businessDesc = log.bussTypeDesc(); HashMap requestMap = ServletUtils.getParametersToHashMap(request) ; //从参数中寻找业务类型 if(businessType.equals("")) { Object objBusinessType = requestMap.get("business_type"); businessType = objBusinessType == null ? "" : objBusinessType.toString(); } //从执行结果的申请单中找业务类型 Object apply = request.getAttribute("apply") ; if(apply != null){ JSONObject obj = JSONFactory.toJSONAbstractEntity(apply); if(obj != null) { valueReturn.setOtherDesc("申请单号:"+obj.getString("apply_no")); if(businessType.equals("")) { businessType = obj.getString("business_type"); } } } //从方法的执行过程参数中找业务类型(一般是手动设置) if(businessType.equals("")) { businessType = (String) request.getAttribute("business_type"); businessType = businessType == null ? "" : businessType; } if(!businessType.equals("") && businessDesc.equals("")) { businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType); } valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType)); valueReturn.setBussTypeDesc(businessDesc); valueReturn.setMoudleCode(log.moudleCode()); valueReturn.setMoudleName(log.moudleName()); valueReturn.setOperateResult(XXXSysConstant.YesOrNo.YES); valueReturn.setOperateType(log.operateType()); valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId()); valueReturn.setOperateTypeDesc(log.operateTypeDesc()); valueReturn.setRequestIp(getRemoteHost(request)); valueReturn.setRequestUrl(request.getRequestURI()); valueReturn.setServerIp(request.getLocalAddr()); valueReturn.setUids(tag.get()); //保存操作日志 systemLogService.saveSystemLog(valueReturn); }else{ logger.info("不记录日志信息"); } //保存操作结果 } catch (Exception e) { e.printStackTrace(); } return ret; } //记录异常日志 @AfterThrowing(pointcut = "log()",throwing="e") public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { try { info(joinPoint); Object[] orgs = joinPoint.getArgs(); SystemLog valueReturn = null; for (int i = 0; i < orgs.length; i++) { if(orgs[i] instanceof SystemLog){ valueReturn = (SystemLog) orgs[i]; } } if(valueReturn==null){ valueReturn = new SystemLog(); } if(valueReturn!=null&&request!=null){ MethodSignature ms = (MethodSignature) joinPoint.getSignature(); Method method = ms.getMethod(); XXXOperateLog log = method.getAnnotation(XXXOperateLog.class); String businessType = log.bussType(); String businessDesc = log.bussTypeDesc(); if(businessType.equals("")) { Object objBusinessType = ServletUtils.getParametersToHashMap(request).get("business_type"); businessType = objBusinessType == null ? "" : objBusinessType.toString(); businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType); } valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType)); valueReturn.setBussTypeDesc(businessDesc); valueReturn.setMoudleCode(log.moudleCode()); valueReturn.setMoudleName(log.moudleName()); valueReturn.setOperateType(log.operateType()); valueReturn.setOperateTypeDesc(log.operateTypeDesc()); valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId()); valueReturn.setOperateResult(XXXSysConstant.YesOrNo.NO); String errMes = e.getMessage(); if(errMes!=null && errMes.length()>800){ errMes = errMes.substring(0, 800); } valueReturn.setErrorMessage(errMes); valueReturn.setRequestIp(getRemoteHost(request)); valueReturn.setRequestUrl(request.getRequestURI()); valueReturn.setServerIp(request.getLocalAddr()); valueReturn.setUids(tag.get()); systemLogService.saveSystemLog(valueReturn); }else{ logger.info("不记录日志信息"); } } catch (Exception e1) { e1.printStackTrace(); } } private void info(JoinPoint joinPoint) { logger.debug("--------------------------------------------------"); logger.debug("King:\t" + joinPoint.getKind()); logger.debug("Target:\t" + joinPoint.getTarget().toString()); Object[] os = joinPoint.getArgs(); logger.debug("Args:"); for (int i = 0; i < os.length; i++) { logger.debug("\t==>参数[" + i + "]:\t" + os[i].toString()); } logger.debug("Signature:\t" + joinPoint.getSignature()); logger.debug("SourceLocation:\t" + joinPoint.getSourceLocation()); logger.debug("StaticPart:\t" + joinPoint.getStaticPart()); logger.debug("--------------------------------------------------"); } /** * 获取远程客户端Ip * @param request * @return */ private String getRemoteHost(javax.servlet.http.HttpServletRequest request){ String ip = request.getHeader("x-forwarded-for"); if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getHeader("WL-Proxy-Client-IP"); } if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; } }修改配置文件spring-mvc.xml,添加如下配置
<!-- 开启AOP拦截 --> <aop:aspectj-autoproxy proxy-target-class="true" /> <mvc:annotation-driven /> <!-- 定义Spring描述Bean的范围 --> <context:component-scan base-package="**.common.log" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>需要注意的是,上述配置必须放在同一个xml文件里面,要么spring-mvc.xml,要么spring-context.xml,否则可能不生效,暂时还未查明是为什么。
注解的使用
@XXXOperateLog( bussType=XXXSysConstant.BUSINESS_TYPE.YYYY ,bussTypeDesc="业务类型描述" ,operateType = XXXSysConstant.LogOperateType.QUERY ,operateTypeDesc = "操作描述" ) @RequestMapping(value = "**/queryXXXXX4DataGrid.json", method = RequestMethod.POST) public void queryXXXXX4DataGrid(HttpServletRequest request, HttpServletResponse arg1, Model model, Writer writer) { logger.info("==========验票查询(出库)交易信息 开始====================="); try { //do something for business } catch (SystemException se) { throw se; } catch (BusinessException be) { throw be; } catch (Exception e) { throw new SystemException(e); } }以上这篇springMVC自定义注解,用AOP来实现日志记录的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用SpringAOP自定义注解形式实现日志管理。废话不多说,直接开始!!!关于配
springmvc自定义注解以及自定义注解的解析一、自定义注解名字@Target({ElementType.TYPE,ElementType.METHOD})/
本文介绍基于springboot和jdk8编写一个aop,结合自定义注解实现通用的接口参数校验。缘由目前参数校验常用的方法是在实体类上添加注解,但对于不同的方法
本文介绍了SpringBoot通过AOP和自定义注解实现权限控制,分享给大家,具体如下:源码:https://github.com/yulc-coding/ja
先看下这个问题的背景:假设有一个spring应用,开发人员希望自定义一个注解@Log,可以加到指定的方法上,实现自动记录日志(入参、出参、响应耗时这些)pack