IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> zuul源码分析 -> 正文阅读

[Java知识库]zuul源码分析

前言

0.本次分享希望能给开发agent的同事一些帮助
1.我搭建的并不是SpringCloud 微服务环境,完全仿照agent(目前没有微服务化)来的
2.搭建的是聚合工程

父pom
在这里插入图片描述

services
在这里插入图片描述
zuul
在这里插入图片描述

版本说明
SpringCloud Finchley.SR2
springboot 2.0.2.RELEASE
选择低版本是因为简单

启动类

在这里插入图片描述
首先看spring.factories文件
导入了ZuulServerAutoConfiguration 和 ZuulProxyAutoConfiguration
在这里插入图片描述
这两个类的区别是生效条件

@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)

@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)

在这里插入图片描述
zuul网关在使用的时候一般在启动类上加@EnableZuulProxy注解

在这里插入图片描述
在这里插入图片描述
启动类只干了一件事注册了一个标记类org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration.Marker
两者导入的maker不同

那么问题来了ZuulServerAutoConfiguration 和 ZuulProxyAutoConfiguration 的区别是啥
在这里插入图片描述
ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration
所以ZuulProxyAutoConfiguration 功能多一些(注入的filter多)
在这里插入图片描述

因此后续内容以ZuulServerAutoConfiguration 来讲

ZuulServerAutoConfiguration 介绍

1.ZuulFilterConfiguration介绍

在这里插入图片描述
zuul的filter要继承ZuulFilter并添加到bean,那么就会被下面的@Autowired注入进来
在这里插入图片描述
在这里插入图片描述

counterFactory

在这里插入图片描述
空实现

tracerFactory

在这里插入图片描述
空实现

FilterLoader(单例)和FilterFileManager(单例)

zuul支持动加载Filter类文件。实现原理是监控存放Filter文件的目录,定期扫描这些目录,如果发现有新Filter源码文件或者Filter源码文件有改动,则对文件进行编译加载。

在项目中调用FilterFileManager.init(int pollingIntervalSeconds, String… directories)
传入 pollingIntervalSeconds 文件检查时间见个,filter文件的目录
就会定时将目录下的filter通过FilterLoader动态写入到filterRegistry中

FilterFileManager源码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
FilterLoader源码
在这里插入图片描述
在这里插入图片描述

FilterRegistry(单例)

在这里插入图片描述
就是一个ConcurrentHashMap 存 beanName 和 ZuulFilter

ZuulFilterInitializer

在这里插入图片描述
在@PostConstruct中
将所有ZuulFilter放入了ZuulFilterInitializer 的成员 filterRegistry 中
查看filterRegistry 源码
在这里插入图片描述
实际上存入的了currentHashMap中

请求拦截器的确定

接下来问题来了,当我用postman请求zuul,会被哪个类处理呢?

  • 方法1.自己写一个filter,通过postman请求一下
    然后查看调用栈信息
    在这里插入图片描述
    可知 zuulController

  • 方法2.断点打到DispatcherServlet.doDispatch方法上,通过postman请求一下

框架处理请求用的是springMvc,熟悉springMvc源码的都知道请求是由DispatcherServlet处理的,这里我在DispatcherServlet.doDispatch方法上打了一个断点
在这里插入图片描述
getHandler方法:根据request请求获取Handler
点进去
在这里插入图片描述
回过来再看:ZuulServerAutoConfiguration
在这里插入图片描述
向容器中注入了ZuulHandlerMapping,而ZuulHandlerMapping又需要RouteLocator(由primaryRouteLocator提供),而primaryRouteLocator又需要routeLocators(由simpleRouteLocator提供)
方法 simpleRouteLocator() 中的 this.zuulProperties 会获取 application.yml配置的映射

在这里插入图片描述

  • 现在可知是ZuulController来处理请求
    在这里插入图片描述
    我在第一次看ZuulServerAutoConfiguration的时候自动注册了ZuulController 和 zuulServlet,这两个都能处理请求
ZuulController

在这里插入图片描述
ZuulController源码发现继承ServletWrappingController
于是搜索ServletWrappingController是做什么的
在这里插入图片描述

通过查阅资料发现
ServletWrappingController 会拦截请求,交给内部包装的的servlet进行处理

  • 最终确定所有请求由ZuulController拦截,由内部包装的ZuulServlet进行处理

ZuulServlet

/*
 * Copyright 2013 Netflix, Inc.
 *
 *      Licensed under the Apache License, Version 2.0 (the "License");
 *      you may not use this file except in compliance with the License.
 *      You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *      Unless required by applicable law or agreed to in writing, software
 *      distributed under the License is distributed on an "AS IS" BASIS,
 *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *      See the License for the specific language governing permissions and
 *      limitations under the License.
 */
package com.netflix.zuul.http;

import com.netflix.zuul.FilterProcessor;
import com.netflix.zuul.ZuulRunner;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;

/**
 * Core Zuul servlet which intializes and orchestrates zuulFilter execution
 *
 * @author Mikey Cohen
 *         Date: 12/23/11
 *         Time: 10:44 AM
 */
public class ZuulServlet extends HttpServlet {

    private static final long serialVersionUID = -3374242278843351500L;
    private ZuulRunner zuulRunner;


    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        String bufferReqsStr = config.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;

        zuulRunner = new ZuulRunner(bufferReqs);
    }

    @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }

        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

    /**
     * executes "post" ZuulFilters
     *
     * @throws ZuulException
     */
    void postRoute() throws ZuulException {
        zuulRunner.postRoute();
    }

    /**
     * executes "route" filters
     *
     * @throws ZuulException
     */
    void route() throws ZuulException {
        zuulRunner.route();
    }

    /**
     * executes "pre" filters
     *
     * @throws ZuulException
     */
    void preRoute() throws ZuulException {
        zuulRunner.preRoute();
    }

    /**
     * initializes request
     *
     * @param servletRequest
     * @param servletResponse
     */
    void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        zuulRunner.init(servletRequest, servletResponse);
    }

    /**
     * sets error context info and executes "error" filters
     *
     * @param e
     */
    void error(ZuulException e) {
        RequestContext.getCurrentContext().setThrowable(e);
        zuulRunner.error();
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class UnitTest {

        @Mock
        HttpServletRequest servletRequest;
        @Mock
        HttpServletResponseWrapper servletResponse;
        @Mock
        FilterProcessor processor;
        @Mock
        PrintWriter writer;

        @Before
        public void before() {
            MockitoAnnotations.initMocks(this);
        }

        @Test
        public void testProcessZuulFilter() {

            ZuulServlet zuulServlet = new ZuulServlet();
            zuulServlet = spy(zuulServlet);
            RequestContext context = spy(RequestContext.getCurrentContext());


            try {
                FilterProcessor.setProcessor(processor);
                RequestContext.testSetCurrentContext(context);
                when(servletResponse.getWriter()).thenReturn(writer);

                zuulServlet.init(servletRequest, servletResponse);
                verify(zuulServlet, times(1)).init(servletRequest, servletResponse);
                assertTrue(RequestContext.getCurrentContext().getRequest() instanceof HttpServletRequestWrapper);
                assertTrue(RequestContext.getCurrentContext().getResponse() instanceof HttpServletResponseWrapper);

                zuulServlet.preRoute();
                verify(processor, times(1)).preRoute();

                zuulServlet.postRoute();
                verify(processor, times(1)).postRoute();
//                verify(context, times(1)).unset();

                zuulServlet.route();
                verify(processor, times(1)).route();
                RequestContext.testSetCurrentContext(null);

            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    }

}

servlet init方法

ZuulServlet 初始化时会执行一次 servlet的init方法创建 ZuulRunner 对象赋值给成员变量
在这里插入图片描述

service方法

处理每次http请求

在这里插入图片描述

init 方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
RequestContext 实际上就是当前线程(threadLocal)里面存了一个map

在这里插入图片描述

  • init 方法总结:当前线程TheadLocal中放了一个map,然后map中存入request 和 response
preRoute方法;

在这里插入图片描述
获取preFilter 逻辑
在filterRegistry获取所有filter然后调用filter的filterType方法,根据filterType方法返回值是否为pre进行过滤
在这里插入图片描述
循环执行preFilter
在这里插入图片描述

可以看到ServletDetectionFilter、Servlet30WrapperFilter、FormBodyWrapperFilter、DebugFilter几个内置的Filter
下面来看看这几个filter的作用

ServletDetectionFilter
/*
 * Copyright 2013-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.zuul.filters.pre;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.DispatcherServlet;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ZuulServlet;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;

/**
 * Detects whether a request is ran through the {@link DispatcherServlet} or {@link ZuulServlet}.
 * The purpose was to detect this up-front at the very beginning of Zuul filter processing
 *  and rely on this information in all filters.
 *  RequestContext is used such that the information is accessible to classes 
 *  which do not have a request reference.
 * @author Adrian Ivan
 */
public class ServletDetectionFilter extends ZuulFilter {

	public ServletDetectionFilter() {
	}

	@Override
	public String filterType() {
		return PRE_TYPE;
	}

	/**
	 * Must run before other filters that rely on the difference between 
	 * DispatcherServlet and ZuulServlet.
	 */
	@Override
	public int filterOrder() {
		return SERVLET_DETECTION_FILTER_ORDER;
	}

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

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (!(request instanceof HttpServletRequestWrapper) 
				&& isDispatcherServletRequest(request)) {
			ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, true);
		} else {
			ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, false);
		}

		return null;
	}
	// 本过滤器的作用是判断请求的来源,判断请求是从dispatcherServlet来的还是 从zuulServlet来的。
	// 并将判断结果存放到RequestContext中。后面的filter会用到
	private boolean isDispatcherServletRequest(HttpServletRequest request) {
		return request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
	}	 	

}

在这里插入图片描述

Servlet30WrapperFilter
/*
 * Copyright 2013-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.zuul.filters.pre;

import java.lang.reflect.Field;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_30_WRAPPER_FILTER_ORDER;

/**
 * Pre {@link ZuulFilter} that wraps requests in a Servlet 3.0 compliant wrapper.
 * Zuul's default wrapper is only Servlet 2.5 compliant.
 * @author Spencer Gibb
 */
public class Servlet30WrapperFilter extends ZuulFilter {

	private Field requestField = null;

	public Servlet30WrapperFilter() {
		this.requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class,
				"req", HttpServletRequest.class);
		Assert.notNull(this.requestField,
				"HttpServletRequestWrapper.req field not found");
		this.requestField.setAccessible(true);
	}

	protected Field getRequestField() {
		return this.requestField;
	}

	@Override
	public String filterType() {
		return PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		return SERVLET_30_WRAPPER_FILTER_ORDER;
	}

	@Override
	public boolean shouldFilter() {
		return true; // TODO: only if in servlet 3.0 env
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request instanceof HttpServletRequestWrapper) {
			request = (HttpServletRequest) ReflectionUtils.getField(this.requestField,
					request);
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		else if (RequestUtils.isDispatcherServletRequest()) {
			// 根据上一个filter设置的IS_DISPATCHER_SERVLET_REQUEST_KEY来判断,对request进行包装为Servlet30RequestWrapper,放入ctx中
			// If it's going through the dispatcher we need to buffer the body
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		return null;
	}

}

Servlet30RequestWrapper 及其父类 com.netflix.zuul.http.HttpServletRequestWrapper
  • 讲到这里本次主要将透传相关,因此Servlet30RequestWrapper这个类将重点介绍

package org.springframework.cloud.netflix.zuul.filters.pre;

import com.netflix.zuul.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;

class Servlet30RequestWrapper extends HttpServletRequestWrapper {
	private HttpServletRequest request;

	Servlet30RequestWrapper(HttpServletRequest request) {
		super(request);
		this.request = request;
	}

	/**
	 * There is a bug in zuul 1.2.2 where HttpServletRequestWrapper.getRequest returns a wrapped request rather than the raw one.
	 * 覆盖父类方法,直接返回原始request对象
	 * @return the original HttpServletRequest
	 */
	@Override
	public HttpServletRequest getRequest() {
		return this.request;
	}
}


package com.netflix.zuul.http;

import com.netflix.zuul.constants.ZuulHeaders;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.util.HTTPRequestUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.util.*;
import java.util.zip.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;

/**
 * This class implements the Wrapper or Decorator pattern.<br/>
 * Methods default to calling through to the wrapped request object,
 * except the ones that read the request's content (parameters, stream or reader).
 * <p/>
 * This class provides a buffered content reading that allows the methods
 * {@link #getReader()}, {@link #getInputStream()} and any of the getParameterXXX to be     called
 * safely and repeatedly with the same results.
 * <p/>
 * This class is intended to wrap relatively small HttpServletRequest instances.
 *
 * @author pgurov
 */
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

    private final static HashMap<String, String[]> EMPTY_MAP = new HashMap<String, String[]>();
    protected static final Logger LOG = LoggerFactory.getLogger(HttpServletRequestWrapper.class);

    private HttpServletRequest req;
    private byte[] contentData = null;
    private HashMap<String, String[]> parameters = null;

    private long bodyBufferingTimeNs = 0;

    public HttpServletRequestWrapper() {
        super(groovyTrick());
    }

    private static HttpServletRequest groovyTrick() {
        //a trick for Groovy
        throw new IllegalArgumentException("Please use HttpServletRequestWrapper(HttpServletRequest request) constructor!");
    }

    private HttpServletRequestWrapper(HttpServletRequest request, byte[] contentData, HashMap<String, String[]> parameters) {
        super(request);
        req = request;
        this.contentData = contentData;
        this.parameters = parameters;
    }

    public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        req = request;
    }

    /**
     * Returns the wrapped HttpServletRequest.
     * Using the getParameterXXX(), getInputStream() or getReader() methods may interfere
     * with this class operation.
     *
     * 会被子类覆盖,详见子类
     * @return The wrapped HttpServletRequest.
     */
    @Override
    public HttpServletRequest getRequest() {
        try {
            parseRequest();
        } catch (IOException e) {
            throw new IllegalStateException("Cannot parse the request!", e);
        }
        return req;
    }

    /**
     * This method is safe to use multiple times.
     *
     * @return The request body data.
     */
    public byte[] getContentData() {
        return contentData;
    }


    /**
     * This method is safe to use multiple times.
     * Changing the returned map or the array of any of the map's values will not
     * interfere with this class operation.
     * 
     * 安全的获取Parameters的方法,返回ParametersMap的一个副本(The cloned parameters map)
     * @return The cloned parameters map.
     */

    public HashMap<String, String[]> getParameters() {
        if (parameters == null) return EMPTY_MAP;
        HashMap<String, String[]> map = new HashMap<String, String[]>(parameters.size() * 2);
        for (String key : parameters.keySet()) {
            map.put(key, parameters.get(key).clone());
        }
        return map;
    }
	/**
	* 很多方法调用之前都会调用此方法
	* 
	*/
    private void parseRequest() throws IOException {
    	// 已经解析过,parameters不为null,直接返回
        if (parameters != null)
            return; //already parsed

        HashMap<String, List<String>> mapA = new HashMap<String, List<String>>();
        List<String> list;
		// 1.首先去 RequestContext.getCurrentContext() 找 requestQueryParams,找到则返回
		// 2.再去 raw request 调用 getQueryString()
		// QueryString处理详见:HTTPRequestUtils.getQueryParams()方法
	    // HTTPRequestUtils.getQueryParams()解析完QueryString后,会将结果设置到RequestContext中(key为requestQueryParams)
        Map<String, List<String>> query = HTTPRequestUtils.getInstance().getQueryParams();
        if (query != null) {
            for (String key : query.keySet()) {
                list = query.get(key);
                mapA.put(key, list);
            }
        }

        if (shouldBufferBody()) {

            // Read the request body inputstream into a byte array.
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try {
                // Copy all bytes from inputstream to byte array, and record time taken.
                long bufferStartTime = System.nanoTime();
                IOUtils.copy(req.getInputStream(), baos);
                bodyBufferingTimeNs = System.nanoTime() - bufferStartTime;

                contentData = baos.toByteArray();
            } catch (SocketTimeoutException e) {
                // This can happen if the request body is smaller than the size specified in the
                // Content-Length header, and using tomcat APR connector.
                LOG.error("SocketTimeoutException reading request body from inputstream. error=" + String.valueOf(e.getMessage()));
                if (contentData == null) {
                    contentData = new byte[0];
                }
            }

            try {
                LOG.debug("Length of contentData byte array = " + contentData.length);
                if (req.getContentLength() != contentData.length) {
                    LOG.warn("Content-length different from byte array length! cl=" + req.getContentLength() + ", array=" + contentData.length);
                }
            } catch(Exception e) {
                LOG.error("Error checking if request body gzipped!", e);
            }

            final boolean isPost = req.getMethod().equals("POST");

            String contentType = req.getContentType();
            final boolean isFormBody = contentType != null && contentType.contains("application/x-www-form-urlencoded");

            // only does magic body param parsing for POST form bodies
            if (isPost && isFormBody) {
                String enc = req.getCharacterEncoding();

                if (enc == null) enc = "UTF-8";
                String s = new String(contentData, enc), name, value;
                StringTokenizer st = new StringTokenizer(s, "&");
                int i;

                boolean decode = req.getContentType() != null;
                while (st.hasMoreTokens()) {
                    s = st.nextToken();
                    i = s.indexOf("=");
                    if (i > 0 && s.length() > i + 1) {
                        name = s.substring(0, i);
                        value = s.substring(i + 1);
                        if (decode) {
                            try {
                                name = URLDecoder.decode(name, "UTF-8");
                            } catch (Exception e) {
                            }
                            try {
                                value = URLDecoder.decode(value, "UTF-8");
                            } catch (Exception e) {
                            }
                        }
                        list = mapA.get(name);
                        if (list == null) {
                            list = new LinkedList<String>();
                            mapA.put(name, list);
                        }
                        list.add(value);
                    }
                }
            }
        }

        HashMap<String, String[]> map = new HashMap<String, String[]>(mapA.size() * 2);
        for (String key : mapA.keySet()) {
            list = mapA.get(key);
            map.put(key, list.toArray(new String[list.size()]));
        }

        parameters = map;

    }
	/**
	* 是否 缓冲 body
	* 当 ContentLength > 0 返回true
	* 当 ContentLength == -1 ,-1 说明没传header  ContentLength  
	* 	如果此时 传了请求头transfer-encoding:chunked (https://blog.csdn.net/u014569188/article/details/78912469)
	*   返回true
	* 其他情况都返回false
	*/
    private boolean shouldBufferBody() {

        if (LOG.isDebugEnabled()) {
            LOG.debug("Path = " + req.getPathInfo());
            LOG.debug("Transfer-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.TRANSFER_ENCODING)));
            LOG.debug("Content-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.CONTENT_ENCODING)));
            LOG.debug("Content-Length header = " + req.getContentLength());
        }

        boolean should = false;
        if (req.getContentLength() > 0) {
            should = true;
        }
        else if (req.getContentLength() == -1) {
            final String transferEncoding = req.getHeader(ZuulHeaders.TRANSFER_ENCODING);
            if (transferEncoding != null && transferEncoding.equals(ZuulHeaders.CHUNKED)) {
                RequestContext.getCurrentContext().setChunkedRequestBody();
                should = true;
            }
        }

        return should;
    }

    /**
     * Time taken to buffer the request body in nanoseconds.
     * buffer 这个request花费的时间
     * @return
     */
    public long getBodyBufferingTimeNs()
    {
        return bodyBufferingTimeNs;
    }

    /**
     * This method is safe to call multiple times.
     * Calling it will not interfere with getParameterXXX() or getReader().
     * Every time a new ServletInputStream is returned that reads data from the begining.
     *
     * @return A new ServletInputStream.
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        parseRequest();

        return new ServletInputStreamWrapper(contentData);
    }

    /**
     * This method is safe to call multiple times.
     * Calling it will not interfere with getParameterXXX() or getInputStream().
     * Every time a new BufferedReader is returned that reads data from the begining.
     *
     * @return A new BufferedReader with the wrapped request's character encoding (or UTF-8 if null).
     */
    @Override
    public BufferedReader getReader() throws IOException {
        parseRequest();

        String enc = req.getCharacterEncoding();
        if (enc == null)
            enc = "UTF-8";
        byte[] data = contentData;
        if (data == null)
            data = new byte[0];
        return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), enc));
    }

    /**
     * This method is safe to execute multiple times.
     *
     * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
     */
    @Override
    public String getParameter(String name) {
        try {
            parseRequest();
        } catch (IOException e) {
            throw new IllegalStateException("Cannot parse the request!", e);
        }
        if (parameters == null) return null;
        String[] values = parameters.get(name);
        if (values == null || values.length == 0)
            return null;
        return values[0];
    }

    /**
     * This method is safe.
     *
     * @see {@link #getParameters()}
     * @see javax.servlet.ServletRequest#getParameterMap()
     */
    @SuppressWarnings("unchecked")
    @Override
    public Map getParameterMap() {
        try {
            parseRequest();
        } catch (IOException e) {
            throw new IllegalStateException("Cannot parse the request!", e);
        }
        return getParameters();
    }

    /**
     * This method is safe to execute multiple times.
     *
     * @see javax.servlet.ServletRequest#getParameterNames()
     */
    @SuppressWarnings("unchecked")
    @Override
    public Enumeration getParameterNames() {
        try {
            parseRequest();
        } catch (IOException e) {
            throw new IllegalStateException("Cannot parse the request!", e);
        }
        return new Enumeration<String>() {
            private String[] arr = getParameters().keySet().toArray(new String[0]);
            private int idx = 0;

            @Override
            public boolean hasMoreElements() {
                return idx < arr.length;
            }

            @Override
            public String nextElement() {
                return arr[idx++];
            }

        };
    }

    /**
     * This method is safe to execute multiple times.
     * Changing the returned array will not interfere with this class operation.
     *
     * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
     */
    @Override
    public String[] getParameterValues(String name) {
        try {
            parseRequest();
        } catch (IOException e) {
            throw new IllegalStateException("Cannot parse the request!", e);
        }
        if (parameters == null) return null;
        String[] arr = parameters.get(name);
        if (arr == null)
            return null;
        return arr.clone();
    }
}

HttpServletRequestWrapper 注释介绍:

This class implements the Wrapper or Decorator pattern. Methods default to calling through to the wrapped request object, except the ones that read the request's content (parameters, stream or reader).

This class provides a buffered content reading that allows the methods getReader(), getInputStream() and any of the getParameterXXX to be called safely and repeatedly with the same results.

This class is intended to wrap relatively small HttpServletRequest instances.
FormBodyWrapperFilter

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:16:56  更:2022-02-26 11:19:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:00:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码