基于AOP+Filter实现的全局日志记录

公司让做全局日志,记录下用户的行为操作,想了下用aop来实现,拦截的是web层,本来使用的配置文件,结果报错了,后来改成注解开发,proxy-target-class="true"  proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理

因为有些请求的方法需要过滤的,不去记录,所以使用了自定义注解去实现

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 审计日志方法过滤的自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AnnotationAudit {
}

因为aop无法获得response响应的内容,只好加上filter一起来实现这个功能

通过 HttpServletResponseWrapper 获得response响应的内容

package com.*.*.base.audit;


import com.*.*.base.action.GainetBaseAction;
import com.*.*.base.service.GainetBaseService;
import com.opensymphony.xwork2.Action;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;


/**
 * [基于AOP切面的日志自定义类]
 * @author xuegengjia
 * @date 10:29 2019/3/22
 */
@Aspect
public class AuditLogAop extends GainetBaseAction{
    Logger logger = Logger.getLogger(AuditLogAop.class);

    //aop拦截规则
    @Pointcut("execution (* cn.包名.*.action..*.*(..))")
    private void logMethod(){
    }

    public static final ThreadLocal<AuditLogVo> threadLocal= new ThreadLocal<>();


    /**
     * [前置通知,方法调用前触发]
     * @author xuegengjia
     * @date 10:01 2019/3/22
     */
    @Before("logMethod()")
    public void doBefore(JoinPoint joinPoint) {
        // 记录方法开始执行的时间
    }

    /**
     * [后置通知,方法调用后触发]
     * @author xuegengjia
     * @date 10:02 2019/3/22
     * @param
     * @return
     */
    @After("logMethod()")
    public void doAfter(JoinPoint joinPoint) {
        // 记录方法执行完成的时间
        //获取执行的目标方法
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature =(MethodSignature)signature;
        Method method = methodSignature.getMethod();
        //获得目标方法上的注解
        AnnotationAudit AnnotationAudit = method.getAnnotation(AnnotationAudit.class);
        //判断请求是跳转页面还是返回参数
        if (!threadLocal.get().getReqPath().endsWith(".jsp") && !threadLocal.get().getReqMethod().contains(".set") && AnnotationAudit == null
                && (threadLocal.get().getResult() == null || threadLocal.get().getResult() == Action.NONE)) {
            if ((AuditLogVo) threadLocal.get() != null) {
                threadLocal.get().setNeedRecord(AuditLogVo.NEED_RECORD);
            }
        }

    }

    /**
     * [环绕通知,围绕着整个方法]
     * @author xuegengjia
     * @date 10:03 2019/3/22
     * @param
     * @return
     */
    @Around("logMethod()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        AuditLogVo auditLogVo = new AuditLogVo();
        auditLogVo.setReqPath(getRequest().getRequestURI());
        auditLogVo.setReqId(随机数);
        auditLogVo.setReqMethod(pjp.getSignature().toString().substring(6).trim());//方法名
        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();
        auditLogVo.setResult(result);
        threadLocal.set(auditLogVo);
        return result;
    }

    @AfterThrowing(value = "logMethod()",throwing = "ex")
    public void AfterThrowingMethod(JoinPoint joinpoint,Exception ex){
        String methodName = joinpoint.getSignature().getName();
        logger.info("方法:"+methodName+"异常,异常原因:"+ex);
    }

}
import org.apache.log4j.Logger;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * [审计日志过滤器]
 * @author xuegengjia
 * @date 17:38 2019/4/2
 * @param
 * @return
 */
public class GainetAuditLogFilter  implements Filter {
   Logger logger =Logger.getLogger(GainetAuditLogFilter.class);

   @Override
   public void init(FilterConfig filterConfig) throws ServletException {

   }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         ServletOutputStream out = response.getOutputStream();
         ThreadLocal<AuditLogVo> thisThreadLocal = AuditLogAop.threadLocal;
         response.setCharacterEncoding("utf-8");
         Date date = new Date();
         WrapperedResponse wrapResponse = new WrapperedResponse((HttpServletResponse)response);
         chain.doFilter(request, wrapResponse);
         //获取响应内容
         byte[] data = wrapResponse.getResponseData();
      try {

         AuditLogVo auditLogVo=thisThreadLocal.get();
         if(auditLogVo !=null && auditLogVo.getNeedRecord() ==AuditLogVo.NEED_RECORD){
            HttpServletRequest req =(HttpServletRequest)request;
            AuditLog auditLog =new AuditLog();
            /**
             *开始封装参数持久化数据
             */
       
         }
      } catch (Exception e) {
         logger.error("审计日志过滤器错误");
         logger.error(e.getMessage(),e);
      }finally {
         thisThreadLocal.remove();
         out.write(data);
         out.flush();
      }
   }

   @Override
   public void destroy() {

   }

}
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

public class WrapperedResponse extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer = null;
    private ServletOutputStream out = null;
    private PrintWriter writer = null;

    public WrapperedResponse(HttpServletResponse resp) throws IOException {
        super(resp);
        buffer = new ByteArrayOutputStream();// 真正存储数据的流
        out = new WapperedOutputStream(buffer);
        writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
    }

    /**重载父类获取outputstream的方法*/
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    /**重载父类获取writer的方法*/
    @Override
    public PrintWriter getWriter() throws UnsupportedEncodingException {
        return writer;
    }

    /**重载父类获取flushBuffer的方法*/
    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
        if (writer != null) {
            writer.flush();
        }
    }

    @Override
    public void reset() {
        buffer.reset();
    }

    /**将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据*/
    public byte[] getResponseData() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    /**内部类,对ServletOutputStream进行包装*/
    private class WapperedOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos = null;

        public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
            bos = stream;
        }

        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }
        @Override
        public void write(byte[] b) throws IOException {
            bos.write(b, 0, b.length);
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener writeListener) {

        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42125810/article/details/88747501