利用Spring AOP为项目增加日志追踪

"Spring AOP "

Posted by 王晓东 on August 22, 2019

需求

给项目加上日志追踪


代码

废话不多说直接放码

日志实体类

package xx.xx.xx.xx.log.entity;

import java.util.Date;

public class NcmLog{
	private static final long serialVersionUID = 1L;
	private String username;		// 用户名
	private String module;		// 执行模块
	private String methods;		// 执行方法
	private String description; //操作描述
	private String content;		// 操作内容
	private String actionurl;		// 请求url
	private String ip;		// IP地址
	private Date date;		// 执行时间
	private String commite;		// 执行描述(1:执行成功、2:执行失败)
	private String show;		// 是否显示(1:显示2:不显示)

}

日志注解

package xx.xx.xx.xx.log.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OptionalLog {
    String module()  default ""; //模块名
    String methods()  default "";//方法名
    String description() default "";//执行描述
    boolean show() default true;//是否显示
}

自定义Spring aop方法(类似拦截器)

package xx.xx.xx.xx.log.web;

import com.alibaba.fastjson.JSONObject;
import xx.xx.xx.xx.context.ThreadLocalUtil;
import xx.xx.xx.xx.annotation.OptionalLog;
import xx.xx.xx.xx.entity.NcmLog;
import xx.xx.xx.xx.service.NcmLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
public class LogAopAction {
    // 注入service,用来将日志信息保存在数据库
    @Resource(name = "ncmLogService")
    private NcmLogService logService;

    // 配置接入点,即为所要记录的action操作目录

    @Pointcut("execution(* gov.cnao.ac.ncm.*.web..*.*(..))")
    private void controllerAspect() {
    }

    @Around("controllerAspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //日志实体对象
        NcmLog logBo = new NcmLog();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        // 获取用户名
        String username = "";
        if (ThreadLocalUtil.getContextUser() == null){
            return null;
        } else {
            username = ThreadLocalUtil.getContextUser().getUserName();
        }
        logBo.setUsername(username);
       //获取系统当前时间
        logBo.setDate(new Date());

        // 获取访问真实IP
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= inet.getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }

        logBo.setIp(ipAddress);

        // 拦截的实体类,就是当前正在执行的controller
        Object target = pjp.getTarget();
        // 拦截的方法名称。当前正在执行的方法
        String methodName = pjp.getSignature().getName();
        // 拦截的方法参数
        Object[] args = pjp.getArgs();

        //获取请求路径
        String actionUrl = request.getRequestURI();

        // 拦截的放参数类型
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Class[] parameterTypes = msig.getMethod().getParameterTypes();

        Object object = null;
        // 获得被拦截的方法
        Method method = null;


        try {
            method = target.getClass().getMethod(methodName, parameterTypes);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        if (null != method) {
            // 获取方法(此为自定义注解)
            OptionalLog op = method.getAnnotation(OptionalLog.class);

            // 获取注解的modules 设为操作模块
            logBo.setModule(op.module());
            // 获取注解的methods 设为执行方法
            logBo.setMethods(op.methods());
            logBo.setShow(op.show()?"1":"2");
            logBo.setDescription(op.description());
            // 将上面获取到的请求路径 设为请求路径
            logBo.setActionurl(actionUrl);
            try {
                object = pjp.proceed();
                //接受客户端的数据
                Map<String,String[]> map = request.getParameterMap();
                // 解决获取参数乱码
                Map<String,String[]> newmap = new HashMap<String,String[]>();
                for(Map.Entry<String, String[]> entry : map.entrySet()){
                    String name = entry.getKey();
                    String values[] = entry.getValue();
                    if(values==null){
                        newmap.put(name, new String[]{});
                        continue;
                    }
                    String newvalues[] = new String[values.length];
                    for(int i=0; i<values.length;i++){
                        String value = values[i];
                        value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
                        newvalues[i] = value; //解决乱码后封装到Map中
                    }

                    newmap.put(name, newvalues);

                }
                logBo.setContent(JSONObject.toJSONString(newmap));
                //1为执行成功
                logBo.setCommite("1");
                // 添加到数据库
                logService.save(logBo);
            } catch (Throwable e) {

                //接受客户端的数据
                Map<String,String[]> map = request.getParameterMap();
                // 解决获取参数乱码
                Map<String,String[]> newmap = new HashMap<String,String[]>();
                for(Map.Entry<String, String[]> entry : map.entrySet()){
                    String name = entry.getKey();
                    String values[] = entry.getValue();

                    if(values==null){
                        newmap.put(name, new String[]{});
                        continue;
                    }
                    String newvalues[] = new String[values.length];
                    for(int i=0; i<values.length;i++){
                        String value = values[i];
                        value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
                        newvalues[i] = value; //解决乱码后封装到Map中
                    }

                    newmap.put(name, newvalues);

                }
                //MapperUtil.toJsonStr为自定义的转换工具类
                logBo.setContent(JSONObject.toJSONString(newmap));
                //2为执行失败
                logBo.setCommite("2");
                 //添加到数据库
                logService.save(logBo);
            }
        }
        return object;
    }
}

spring-mvc.xml中注入bean

<!-- 操作日志配置 指定扫描aop执行操作的类 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="logAopAction" class="xx.xx.xx.xx.log.web.LogAopAction"/>

在请求上加上@OptionalLog注解

@OptionalLog(description="查看行政区划列表")
@RequestMapping(value = "list")
public String list() {
    return "areas/areasList";
}