- 浏览: 205291 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
jongde1:
Axure太难学了,分享mockplus工具,有兴趣可以去了解 ...
Axure RP 原型设计工具 -
di1984HIT:
这里面提到了好几种解决办法。
Spring AOP对日志记录、Exception日志记录 -
di1984HIT:
学习一下。
spring struts2 零配置 -
di1984HIT:
不错,不错啊
Struts2防止表单重复提交 -
di1984HIT:
kettle怎么样啊。
Kettle初探
Spring 自定义注解实现操作日志记录功能
最近项目组长分配给我一个任务,让我完成一个操作日志的管理功能。需求是这样的:项目很大,有好几个子系统,而且这些子系统已经都在开发过程中了,都进行了大半了。现在要实现的操作日志管理是要将用户在登录系统后所做的重要操作记录下来并查询。记录的内容包括操作人的相关信息(比如:用户ID,用户名,用户IP地址,所属机构等)和所执行的操作的相关信息(比如:所属模块名称、类名、方法名、参数、是否操作成功、描述信息和错误信息)。操作日志查询功能没有什么可说的,难点是在于操作日志的记录,首先要考虑到的是日志的记录不能或要尽量少地让其他人改动自己的代码;其次要考虑到日志记录的灵活性。因此我采用了注解的方式来实现,只要将注解添加到想要记录日志的方法体上就可以实现操作日志的记录。
实现步骤如下:
第一步:启用@AspectJ支持。
在你的工程lib目录下添加aspectjweaver.jar和aspectjrt.jar并在spring 的配置文件中添加如下代码:
这一步就完成了@AspectJ的支持,从而可以实现通过注解方式将通知编织到非公共方法中。
第二步:编写自定义注解。实现对方法所实现的功能进行描述,以便在通知中获取描述信息,代码非常简单,如下:
第三步:编写操作日志切面通知实现类。
在编写切面通知实现类之前,我们需要搞清楚我们需要哪些通知类型,是前置通知、后置通知、环绕通知或异常通知?根据我的需求,我们知道我们记录的操作日志有两种情况,一种是操作成功,一种是操作失败。操作成功时则方法肯定已经执行完成,顾我们需要实现一个后置通知;操作失败时则说明方法出现异常无法正常执行完成,顾还需要一个异常通知。因此我们就需要实现这两个通知即可。代码如下:
大家看上面的代码会发现这两个方法体:
这两个方法体分别是后置通知和异常通知的实现。它们有两个相同的参数jp和rl,jp是切点对象,通过该对象可以获取切点所切入方法所在的类,方法名、参数等信息,具体方法可以看方法体的实现;rl则是我们的自定义注解的对象,通过该对象我们可以获取注解中参数值,从而获取方法的描述信息。在异常通知中多出了一个ex参数,该参数是方法执行时所抛出的异常,从而可以获取相应的异常信息。此处为我写的自定义异常。注意:如果指定异常参数,则异常对象必须与通知所切入的方法体抛出的异常保持一致,否则该通知不会执行。
addLogSuccess方法签名上的@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")注解,是指定该方法体为后置通知,其有一个表达式参数,用来检索符合条件的切点。该表达式指定com/abchina/irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体为切点。
addLog方法签名上的@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")注解,是指定方法体为异常通知,其有一个表达式参数和一个抛出异常参数。表达式参数与后置通知的表达式参数含义相同,而抛出异常参数,则表示如果com/abchina/irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体在执行时抛出BusinessException异常时该通知便会执行。
注意:切面通知实现类是一个普通的pojo对象,如果要想指定其为通知对象,则需在其类名上添加@Aspect注解
第四步:在spring配置文件中创建通知bean对象。
第五步:使用操作日志记录注解。
通过以上四步操作后,操作日志的记录功能就算完成了,那我们该如何使用呢?很简单!在com/abchina/irms目录下及其所有子目录下任意找到一个service层的某个类的方法,在其方法体上添加@rmpfLog(desc=”描述信息”)即可。代码如下:
最近项目组长分配给我一个任务,让我完成一个操作日志的管理功能。需求是这样的:项目很大,有好几个子系统,而且这些子系统已经都在开发过程中了,都进行了大半了。现在要实现的操作日志管理是要将用户在登录系统后所做的重要操作记录下来并查询。记录的内容包括操作人的相关信息(比如:用户ID,用户名,用户IP地址,所属机构等)和所执行的操作的相关信息(比如:所属模块名称、类名、方法名、参数、是否操作成功、描述信息和错误信息)。操作日志查询功能没有什么可说的,难点是在于操作日志的记录,首先要考虑到的是日志的记录不能或要尽量少地让其他人改动自己的代码;其次要考虑到日志记录的灵活性。因此我采用了注解的方式来实现,只要将注解添加到想要记录日志的方法体上就可以实现操作日志的记录。
实现步骤如下:
第一步:启用@AspectJ支持。
在你的工程lib目录下添加aspectjweaver.jar和aspectjrt.jar并在spring 的配置文件中添加如下代码:
<aop:aspectj-autoproxy/>
这一步就完成了@AspectJ的支持,从而可以实现通过注解方式将通知编织到非公共方法中。
第二步:编写自定义注解。实现对方法所实现的功能进行描述,以便在通知中获取描述信息,代码非常简单,如下:
package com.abchina.rmpf.logmng.ann; import java.lang.annotation.Documented; 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) @Documented public @interface rmpfLog { String desc() default "无描述信息"; }
第三步:编写操作日志切面通知实现类。
在编写切面通知实现类之前,我们需要搞清楚我们需要哪些通知类型,是前置通知、后置通知、环绕通知或异常通知?根据我的需求,我们知道我们记录的操作日志有两种情况,一种是操作成功,一种是操作失败。操作成功时则方法肯定已经执行完成,顾我们需要实现一个后置通知;操作失败时则说明方法出现异常无法正常执行完成,顾还需要一个异常通知。因此我们就需要实现这两个通知即可。代码如下:
package com.abchina.rmpf.logmng.aop; import java.io.File; import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.util.ResourceUtils; import plantix.core.business.exception.BusinessException; import plantix.core.context.ThreadContext; import com.abchina.rmpf.privmng.web.vo.ActorVO; import com.abchina.rmpf.common.Constant; import com.abchina.rmpf.common.DateTool; import com.abchina.rmpf.logmng.ann.rmpfLog; import com.abchina.rmpf.logmng.service.ILogService; import com.abchina.rmpf.logmng.web.vo.LogVO; import com.opensymphony.xwork2.ActionContext; @Aspect //该注解标示该类为切面类 public class LogAspect { /** * LogService * @generated */ private ILogService logService; //标注该方法体为后置通知,当目标方法执行成功后执行该方法体 @AfterReturning("within(com.abchina.irms..*) && @annotation(rl)") public void addLogSuccess(JoinPoint jp, rmpfLog rl){ Object[] parames = jp.getArgs();//获取目标方法体参数 String params = parseParames(parames); //解析目标方法体的参数 String className = jp.getTarget().getClass().toString();//获取目标类名 className = className.substring(className.indexOf("com")); String signature = jp.getSignature().toString();//获取目标方法签名 String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("(")); String modelName = getModelName(className); //根据类名获取所属的模块 String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY); //用户IP ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY)); LogVO logvo = new LogVO(); logvo.setId(java.util.UUID.randomUUID().toString()); logvo.setClassname(className); logvo.setMethodname(methodName); logvo.setArgument(params); logvo.setMemo(rl.desc()); logvo.setModelname(modelName); logvo.setIp(ip); logvo.setOperationtime(DateTool.getDateTime4()); // logvo.setErr(""); logvo.setFlag("1"); if(actor!=null){ logvo.setOrgid(actor.getOrgcode()); logvo.setUserid(actor.getUserid()); logvo.setUsername(actor.getUsername()); } logService.insertLog(logvo); } //标注该方法体为异常通知,当目标方法出现异常时,执行该方法体 @AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex") public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){ Object[] parames = jp.getArgs(); String params = parseParames(parames); String className = jp.getTarget().getClass().toString(); className = className.substring(className.indexOf("com")); String signature = jp.getSignature().toString(); String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("(")); String modelName = getModelName(className); String ip = (String)ActionContext.getContext().getSession().get(Constant.THREAD_APP_IP_KEY); ActorVO actor = ((ActorVO)ActionContext.getContext().getSession().get(Constant.SESSION_ACTOR_KEY)); LogVO logvo = new LogVO(); logvo.setId(java.util.UUID.randomUUID().toString()); logvo.setClassname(className); logvo.setMethodname(methodName); logvo.setArgument(params); logvo.setMemo(rl.desc()); logvo.setModelname(modelName); logvo.setIp(ip); logvo.setOperationtime(DateTool.getDateTime4()); logvo.setErr(ex.toString());//记录异常信息 logvo.setFlag("0"); if(actor!=null){ logvo.setOrgid(actor.getOrgcode()); logvo.setUserid(actor.getUserid()); logvo.setUsername(actor.getUsername()); } logService.insertLog(logvo); } /** * 根据包名查询检索其所属的模块 * @param packageName 包名 * @return 模块名称 */ private String getModelName(String packageName){ String modelName = ""; SAXReader reader = new SAXReader(); try { //读取project.xml模块信息描述xml文档 File proj = ResourceUtils.getFile("classpath:project.xml"); Document doc = reader.read(proj); //获取文档根节点 Element root = doc.getRootElement(); //查询模块名称 modelName = searchModelName(root, packageName); } catch (Exception e) { e.printStackTrace(); } return modelName; } /** * 采用递归方式根据包名逐级检索所属模块 * @param element 元素节点 * @param packageName 包名 * @return 模块名称 */ private String searchModelName(Element element, String packageName){ String modelName = searchModelNodes(element, packageName); //若将包名解析到最后的根目录后仍未检索到模块名称,则返回空 if(packageName.lastIndexOf(".")==-1){ return modelName; } //逐级检索模块名称 if(modelName.equals("")){ packageName = packageName.substring(0, packageName.lastIndexOf(".")); modelName = searchModelName(element, packageName); } return modelName; } /** * 根据xml文档逐个节点检索模块名称 * @param element 节点元素 * @param packageName 包名 * @return 模块名称 */ @SuppressWarnings("unchecked") private String searchModelNodes(Element element, String packageName){ String modelName = ""; Element modules = element.element("modules"); Iterator it = modules.elementIterator(); if(!it.hasNext()){ return modelName; } while (it.hasNext()) { Element model = (Element) it.next(); String pack = model.attributeValue("packageName"); String name = model.elementText("moduleCHPath"); if(packageName.equals(pack)){ modelName = name; return modelName; } if(modelName!=null && !modelName.equals("")){ break; } modelName = searchModelNodes(model, packageName); } return modelName; } /** * 解析方法参数 * @param parames 方法参数 * @return 解析后的方法参数 */ private String parseParames(Object[] parames) { StringBuffer sb = new StringBuffer(); for(int i=0; i<parames.length; i++){ if(parames[i] instanceof Object[] || parames[i] instanceof Collection){ JSONArray json = JSONArray.fromObject(parames[i]); if(i==parames.length-1){ sb.append(json.toString()); }else{ sb.append(json.toString() + ","); } }else{ JSONObject json = JSONObject.fromObject(parames[i]); if(i==parames.length-1){ sb.append(json.toString()); }else{ sb.append(json.toString() + ","); } } } String params = sb.toString(); params = params.replaceAll("(\"\\w+\":\"\",)", ""); params = params.replaceAll("(,\"\\w+\":\"\")", ""); return params; } public ILogService getLogService() { return logService; } public void setLogService(ILogService logService) { this.logService = logService; } }
大家看上面的代码会发现这两个方法体:
@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)") public void addLogSuccess(JoinPoint jp, rmpfLog rl){…}
@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex") public void addLog(JoinPoint jp, rmpfLog rl, BusinessException ex){…}
这两个方法体分别是后置通知和异常通知的实现。它们有两个相同的参数jp和rl,jp是切点对象,通过该对象可以获取切点所切入方法所在的类,方法名、参数等信息,具体方法可以看方法体的实现;rl则是我们的自定义注解的对象,通过该对象我们可以获取注解中参数值,从而获取方法的描述信息。在异常通知中多出了一个ex参数,该参数是方法执行时所抛出的异常,从而可以获取相应的异常信息。此处为我写的自定义异常。注意:如果指定异常参数,则异常对象必须与通知所切入的方法体抛出的异常保持一致,否则该通知不会执行。
addLogSuccess方法签名上的@AfterReturning("within(com.abchina.irms..*) && @annotation(rl)")注解,是指定该方法体为后置通知,其有一个表达式参数,用来检索符合条件的切点。该表达式指定com/abchina/irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体为切点。
addLog方法签名上的@AfterThrowing(pointcut="within(com.abchina.irms..*) && @annotation(rl)", throwing="ex")注解,是指定方法体为异常通知,其有一个表达式参数和一个抛出异常参数。表达式参数与后置通知的表达式参数含义相同,而抛出异常参数,则表示如果com/abchina/irms目录下及其所有子目录下的所有带有@rmpfLog注解的方法体在执行时抛出BusinessException异常时该通知便会执行。
注意:切面通知实现类是一个普通的pojo对象,如果要想指定其为通知对象,则需在其类名上添加@Aspect注解
第四步:在spring配置文件中创建通知bean对象。
<bean id="logAspect" class="com.abchina.rmpf.logmng.aop.LogAspect"> <property name="logService"> <ref local="com.abchina.rmpf.logmng.service.impl.LogServiceImpl"/> </property> </bean>
第五步:使用操作日志记录注解。
通过以上四步操作后,操作日志的记录功能就算完成了,那我们该如何使用呢?很简单!在com/abchina/irms目录下及其所有子目录下任意找到一个service层的某个类的方法,在其方法体上添加@rmpfLog(desc=”描述信息”)即可。代码如下:
@rmpfLog(desc="创建关联交易合同") @Transactional public void insertRtcont(RtcontVO rtcontVO) throws BusinessException { rtcontAL.insertRtcont(toRtcontDomain(rtcontVO)); }
发表评论
-
Replace constructor-arg by annotation
2013-05-24 04:34 1060replace constructor-arg by anno ... -
spring struts2 零配置
2013-05-23 18:04 6531.ssh(struts2.3.8+spring3.2+hei ... -
使用Spring进行统一日志管理 + 统一异常管理
2013-03-15 16:23 1175使用Spring进行统一日志管理 + 统一异常管理 统一日志 ... -
Spring 配置(事务和日志)
2013-03-15 16:11 1800Spring的那些配置(事务和日志) Spring整合Iba ... -
Spring AOP对日志记录、Exception日志记录
2013-03-14 18:14 6428Spring AOP对日志记录、Exception日志记录 ... -
Struts2防止表单重复提交
2013-03-14 10:18 866Struts2防止表单重复提交 最近开发中涉及到了表单重复提 ... -
Spring事务配置的五种方式
2013-03-05 10:08 670Spring事务配置的五种方式 前段时间对Spring的事 ... -
spring 异常与事务
2013-03-01 11:21 1761Service层捕获异常,并抛出RuntimeExceptio ... -
如何将基于 Struts、Spring 和 Hibernate 的应用从 Tomcat 迁移到 WebSphere Application Server
2012-12-21 10:28 1144引言 现在很多的企业都 ... -
Spring 事务管理高级应用难点剖析: 第 3 部分
2011-04-26 17:29 768Spring 事务管理高级应用 ... -
Spring 事务管理高级应用难点剖析: 第 2 部分
2011-04-26 17:20 887Spring 事务管理高级应用 ... -
Spring 事务管理高级应用难点剖析: 第 1 部分
2011-04-26 17:05 755Spring 事务管理高级应用难点剖析: 第 1 部分 简介 ...
相关推荐
4、想看spring aop 注解实现记录系统日志并入库等 二、能学到什么 1、收获可用源码 2、能够清楚的知道如何用spring aop实现自定义注解以及注解的逻辑实现 (需要知道原理的请看spring aop源码,此处不做赘述) 3、...
SpringMVC利用AOP实现自定义注解记录日志
下面小编就为大家分享一篇spring AOP自定义注解方式实现日志管理的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
自定义注解+Spring AOP实现记录用户操作日志-附件资源
springboot 脱敏自定义注解 SpringAOP
自定义注解日志技术: 在系统中使用自定义注解来实现日志记录,记录用户的操作行为、异常情况等重要信息,便于系统运维和故障排查。 图片上传和下载: 利用对象存储服务(OOS)来实现用户上传头像或者餐厅上传菜品图片...
Spring MVC AOP通过自定义注解方式拦截Controller等实现日志管理, springMVC里做添加AOP拦截,用于捕获异常。
本文我们通过Spring AOP和Java的自定义注解来实现日志的插入功能,非常不错,具有一定的参考借鉴价值,需要的朋友一起看看吧
后台通过自定义注解结合一个访问拦截器实现整个系统的权限控制 04. 系统前台采用全采用的Html+jQuery开发 05. 系统前台与后台的交互全部使用 Ajax 异步请求 06. 自定义 SecureValid 注解实现权限的控制 07. ...
几乎是网上 能找到的 日志脱敏的所有实现 1、基于正则表达式的 日志脱敏实现 ,扩展logback 、log4j 2、springmvc 返回报文脱敏。 3、基于注解方式的脱敏。 大家选择使用。
OpenFeign 有 4 种日志级别:NONE: 不记录任何日志,是OpenFeign默认日志级别(性能最佳,适用于生产环境)。BASIC: 仅记录请求方法、URL、响应状态码、执行时间(适用于生产环境追踪问题)。HEADERS: 在记录BASIC...
时间过的真快,转眼就一年了,没想到随手写的笔记会被这么多人浏览,不想误人子弟,于是整理了一个优化版。感谢各位同道的支持!
下面小编就为大家分享一篇aop注解方式实现全局日志管理方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
java springboot架构 自定义注解保存项目业务日志,使用线程池保存到数据库
NULL 博文链接:https://conkeyn.iteye.com/blog/2354644
利用自定义注解和spring aop和java反射机制生成用户能够读懂的日志记录。如:用户张三在2013年9月27日17:00执行了用户管理模块的用户删除功能参数为(编号:123456)各位可根据需要写入数据库或者保存到文件。
今天主要说说如何通过自定义注解的方式,在 Spring Boot 中来实现 AOP 切面统一打印出入参日志。小伙伴们可以收藏一波。 废话不多说,进入正题 ! 目录 一、先看看切面日志输出效果 二、添加 AOP Maven
自定义注解实现aop日志 自定义注解实现实体类参数校验 添加mybatis自定生成映射实体类、mapper等 添加全局异常处理 添加fegin自定义数据解析 20180907 添加注解,作为参数校验入口 20180910 解决服务之间调用fegin+...