`
hatedance
  • 浏览: 59489 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

小试ThreadLocal想到“隐式参数”模式

阅读更多
最近同事想通过自定义函数来输出国际化文字。比如:
${my:i18n('login.userid')}.
EL支持我们自定义这样的函数,问题是这个函数没法获取request对象,不知道当前页面的语言。

由此我想到threadlocal也许可以解决这个问题。
我的思路是做一个filter,每次都把request引用保存在一个threadlocal变量里。然后在上述的i18n自定义函数里读取这个threadlocal变量,得到request。
代码如下:
1 定义一个类读写threadlocal变量

public class ThreadAttributes {

        private static ThreadLocal<Map<String, Object>> threadAttribues = new ThreadLocal<Map<String, Object>>() {

                protected synchronized Map<String, Object> initialValue() {
                        return new HashMap<String, Object>();
                }
        };

        public static Object getThreadAttribute(String name) {

                return threadAttribues.get().get(name);

        }

        public static Object setThreadAttribute(String name, Object value) {

                return threadAttribues.get().put(name, value);

        }

}

2 在一个filter里写入request
public void doFilter(ServletRequest req, ServletResponse resp,
                        FilterChain chain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) resp;

                
                ThreadAttributes.setThreadAttribute("request", req);
        .....
}

3 读取request
HttpServletRequest request = (HttpServletRequest)ThreadAttributes.getThreadAttribute("request");
                
                



由此,我想到其他类似的问题。
跟web相关的里的方法,往往都有request参数;
跟数据库相关的方法,往往都有连接或事务的参数;
跟绘图相关的方法里,往往有一个绘图设备参数;
。。。。
如今分层架构非常普及,如果这些非常令人讨厌,但又的确需要的参数,都用上述的方案来在堆栈之间传递,将是一个不错的主意。事实上,spring已经让我们尝到了甜头,解放了我们的DAO对象,看不到connection参数。

我这里且叫它为“隐式参数”模式。



分享到:
评论
19 楼 presses 2009-05-06  
    ThreadLocal是针对特定场景的特效药而不是万甘油。
    一方面,很多模式的实现都用上了ThreadLocal,例如OpenSessionInView。
另一方面,ThreadLocal不能滥用。因为什么?因为他隐形,隐形的东西容易让代码不清析。像当年的"goto"?
     所以我建议,ThreadLocal可以用,但要限制在系统架构层等一些框架类中,而且这些基础类不能直接爆露ThreadLocal给一般模块调用。
     另外要注意,在servlet容器中使用时,在请求过程中注入的对像,要在请求完毕前,完全清理。因为servlet容器的实现,大多用线程池实现。不同的表求有可能共用一个线程。

18 楼 vlinux 2009-05-06  
如果业务层和表示层不在同一个应用中,而是通过rmi、soap调用,就歇菜了。
17 楼 凤舞凰扬 2009-05-06  
hatedance 写道
最近同事想通过自定义函数来输出国际化文字。比如:
${my:i18n('login.userid')}.
EL支持我们自定义这样的函数,问题是这个函数没法获取request对象,不知道当前页面的语言。

由此我想到threadlocal也许可以解决这个问题。
我的思路是做一个filter,每次都把request引用保存在一个threadlocal变量里。然后在上述的i18n自定义函数里读取这个threadlocal变量,得到request。
代码如下:
1 定义一个类读写threadlocal变量

public class ThreadAttributes {

        private static ThreadLocal<Map<String, Object>> threadAttribues = new ThreadLocal<Map<String, Object>>() {

                protected synchronized Map<String, Object> initialValue() {
                        return new HashMap<String, Object>();
                }
        };

        public static Object getThreadAttribute(String name) {

                return threadAttribues.get().get(name);

        }

        public static Object setThreadAttribute(String name, Object value) {

                return threadAttribues.get().put(name, value);

        }

}

2 在一个filter里写入request
public void doFilter(ServletRequest req, ServletResponse resp,
                        FilterChain chain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) resp;

                
                ThreadAttributes.setThreadAttribute("request", req);
        .....
}

3 读取request
HttpServletRequest request = (HttpServletRequest)ThreadAttributes.getThreadAttribute("request");
                
                



由此,我想到其他类似的问题。
跟web相关的里的方法,往往都有request参数;
跟数据库相关的方法,往往都有连接或事务的参数;
跟绘图相关的方法里,往往有一个绘图设备参数;
。。。。
如今分层架构非常普及,如果这些非常令人讨厌,但又的确需要的参数,都用上述的方案来在堆栈之间传递,将是一个不错的主意。事实上,spring已经让我们尝到了甜头,解放了我们的DAO对象,看不到connection参数。

我这里且叫它为“隐式参数”模式。




     楼上的想法不错,我支持你!
     不过这不是什么隐式参数模式,呵呵,这种思想其实被广泛应用,一般我们称之为Context,也就是上下文。比如在JSF中,比如EJB中,都包含着这种思想。它区别于常见的层的概念或者说设计思路的
      当然了,其他朋友所担心的滥用也是可以理解的,不过任何一个好的设计都来自于思想的理解。楼主有了第一步,建议拓广一下思路。
16 楼 ajoo 2009-05-05  
依赖注入. 不要传递参数. 用的地方直接inject.


15 楼 Bernard 2009-05-05  
http://www.iteye.com/post/985950?page=1

我已经做过了。
这种东西得通过大量测试才能令人令己信服。

我们当中有多人没有做过测试也没用过这些东西,只会迷信一些‘牛人’的‘教诲’。
然后就去‘教诲’别人了。
14 楼 DT1 2009-05-05  
最近做框架,使用到了较多的ThreadLocal,只是在Tomcat等使用线程池的WEB容器里,由于线程是放池里的,所以在某些应用场合,需要一个地方对ThreadLocal进行清理。
如果用ThreadLocal的地方多了,如何清理干净,不对其他请求产生影响倒是成了问题。

各位有没有好的建议?谢谢。
13 楼 linliangyi2007 2009-05-04  
downpour 写道
hatedance 写道
我不知道各位对threadlocal的恐惧来自哪里,也许我是初生牛犊。

在struts2中,有2种方式获取request对象,一种是ioc即实现ServletRequestAware接口。
另一种就是HttpServletRequest request = ServletActionContext.getRequest ();

我曾奇怪,这个ServletActionContext怎么知道request对象是哪个?我看了手册,原来也是用threadlocal实现的。

http://www.opensymphony.com/webwork/api/com/opensymphony/xwork/ActionContext.html
官方文档 写道

The ActionContext is the context in which an Action is executed. Each context is basically a container of objects an action needs for execution like the session, parameters, locale, etc.

The ActionContext is thread local which means that values stored in the ActionContext are unique per thread. See the ThreadLocal class for more information. The benefit of this is you don't need to worry about a user specific action context, you just get it:

      ActionContext context = ActionContext.getContext();

Finally, because of the thread local usage you don't need to worry about making your actions thread safe.


问题在于耦合性和可测试性。

如果不用ThreadLocal,业务逻辑层是可以独立进行测试的,然而现在你的实现类中混有了对ThreadLocal的依赖,也就是说,你的测试比如依赖于Web容器对象的模拟。

使用ThreadLocal做隐含参数和使用ThreadLocal实现读取HttpServletRequest对象应该是两回事。前者将跨越表示层和业务逻辑层,而后者却只是限于表示层。



楼上这位大牛说到点子上了,
12 楼 downpour 2009-05-04  
hatedance 写道
我不知道各位对threadlocal的恐惧来自哪里,也许我是初生牛犊。

在struts2中,有2种方式获取request对象,一种是ioc即实现ServletRequestAware接口。
另一种就是HttpServletRequest request = ServletActionContext.getRequest ();

我曾奇怪,这个ServletActionContext怎么知道request对象是哪个?我看了手册,原来也是用threadlocal实现的。

http://www.opensymphony.com/webwork/api/com/opensymphony/xwork/ActionContext.html
官方文档 写道

The ActionContext is the context in which an Action is executed. Each context is basically a container of objects an action needs for execution like the session, parameters, locale, etc.

The ActionContext is thread local which means that values stored in the ActionContext are unique per thread. See the ThreadLocal class for more information. The benefit of this is you don't need to worry about a user specific action context, you just get it:

      ActionContext context = ActionContext.getContext();

Finally, because of the thread local usage you don't need to worry about making your actions thread safe.


问题在于耦合性和可测试性。

如果不用ThreadLocal,业务逻辑层是可以独立进行测试的,然而现在你的实现类中混有了对ThreadLocal的依赖,也就是说,你的测试比如依赖于Web容器对象的模拟。

使用ThreadLocal做隐含参数和使用ThreadLocal实现读取HttpServletRequest对象应该是两回事。前者将跨越表示层和业务逻辑层,而后者却只是限于表示层。
11 楼 neora 2009-05-04  
这种讨论我喜欢!思路开阔。
10 楼 winstonczc 2009-05-04  
如果用户的web请求超过tomcat的线程限制会怎么样?theadlocal变量里面保存的东西会怎么取舍呢?
9 楼 linliangyi2007 2009-04-16  
楼主的探讨精神还是好的。加油。
没有同样的程序,只有不同的编程思想!!!
8 楼 Joo 2009-04-15  
sdh5724 写道
数据少了没有关系, 如果一个请求10M数据, 就玩完了. 需要你要在确保在适当的地方释放这些数据.


HttpServletRequest声明周期短,加上大多数请求都很短小精悍,因此我觉得对到过滤器的request对象判断一下contentType提出大容量的请求,然后再做ThreadLocal处理其实也不错.楼主看能不能顺着这个思路再优化一下
7 楼 zhuxing 2009-04-03  
看起来,楼主有很强的“模式”发明欲
6 楼 luanma 2009-04-03  
WEB国际化完全可以这样做,我上一个项目就是这样实现的,TOMcat最多也就是250个http线程(缺省80个)没问题。

不过没有像LZ一样把所有request保存下来,我们只是保存客户端的locale,似乎LZ有滥用嫌疑哈:)
5 楼 hatedance 2009-04-03  
我不知道各位对threadlocal的恐惧来自哪里,也许我是初生牛犊。

在struts2中,有2种方式获取request对象,一种是ioc即实现ServletRequestAware接口。
另一种就是HttpServletRequest request = ServletActionContext.getRequest ();

我曾奇怪,这个ServletActionContext怎么知道request对象是哪个?我看了手册,原来也是用threadlocal实现的。

http://www.opensymphony.com/webwork/api/com/opensymphony/xwork/ActionContext.html
官方文档 写道

The ActionContext is the context in which an Action is executed. Each context is basically a container of objects an action needs for execution like the session, parameters, locale, etc.

The ActionContext is thread local which means that values stored in the ActionContext are unique per thread. See the ThreadLocal class for more information. The benefit of this is you don't need to worry about a user specific action context, you just get it:

      ActionContext context = ActionContext.getContext();

Finally, because of the thread local usage you don't need to worry about making your actions thread safe.
4 楼 leadyu 2009-04-02  
还得考虑中间件的线程池,释放不好的话ThreadLocal变量也不好维护。还不如i18n方法把Locale开放出来,由页面传进去

3 楼 superdandy 2009-04-02  
Threadlocal使用不当很容易造成内存泄漏。 特别是在web环境中
2 楼 sdh5724 2009-03-27  
数据少了没有关系, 如果一个请求10M数据, 就玩完了. 需要你要在确保在适当的地方释放这些数据.
1 楼 linliangyi2007 2009-03-27  
兄弟,你的想法固然好,但是否考虑过度的使用,会造成架构层模型的破坏。

使用上是方便了,你可以在一个Thread的任何时候访问到request,但你也破坏的卸耦原则,使得你的很多Biz层甚至DAO层的类绑定了HttpServletRequest对象,也就绑定了Web服务这种模式。

相关推荐

    设计模式及ThreadLocal资料

    总结一下,这份资料涵盖了设计模式中的单例模式、工厂模式和代理模式,以及Java中的ThreadLocal特性。理解并熟练应用这些概念和技术,对于提升Java开发者的技能水平,优化代码的可读性和可维护性具有重要作用。在...

    ThreadLocal应用示例及理解

    创建ThreadLocal实例时,通常会定义一个泛型参数,代表该线程局部变量的类型。例如: ```java ThreadLocal&lt;String&gt; threadLocal = new ThreadLocal(); ``` ### 设置和获取值 在ThreadLocal中,`set()`方法用于...

    ThreadLocal原理及在多层架构中的应用

    - **难以调试**:由于ThreadLocal的隐式性,有时可能会导致难以发现的问题,特别是当线程池中的线程复用时。 ### 4. 使用注意事项 - 使用完毕后,应调用`remove()`方法清除ThreadLocal变量,防止内存泄漏。 - 尽量...

    ThreadLocal

    ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...

    正确理解ThreadLocal.pdf

    ### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...

    理解ThreadLocal

    理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal

    ThreadLocal 内存泄露的实例分析1

    在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...

    ThreadLocal的几种误区

    ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...

    java 简单的ThreadLocal示例

    Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...

    ThreadLocal整理.docx

    ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...

    java事务 - threadlocal

    Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...

    threadLocal

    ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...

    java中ThreadLocal详解

    ### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...

    ThreadLocal详解.md

    学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!

    Hibernate用ThreadLocal模式(线程局部变量模式)管理Session

    【ThreadLocal模式管理Session的理解】 在使用Hibernate进行数据库操作时,正确管理Session是优化系统性能的关键。Session由SessionFactory创建,而SessionFactory是线程安全的,这意味着它可以被多个并发线程共享...

    事务的封装和Threadlocal实例

    在默认情况下,JDBC连接是非自动提交模式,这意味着每条SQL语句执行后,如果未显式提交,事务会一直持续到程序结束或遇到异常。要手动控制事务,我们需要调用`Connection`对象的`setAutoCommit(false)`方法来禁用...

    ThreadLocal简单Demo

    **线程局部变量(ThreadLocal)** 在Java编程中,`ThreadLocal`是一个非常重要的工具类,它用于在多线程环境中提供线程安全的局部变量。`ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己...

Global site tag (gtag.js) - Google Analytics