三种实现日志过滤器的方式 (过滤器 (Filter)、拦截器(Interceptors)和切面(Aspect))

时间:2021-04-16 18:25:51

1.建立RequestWrapper类

import com.g2.order.server.utils.HttpHelper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;


public class HttpServletRequestWrapper extends
        javax.servlet.http.HttpServletRequestWrapper {

    private final byte[] body;

    public HttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        System.out.println("-------------------------------------------------");
        Enumeration e = request.getHeaderNames()   ;
        while(e.hasMoreElements()){
            String name = (String) e.nextElement();
            String value = request.getHeader(name);
            System.out.println(name+" = "+value);

        }
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return true;
            }

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

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    @Override
    public String getHeader(String name) {
        return super.getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return super.getHeaderNames();
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return super.getHeaders(name);
    }

    public String getStringBody(){
        return new String(body);
    }
}

 

2.定义ResponseWrapper

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;

public class HttpServletResponseWrapper extends javax.servlet.http.HttpServletResponseWrapper {


    private ByteArrayOutputStream buffer = null;

    private ServletOutputStream out = null;

    private PrintWriter writer = null;


    public HttpServletResponseWrapper(HttpServletResponse response) throws IOException{
        super(response);

        buffer = new ByteArrayOutputStream();
        out = new WapperedOutputStream(buffer);
        writer = new PrintWriter(new OutputStreamWriter(buffer, "UTF-8"));
    }

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

    @Override
    public PrintWriter getWriter() throws IOException {
        return writer;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
        if (writer != null) {
            writer.flush();
        }
    }

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

    public String getResponseData(String charset) throws IOException {
        flushBuffer();//将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
        byte[] bytes = buffer.toByteArray();
        try {
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            return "";
        }

    }


    //内部类,对ServletOutputStream进行包装,指定输出流的输出端

    private class WapperedOutputStream extends ServletOutputStream {

        private ByteArrayOutputStream bos = null;

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

        //将指定字节写入输出流bos
        @Override
        public void write(int b) throws IOException {
            bos.write(b);
        }

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

        @Override
        public void setWriteListener(WriteListener listener) {

        }
    }
}

 

这里有三种实现方式 (过滤器 (Filter)、拦截器(Interceptors)和切面(Aspect))

3.1 建立过滤器

import com.g2.order.server.api.HttpServletRequestWrapper;
import com.g2.order.server.api.HttpServletResponseWrapper;
import com.g2.order.server.utils.HttpHelper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

 
@WebFilter(filterName = "accessLog", urlPatterns = "/*")
public class AccessLogFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(AccessLogFilter.class);

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

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequestWrapper requestWrapper;
        if (request instanceof HttpServletRequestWrapper) {
            requestWrapper = (HttpServletRequestWrapper) request;
        } else {
            requestWrapper = new HttpServletRequestWrapper((HttpServletRequest) request);
        }

        HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper((HttpServletResponse) response);
        chain.doFilter(requestWrapper, responseWrapper);
        String result = responseWrapper.getResponseData(response.getCharacterEncoding());
        response.getOutputStream().write(result.getBytes());
        logger.info("请求值链接:{},method:{},body:{},header:{},response:{}"
                , requestWrapper.getRequestURI()
                , requestWrapper.getMethod()
                , requestWrapper.getStringBody()
                , HttpHelper.getStringHeaders(requestWrapper)
                , result);
    }

    @Override
    public void destroy() {

    }
}

 

 

4.启动类

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * 程序入口
 */
@SpringBootApplication
@ServletComponentScan
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

 

5.建立controller

import com.g2.order.dao.mapper.user.UserMapper;
import com.g2.order.server.vo.user.UserLoginReq;
import com.g2.order.dao.model.user.UserDao;
import com.g2.order.server.vo.user.UserLoginResp;
import com.g2.order.server.vo.user.UserModel;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

@Api(value = "HomeController", description = "用户登录登出接口")
@RestController
@RequestMapping("/home")
public class HomeController {

    @Autowired
    private UserMapper userMapper;

