论坛首页 Java企业应用论坛

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

浏览 8341 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (3) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-05-04  
如果用户的web请求超过tomcat的线程限制会怎么样?theadlocal变量里面保存的东西会怎么取舍呢?
0 请登录后投票
   发表时间:2009-05-04  
这种讨论我喜欢!思路开阔。
0 请登录后投票
   发表时间: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对象应该是两回事。前者将跨越表示层和业务逻辑层,而后者却只是限于表示层。
0 请登录后投票
   发表时间: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对象应该是两回事。前者将跨越表示层和业务逻辑层,而后者却只是限于表示层。



楼上这位大牛说到点子上了,
0 请登录后投票
   发表时间:2009-05-05  
最近做框架,使用到了较多的ThreadLocal,只是在Tomcat等使用线程池的WEB容器里,由于线程是放池里的,所以在某些应用场合,需要一个地方对ThreadLocal进行清理。
如果用ThreadLocal的地方多了,如何清理干净,不对其他请求产生影响倒是成了问题。

各位有没有好的建议?谢谢。
0 请登录后投票
   发表时间:2009-05-05  
http://www.iteye.com/post/985950?page=1

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

我们当中有多人没有做过测试也没用过这些东西,只会迷信一些‘牛人’的‘教诲’。
然后就去‘教诲’别人了。
0 请登录后投票
   发表时间:2009-05-05  
依赖注入. 不要传递参数. 用的地方直接inject.


0 请登录后投票
   发表时间: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中,都包含着这种思想。它区别于常见的层的概念或者说设计思路的
      当然了,其他朋友所担心的滥用也是可以理解的,不过任何一个好的设计都来自于思想的理解。楼主有了第一步,建议拓广一下思路。
0 请登录后投票
   发表时间:2009-05-06  
如果业务层和表示层不在同一个应用中,而是通过rmi、soap调用,就歇菜了。
0 请登录后投票
   发表时间:2009-05-06  
    ThreadLocal是针对特定场景的特效药而不是万甘油。
    一方面,很多模式的实现都用上了ThreadLocal,例如OpenSessionInView。
另一方面,ThreadLocal不能滥用。因为什么?因为他隐形,隐形的东西容易让代码不清析。像当年的"goto"?
     所以我建议,ThreadLocal可以用,但要限制在系统架构层等一些框架类中,而且这些基础类不能直接爆露ThreadLocal给一般模块调用。
     另外要注意,在servlet容器中使用时,在请求过程中注入的对像,要在请求完毕前,完全清理。因为servlet容器的实现,大多用线程池实现。不同的表求有可能共用一个线程。

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics