由RequestContextHolder获取HttpServletRequest引发的思考

 最近做项目中需要获取request,从而获取请求ip,request可以从控制层传过来,也可以调用RequestContextHolder获取。获取方式如下:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes()).getRequest()

 那么问题来了,问什么调用这个方法就可以呢,值又是什么时候set进去的呢,首先要明确这是springMvc提供的方法,所以得从springMvc的源码看起,spring Mvc核心代码图谱如下:
springMvc核心代码继承关系图谱
http请求会出发HttpServlet的doGet或doPost方法,但这两个方法的实现是在FrameworkServlet中,所以看FrameworkServlet源码,先看doGet方法入手

protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response); 
	}

只有个processRequest方法,那么重点看下这个方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		//先获取LocaleContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//建立新的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);
		//建立新的ServletRequestAttributes   这个就是存在后来RequestContextHolder的RequestAttributes,
		//所以取出来的时候需要强转一下,获取RequestContextHolder的HttpServletRequest
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		//具体方法
		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response); //这是DispatcherServlet核心实现代码,有兴趣的可以看下,这里不做讨论
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		………… 此处省略
		
		}
	}

接下来看下其中的initContextHolders方法到底做了什么

private void initContextHolders(
			HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		if (requestAttributes != null) {  //设置的地方requestAttributes
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Bound request context to thread: " + request);
		}
	}


public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
		if (attributes == null) {
			resetRequestAttributes();
		}
		else {
			if (inheritable) {
				inheritableRequestAttributesHolder.set(attributes);
				requestAttributesHolder.remove();
			}
			else {
				requestAttributesHolder.set(attributes);
				inheritableRequestAttributesHolder.remove();
			}
		}
	}

inheritableRequestAttributesHolder和requestAttributesHolder其实就是RequestContextHolder的属性,就在这个时候被set进去了,

//得到存储进去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<RequestAttributes>("Request attributes");

//可被子线程继承的request
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
			new NamedInheritableThreadLocal<RequestAttributes>("Request context");

这里需要对ThreadLocal简单解释一下,ThreadLocal即线程共享变量,线程间互不影响,所以在每个请求里获取的就是当前线程的request.对ThreadLocal的详细解释请看这里

  到这里应该对之前获取的request不足为奇了,希望能帮到爱学习的你!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用RequestContextHolder获取当前请求的HttpServletRequest对象,然后从HttpServletRequest对象中获取客户端的IP地址。示例如下: ```java import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; public class MyController { public String myMethod() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 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.getHeader("HTTP_CLIENT_IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); } return ipAddress; } } ``` 以上代码会依次从请求头中获取客户端IP地址,如果无法获取则返回RemoteAddr。注意,由于可能存在代理服务器,因此需要从请求头中获取真实IP地址,而不是直接从RemoteAddr中获取
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值