    @ApiOperation(value = "用户登录", notes = "用户登录接口")
    @RequestMapping(value = "/login",
            method = RequestMethod.POST,
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public UserLoginResp login(@RequestBody UserLoginReq req) {
        UserDao userDao = userMapper.getById(1);
        UserModel userModel = new UserModel();
        userModel.setUserId(Integer.toString(userDao.getUserId()));
        return new UserLoginResp(userModel);
    }
}

 

6.请求后的日志

 

请求值链接:/home/login,method:POST,body:{ "userId":"123","password":"123444"},header:cache-control:no-cache
postman-token:a75b1ed5-27d4-47ea-ba21-ee368b463fc5
content-type:application/json
user-agent:PostmanRuntime/2.3.2
host:127.0.0.1:88
accept-encoding:gzip, deflate
content-length:42
connection:keep-alive

返回值:{"success":true,"errorMessage":"","payload":{"userId":"1","roleName":null,"roleId":null}}

 

 

 

如果使用 拦截器(Interceptors)代码会更加简单

import com.google.common.base.Strings;

import com.g2.order.server.api.HttpServletRequestWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static java.util.stream.Collectors.joining;


public class AccessLogInterceptor extends HandlerInterceptorAdapter {
    private static Logger logger = LoggerFactory.getLogger(AccessLogInterceptor.class);

 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String body = "";
        logger.debug("Access Auth Interceptor - 进入拦截器");
        if (request instanceof HttpServletRequestWrapper) {
            HttpServletRequestWrapper requestWrapper = (HttpServletRequestWrapper) request;
            body = requestWrapper.getStringBody();
        }
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            Class[] paramTypes = method.getParameterTypes();
            String paramTypesString = Arrays.stream(paramTypes).map(p -> p.getName()).collect(joining(","));

            Parameter[] methodParameters = method.getParameters();
            if (methodParameters.length == 0) {
                return false;
            }
            Parameter methodParameter = methodParameters[0];
            String parameterName = methodParameter.getName();
            String parameterClass = methodParameter.getType().getCanonicalName();
            String parameterVelue = request.getParameter(parameterName);
            if (Strings.isNullOrEmpty(parameterVelue)) {
                parameterVelue = body;
            }
            logger.info("请求方法:{},请求参数类型:{},请求值:{}", method.getName(), parameterClass, parameterVelue);
        }
        return true;
    }

    /**
     * This implementation is empty.
     */
    @Override
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
    }

    /**
     * This implementation is empty.
     */
    @Override
    public void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

    /**
     * This implementation is empty.
     */
    @Override
    public void afterConcurrentHandlingStarted(
            HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    }
}

 

再建立Config 类

import com.g2.order.server.interceptor.AccessLogInterceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


@Component
public class LogInterceptorConfig extends WebMvcConfigurerAdapter {
    @Bean
    public AccessLogInterceptor getAccessLogInterceptor() {
        return new AccessLogInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration addInterceptor = registry.addInterceptor(getAccessLogInterceptor());

        // 排除配置
        addInterceptor.excludePathPatterns("/error");

        // 拦截配置
        addInterceptor.addPathPatterns("/**");
    }
}

 

3.3 对controller层使用切面(Aspect)(代码最简单,功能最强大)

 

 
 

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

import javax.annotation.Resource;

//开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false,
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Aspect
public class ControllerAspectConfig {
@Around("execution(* com.g2.order.server.controller.*.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("进入切面,执行before..");

//获取controller对应的方法.
org.aspectj.lang.reflect.MethodSignature methodSignature = (org.aspectj.lang.reflect.MethodSignature) proceedingJoinPoint.getSignature();
//获取方法所在的类(controller)
Class beanType = methodSignature.getDeclaringType();
//获取方法
Method method = methodSignature.getMethod();

//获取方法参数列表(无需处理讨厌的流了)
Object[] args = proceedingJoinPoint.getArgs();
for (Object arg : args) {
//获取参数的类型与值
System.out.println(arg.getClass().getName());
System.out.println("arg is " + arg);
}

long startTime = System.currentTimeMillis();
System.out.println("进入其他切面或业务执行..");
Object obj = proceedingJoinPoint.proceed();
     System.out.println("业务完成,执行after..");
        //获取返回值的类型,与 Method.getReturnType()一致
Class responseClass=obj.getClass();
System.out.println("time aspect 耗时" + (System.currentTimeMillis() - startTime));

//方法的返回值是:
System.out.println("response is " + obj);

return obj;
}
}
 

 

拦截器和过滤器的区别

spring boot RESTFul API拦截 以及Filter和interceptor 、Aspect区别

 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

 

 

过滤器(Filter)         :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。

拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数。

切片   (Aspect)       :  可以拿到方法的参数,但是却拿不到http请求和响应的对象