三種實現日志過濾器的方式 (過濾器 (Filter)、攔截器(Interceptors)和切面(Aspect))


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請求和響應的對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM