添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在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有较大的扩展,里面代理了requestresponse很多方法:

	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;

ServletWebRequestServletRequestAttributes的子类,还实现了接口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");
	//没有手动调用setDefaultLocale,取值为  Locale#getDefault()
	private static Locale defaultLocale;
	//同上 默认取值为TimeZone.getDefault()
	private static TimeZone defaultTimeZone;

几乎源码过程同RequestContextHolder,只需要注意一个方法:

// 我们可以直接从请求域拿到Local上下文,但是也是可以自己传进来的。。。
public static Locale getLocale(@Nullable LocaleContext localeContext) { ... }

其实这两个类也可以作为我们的工具来使用,我们集成的时候也可以使用Spring提供的两个类。
以小见大,优秀之所以优秀,是因为Spring确实做到了方便、快捷的编码环境,解放coder,它做了很多。当然人无完人,没有完美的东西,深入理解后我们也会发现,其实优秀如Spring,里面还是有些我们可以发挥,补充的地方

AuthorA哥(YourBatman)
个人站点www.yourbatman.cn
E-mailyourbatman@qq.com
微 信fsx641385712
活跃平台
公众号BAT的乌托邦(ID:BAT-utopia)
知识星球BAT的乌托邦
每日文章推荐每日文章推荐
前言在Java Web的开发中,我们大都执行着三层的开发模式(Controller、Service、Dao)。然后很少有人知道这三层的职责便捷在哪?所以不乏经常遇到这样的问题:我这块逻辑该写在哪呢?我相信大多数初、中甚至高级程序员也分不太清楚,逻辑分层有点信手拈来,所以最终写成了后辈们眼中的“屎”,哈哈当然代码组织结构不是本文讨论的范畴~~~在实际开发中:有不少小伙伴想在Service层或者...
用于商品购物、包含数据库、前台:商品显示、商品分页、商品类别查询、排序、购物车、购物订单 后台:商品管理:商品显示、商品模糊查询、商品删除、商品添加、商品管理分页、商品修改; 类别管理:类别显示、类别删除、类别数据查询、类别修改、类别添加、类别查询; 订单管理:订单显示、订单查询、订单删除、订单修改;包含项目文档
很多小伙伴都知道 SpringMVC 的核心是 DispatcherServlet,而 DispatcherServlet 的父类就是 FrameworkServlet,因此我们先来看看 FrameworkServlet,这有助于我们理解 DispatcherServlet。 1.FrameworkServlet FrameworkServlet 继承自 HttpServletBean,而 HttpServletBean 继承自 HttpServlet,HttpServlet 就是 JavaEE 里边的
2.application.properties(指定自定义的目录) #ResourceBundleMessageSource管理国际化资源文件 spring.messages.basename=i20n.login 3.源码(分析) 如果你创建的国际化资源文件不是直接在resources目录下 那么他就去appli... // 根据语言环境获取对应资源文件 // 需要注意getBundle(baseName, locale)中的baseName是指资源文件所在目录,而非资源文件前缀, // 网上搜了好多都说是前缀,搞了半天都读不到。。。 ResourceBundle bundle = ResourceBundle.getBundle("resource/messages" , locale); &lt;bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"&gt;
SpringMVC国际化绕不开的话题是LocaleResolver。第一次接触LocaleResolver时,对这个类的作用不是很清楚。所以,这次特别写篇文章,记录下学习心得。 SpringMVC默认提供了四种实现了接口的类:CookieLocaleResolver, AcceptHeaderLocalResolver,FixdLocaleResolver,SessionLocaleResolver。结构图如下所示: LocaleResolver的主要作用: 解析Request中的语言标志参数或者head中
本教程的第一部分描述了如何配置使用Spring MVC Test框架的单元测试 。 现在是时候动手做,学习如何为“常规”控制器编写单元测试了。 显而易见的下一个问题是: 什么是普通控制器? 好吧,一个普通的控制器(在此博客文章的上下文中)是一个控制器,它要么呈现视图,要么处理表单提交。 让我们开始吧。 注意 :建议您在阅读此博客文章之前先阅读本教程的第一部分 (如果已经阅...
上下文实际上是应用程序运行所依赖的环境。获取上下文的三种方式: xxxActivity.class Activity类型 Activity的实例,通常与getApplicationContext类型通用,只有在AlertDialog对话框的时候上下文必须是Activity类型。 2.getApplicationContext Context类型
Microchip是一家全球领先的半导体生产厂商,也是电子元件及软件解决方案的供应商。微控制器、数字信号处理器、模拟集成电路、无线收发器等产品主要用于嵌入式系统、通讯基础设施、电池电源管理和电子设备控制等领域。 而微逆,顾名思义是一种微型逆变器,主要用于将直流电转换成交流电,以便于应用到电能供应系统中。微逆具有体积小、效率高、可靠性强的特点,能够直接连接光伏组件或风力机,并通过逆变,将直流输出的电能转化为交流电能输出到电池或电网中。 近年来,随着可再生能源的不断推广和普及,微逆的市场需求也逐渐增加。微逆已经成为大型逆变系统中不可缺少的一个组成部分,同时也成为小家电器及汽车等领域中的重要组成部分,为人们的生活和工作提供了更加高效、稳定的电力支持。 总之,Microchip和微逆都是为电子技术和电力行业做出了巨大贡献的。
【小家java】Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only 103119 【小家Spring】注意BeanPostProcessor启动时对依赖Bean的“误伤”陷阱(is not eligible for getting processed by all...) 96880
【小家java】Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only 月落漫天星辰: 厉害了 大佬 清楚明白 【小家java】Spring事务嵌套引发的血案---Transaction rolled back because it has been marked as rollback-only 月落漫天星辰: