Aop实现数据操作增删改查日志记录
1 功能需求
需要一个日志监控系统,监控每天数据操作记录
2实现通过aop after-returning 实现配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 声明目标对象 --> <!-- 声明切面,但此时没有灵魂 --> <bean id="logmonitorORG" class=" com.qb.modules.organtrans.logmonitor.LogMonitorAfterReturnInterceptor"></bean> <!-- 声明切面的所有操作,放置到aop:config中 --> <aop:config> <!-- 定义切面的类,也就是说将切面注入灵魂 --> <aop:aspect id="logmonitorORGS" ref="logmonitorORG"> <!-- aop:pointcut:声明切入点,对目标对象连接点的某些方法进行进行拦截 * id:为切入点起个名称 * expression:表达式:使用表达式定义哪些方法要被定义成切入点 注意:只有通知在切入点的范围之内,才能对通知定义的方法进行拦截,通过代理访问目标对象 如果通知不再切入点的范围之内,不能对通知定义的方法进行拦截,直接访问目标对象 --> <aop:pointcut id="save" expression="execution(* com.qb.modules.organtrans.contractinfo.service.ContractInfoService.save*(..))"/> <aop:pointcut id="insert" expression="execution(* com.qb.modules.organtrans.contractinfo.service.ContractInfoService.insert*(..))"/> <aop:pointcut id="update" expression="execution(* com.qb.modules.organtrans.contractinfo.service.ContractInfoService.update*(..))"/> <!-- 将切入点关联通知 aop:after:最终通知,无论访问目标对象切入点方法之后是否出现异常的时候,都会触发通知,处理业务逻辑 * pointcut-ref:通知关联切入点,将切入点注入 * method:切面中的定义的通知 --> <!-- <aop:after pointcut-ref="insert" method="logMonitorInsertOperate"/> <aop:after pointcut-ref="update" method="logMonitorUpdateOperate"/> --> <aop:after-returning pointcut-ref="insert" method="logMonitorInsertOperate" returning="returnValue"/> <aop:after-returning pointcut-ref="save" method="logMonitorInsertOperate" returning="returnValue"/> <aop:after-returning pointcut-ref="update" method="logMonitorUpdateOperate" returning="returnValue"/> </aop:aspect> </aop:config> </beans>
3java代码
public void logMonitorInsertOperate(JoinPoint joinPoint, Object returnValue) { System.out.println("@afterReturning:目标方法为insert:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); System.out.println("@afterReturning:参数为:" + Arrays.toString(joinPoint.getArgs())); System.out.println("@afterReturning:被织入的目标对象为:" + joinPoint.getTarget()); // 新增记录日志 //获取目标方法名 joinPoint.getSignature().getName() if (joinPoint.getSignature().getName().toString().toLowerCase().indexOf("oms") <= -1) { //joinPoint.getArgs()方法参数,根据参数判断执行的操作 if (joinPoint.getArgs() != null) { for (Object s : joinPoint.getArgs()) { if (s instanceof ContractInfoDTO) { ContractInfoDTO dto = (ContractInfoDTO) s; String data = "{\"database\":\"" + "ORGANTRANS" + "\",\"orgid\":\"" + dto.getCoorgCode() + "\",\"productid\":\"" + dto.getProductCode() + "\",\"datapoint\":" + "\"" + INSERT + "\",\"datetime1\":\"" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\"," + "\"count\":" + "1" + ",\"status\":\"" + 1 + "\"}"; monitor_logger.warn(data); break; } } } } }
public void logMonitorUpdateOperate(JoinPoint joinPoint, Object returnValue) { // System.out.println("@afterReturning:update"); System.out.println("@afterReturning:目标方法为:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); System.out.println("@afterReturning:参数为:" + Arrays.toString(joinPoint.getArgs())); for (Object s : joinPoint.getArgs()) { if (s instanceof ContractInfoDTO) { singleContractInfo(s); } else if (s instanceof List) { listContractInfo(s); } else if (s instanceof String) { singleContractInfo(s); } else if (s instanceof Map) { // 目前没有这样的 如果有也只可能是 dto 里面是 实体类 Map map = (Map) s; if (map.containsKey("dto")) { singleContractInfo(map.get("dto")); } } } System.out.println("@afterReturning:被织入的目标对象为:" + joinPoint.getTarget()); }
private void singleContractInfo(Object s) { String operType = "", status = "", loanStatus = ""; String productCode = "", orgCode = ""; if (s instanceof ContractInfoDTO) { ContractInfoDTO dto = (ContractInfoDTO) s; loanStatus = dto.getLoanStatus(); productCode = dto.getProductCode(); orgCode = dto.getCoorgCode(); } else if (s instanceof String) { loanStatus = s.toString(); } boolean isWrite = true; if (StringUtils.isNotBlank(loanStatus)) { if (StringUtils.equals(Constants.LoanStatusEnum.AUDIT_PASS.getCode(), loanStatus)) { operType = ADUIT; status = "1"; } else if (StringUtils.equals(Constants.LoanStatusEnum.AUDIT_NO.getCode(), loanStatus)) { operType = ADUIT; status = "0"; } else if (StringUtils.equals(Constants.LoanStatusEnum.RAISE_FAIL.getCode(), loanStatus)) { operType = RAISE; status = "0"; } else if (StringUtils.equals(Constants.LoanStatusEnum.RAISE_SUCCESS.getCode(), loanStatus)) { operType = RAISE; status = "1"; } else if (StringUtils.equals(Constants.LoanStatusEnum.LOAN_FAIL.getCode(), loanStatus)) { operType = LOAN; status = "0"; } else if (StringUtils.equals(Constants.LoanStatusEnum.LOAN_SUCCESS.getCode(), loanStatus)) { operType = LOAN; status = "1"; } else { // 非借据贷款状态 isWrite = false; } if (isWrite) { String data = "{\"database\":\"" + "ORGANTRANS" + "\",\"orgid\":\"" + orgCode + "\",\"productid\":\"" + productCode + "\",\"datapoint\":" + "\"" + operType + "\",\"datetime1\":\"" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\"," + "\"count\":" + "1" + ",\"status\":\"" + status + "\"}"; logger.error(data); logger.info(data); monitor_logger.warn(data); } } }
4总结
advice(通知)注解的执行先后顺序
AfterReturn 并不是方法一定有返回值的时候才会触发这个界面,而是方法正常执行只要不报异常aop就会拦截该方法。
@Before
前置通知(Before advice) :在某连接点(JoinPoint)——核心代码(类或者方法)之前执行的通知
@After
后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
@AfterReturning
返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。
@Around
环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。这时aop的最重要的,最常用的注解。用这个注解的方法入参传的是ProceedingJionPoint pjp,可以决定当前线程能否进入核心方法中——通过调用pjp.proceed();
@AfterThrowing
抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
执行到核心业务方法或者类时,会先执行AOP。在aop的逻辑内,先走@Around注解的方法。然后是@Before注解的方法,然后这两个都通过了,走核心代码,核心代码走完,无论核心有没有返回值,都会走@After方法。然后如果程序无异常,正常返回就走@AfterReturn,有异常就走@AfterThrowing。