Spring aspectjrt记录操作日志

操作日志:一般系统都需要日志来记录用户操作情况,我们也称为操作日志,当然了,也有做系统日志的(前端就可以做);但是对用户来说操作日志更加容易读懂。要记录操作日志有两种方法,第一种就是手动添加操作日志,在每个方法里面手动添加操作日志,这样做的好处就是日志可以做的很精准,当然这样工作量也比较大,一般的增删改什么的都需要日志来记录,每个方法都调用一次操作日志很繁琐;第二种方法就是Spring面向切面的AOP,今天就来实践一下AOP。
首先你想要知道的是:jar包
一般来说需要这两个就够了:aspectjweaver.jar,aspectjrt.jar 有些需要cglib.jar 。
maven项目的话直接添加就OK了,或者自己去下载然后导入jar包都可以

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.10</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>
        <!--<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>-->

让我们首先了解一下AOP
spring切面可以应用5中类型的通知:

  • Before —— 在方法被调用之前调用通知;
  • After —— 在方法完成之后调用通知,无论方法执行是否成功;
  • AfterReturning —— 在方法成功执行之后调用通知;
  • AfterThrowing —— 在方法抛出异常后调用通知;
  • Around —— 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为;

连接点(Joinpoint)

连接点是在应用执行过程中能够插入切面的一个点。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

切点(Pointcut)

切点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称模式来指定这些切点。
切点用于准确定位应该在什么地方应用切面的通知。

切面(Aspect)

切面是通知和切点的结合。通知和切点共同定义了关于切面的全部内容——它是什么,在何时和何处完成其他功能。

引入(Introduction)

引入允许我们向现有的类添加新方法或属性,通过通知类,从而可以在无需修改现有的类的情况下,让它们具有新的行为和状态。
参见元素的使用,引入新的接口行为。

织入(Weaving)

织入是将切面应用到目标对象来创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中(注入前置、后置通知等)。

使用时只需要在class上注解@Aspect就可以了,如下:

@Aspect
public class OperaLog {}

现在来说怎么实现记录日志操作, 上面提到比如Before —— 在方法被调用之前调用通知; After —— 在方法完成之后调用通知,无论方法执行是否成功;看到这里大家也都知道了,就是在方法执行前后添加操作日志,就是监听方法,从而参数,返回值,方法名,请求方式等等来记录日志。
我们就拿AfterReturning来说:

@Aspect
public class OperaLog {
    @AfterReturning(value ="within(com.nikoyo.dms.rest.*Controller)", returning = "returnValue")
    public  void AfterTitle(JoinPoint joinpoint, Object returnValue) throws Exception {

    }
}

大家可以要问:“winthin()”是什么?该怎么用?returning = “returnValue”是什么?
大致来讲就是winthin()监听一些特定的方法,returning 就是返回值;这是一些常见的切入点的例子 :

  • execution(public * * (. .)) 任意公共方法被执行时,执行切入点函数。
  • execution( * set*(. .)) 任何以一个“set”开始的方法被执行时,执行切入点函数。
  • execution( * com.demo.service.AccountService.* (. .)) 当接口AccountService 中的任意方法被执行时,执行切入点函数。
  • execution( * com.demo.service.. (. .)) 当service包中的任意方法被执行时,执行切入点函数。
  • within(com.demo.service.*) 在service 包里的任意连接点。
  • within(com.demo.service. .*) 在service 包或子包的任意连接点。
  • this(com.demo.service.AccountService) 实现了AccountService 接口的代理对象的任意连接点。
  • target(com.demo.service.AccountService) 实现了AccountService
    接口的目标对象的任意连接点。
  • args(Java.io.Serializable) 任何一个只接受一个参数,且在运行时传入参数实现了 Serializable接口的连接点。

需要的一些方法:

//遍历请求参数 可以和下面的adminOptionContent()方法合用
for (int i = 0; i < joinpoint.getArgs().length; i++) {
    Object o = joinpoint.getArgs()[i];
}


/**
 * 使用Java反射来获取被拦截方法(insert、update)的参数值,
 * 将参数值拼接为操作内容
 */
public String adminOptionContent(Object[] args, String mName) throws Exception {
        if (args == null) {
            return null;
        }
        StringBuffer rs = new StringBuffer();
        rs.append(mName);
        String className = null;
        int index = 1;
        // 遍历参数对象
        for (Object info : args) {
            //获取对象类型
            className = info.getClass().getName();
            className = className.substring(className.lastIndexOf(".") + 1);
            rs.append("[参数" + index + ",类型:" + className + ",值:" + info);

            // 获取对象的所有方法
            Method[] methods = info.getClass().getDeclaredMethods();
            // 遍历方法,判断get方法
            for (Method method : methods) {
                String methodName = method.getName();
                // 判断是不是get方法
                if (methodName.indexOf("get") == -1) {// 不是get方法
                    continue;// 不处理
                }
                Object rsValue = null;
                try {
                    // 调用get方法,获取返回值
                    rsValue = method.invoke(info);
                    if (rsValue == null) {//没有返回值
                        continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                //将值加入内容中
                rs.append("(" + methodName + " : " + rsValue + ")");
            }
            rs.append("]");
            index++;
        }
        return rs.toString();
    }

/***
* 获取客户端ip地址(可以穿透代理)
* @param request
* @return
*/
public static String getClientIpAddr(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.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_VIA");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("REMOTE_ADDR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

前面说的AfterReturning是成功之后执行的,如果异常或者失败呢?操作日志就得用AfterThrowing。

@AfterThrowing(value = "within(com.nikoyo.dms.rest.*Controller)",throwing = "e")
public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {

   String params = "";
   if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {
       for ( int i = 0; i < joinPoint.getArgs().length; i++) {
           params += joinPoint.getArgs()[i] + ";";
       }
   }

System.out.println(("异常方法:异常信息:{}参数:{}"+ joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName()+e.getClass().getName()+e.getMessage()+params));

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值