相关文章推荐
睿智的松鼠  ·  HTTP客户端之Spring ...·  2 周前    · 
不拘小节的排球  ·  IllegalStateException: ...·  1 年前    · 
冲动的啄木鸟  ·  如何在C#中将两个对象合并为一个对象-腾讯云 ...·  1 年前    · 
伤情的充电器  ·  Python保姆级教程.pdf_爬虫_技术_实战·  2 年前    · 
一身肌肉的砖头  ·  Azure ML Workspace ...·  2 年前    · 
火爆的乒乓球  ·  agps android,Android ...·  2 年前    · 
Code  ›  【小家Spring】Spring MVC之RequestContextHolder和LocaleContextHolder的使用详解以及使用误区开发者社区
spring框架
https://cloud.tencent.com/developer/article/1497802
纯真的电脑桌
2 年前
作者头像
YourBatman
0 篇文章

【小家Spring】Spring MVC之RequestContextHolder和LocaleContextHolder的使用详解以及使用误区

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
社区首页 > 专栏 > BAT的乌托邦 > 正文

【小家Spring】Spring MVC之RequestContextHolder和LocaleContextHolder的使用详解以及使用误区

发布 于 2019-09-03 16:34:39
4.4K 0
举报

前言

在Java Web的开发中,我们大都执行着三层的开发模式( Controller、Service、Dao )。然后很少有人知道这三层的职责便捷在哪? 所以不乏经常遇到这样的问题:我这块逻辑该写在哪呢?我相信大多数初、中甚至高级程序员也分不太清楚,逻辑分层有点信手拈来,所以最终写成了后辈们眼中的“屎”,哈哈当然代码组织结构不是本文讨论的范畴~~~

在 实际 开发中:有不少小伙伴想在 Service 层或者 某个工具类 层里获取 HttpServletRequest 对象,甚至response的都有。 其中一种方式是,把request当作入参,一层一层的传递下去。不过这种有点费劲,且做起来很不优雅。这里介绍另外一种方案: RequestContextHolder ,任意地方使用如下代码:

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

类似的, LocaleContextHolder 是用来处理Local的上下文 容器

RequestContextHolder使用以及源码分析

RequestContextHolder顾名思义,持有上下文的Request容器.使用是很简单的 ,它所有方法都是 static 的

该类主要维护了两个全局容器(基于 ThreadLocal ):

	// jsf是JSR-127标准的一种用户界面框架  过时的技术,所以此处不再做讨论
	private static final boolean jsfPresent =
			ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
	//现成和request绑定的容器
	private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<>("Request attributes");
	// 和上面比较,它是被子线程继承的request   Inheritable:可继承的
	private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
			new NamedInheritableThreadLocal<>("Request context");

现在主要是她的一些get/set方法之类的:

	public static void resetRequestAttributes() {
		requestAttributesHolder.remove();
		inheritableRequestAttributesHolder.remove();
	// 把传入的RequestAttributes和当前线程绑定。 注意这里传入false:表示不能被继承
	public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
		setRequestAttributes(attributes, false);
	//兼容继承和非继承  只要得到了就成
	public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		return attributes;
	//在没有jsf的时候,效果完全同getRequestAttributes()  因为jsf几乎废弃了,所以效果可以说一致
	public static RequestAttributes currentRequestAttributes() throws IllegalStateException;
相关使用

DispatcherServlet 在处理请求的时候,父类 FrameworkServlet#processRequest 就有向 RequestContextHolder 初始化绑定一些通用参数的操作,这样子使用者可以在任意地方,拿到这些公用参数了,可谓特别的方便。

在下面这篇博文讲解 Spring MVC执行流程 源码分析 中,就明确的讲述到了它的初始化过程~

小伙伴可以先自行先思考一个问题:request和response是怎么样设置进去的呢?(下文会分解)

另外监听器 org.springframework.web.context.request.RequestContextListener (它是一个 ServletRequestListener )里也有所体现,我们只需要配置上此监听器即可(因为DispatcherServlet里有处理,所以此监听器加不加,无所谓了~)

使用误区

场景描述一:在一个商品编辑页面,提交一个有附件的表单,这个时候通过 RequestHolder.getRequest().getParameter() 得不到参数值,这是为何? 其实使用过的我们发现,这么操作大部分情况下都是好使的,但是如果是文件上传,在 DispatcherServlet 里会把request包装成 MultipartHttpServletRequest ,同时content-type为 multipart/form-data ,因此这个时候getParameter()就失效了~

根本原因:checkMultipart()方法返回的是new出来的一个新的request,所以根本就不再是原来的引用了

场景描述二:在自己新启的线程里,是不能使用request对象的,当然也就不能使用 RequestContextHolder 去获取到请求域对象了,需要稍加注意

相关类:RequestAttributes

RequestAttributes 该接口的定义了一些比如 get/setAttribute() 的便捷方法。它有很多子类,比如我们最常用的 ServletRequestAttributes 有较大的扩展,里面代理了 request 和 response 很多方法:

	public final HttpServletRequest getRequest() {
		return this.request;
	@Nullable
	public final HttpServletResponse getResponse() {
		return this.response;
	@Override
	public Object getAttribute / setAttribute(String name, int scope) { ... }
	getAttributeNames;

ServletWebRequest 是 ServletRequestAttributes 的子类,还实现了接口 NativeWebRequest(提供一些获取Native Request的方法,其实没太大作用) :它代理得就更加的全一些,比如:

	@Nullable
	public HttpMethod getHttpMethod() {
		return HttpMethod.resolve(getRequest().getMethod());
	@Override
	@Nullable
	public String getHeader(String headerName) {
		return getRequest().getHeader(headerName);
	getHeaderValues/getParameter/... 等等一些列更多的代理方法

DispatcherServletWebRequest 继续继承的子类,没什么好说的,唯一就是复写了此方法:

	@Override
	public Locale getLocale() {
		return RequestContextUtils.getLocale(getRequest());
	}

StandardServletAsyncWebRequest 这个和异步拦截器相关,属于异步上下文范畴,此处不做讨论。

LocaleContextHolder使用以及源码分析

这个比上面就更简单些,是来做本地化、国际化的上下文容器。

	private static final ThreadLocal<LocaleContext> localeContextHolder =
			new NamedThreadLocal<>("LocaleContext");
	private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
			new NamedInheritableThreadLocal<>("LocaleContext");
 
推荐文章
睿智的松鼠  ·  HTTP客户端之Spring WebClient - 钟小嘿
2 周前
不拘小节的排球  ·  IllegalStateException: 非法访问;此Web应用程序实例已停止。无法加载[]。为了调试以及终止导致非法访问的线程,抛出以下堆栈跟踪_illegalstateexception非法访
1 年前
冲动的啄木鸟  ·  如何在C#中将两个对象合并为一个对象-腾讯云开发者社区-腾讯云
1 年前
伤情的充电器  ·  Python保姆级教程.pdf_爬虫_技术_实战
2 年前
一身肌肉的砖头  ·  Azure ML Workspace Notebook: ModuleNotFoundError: No module named 'azureml.pipeline' - Microsoft Q&A
2 年前
火爆的乒乓球  ·  agps android,Android AGPS 定位 测试程序_君信陌的博客-CSDN博客
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号