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。

posted @ 2018-02-28 17:09  暖暖-木木  阅读(478)  评论(0编辑  收藏  举报