在JSF 2中的requestScoped bean中显式创建会话

h-k*_*ach 7 multithreading java-ee jsf-2 managed-bean

我对在JSF 2.x中的requestScoped bean中创建会话有疑问.我知道我们不需要在JSF 2.x中创建会话,因为我们可以直接使用sessionScoped bean来放置用户所需的数据.但我最近介绍了一个JSF代码,其中开发人员创建了session的实例变量并从facescontext获取会话,如下所示

 @ManagedBean
 @RequestScoped
 Public class someClass(){

 FacesContext facesContext = FacesContext.getCurrentInstance();
 HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);

 public void someFunction(){

 //Some code here..

 session.setAttribute("key","value");

 //Some code here..

 }

  //rest of the code here...
 }
Run Code Online (Sandbox Code Playgroud)

好吧,我告诉他们一个人不应该将"session"作为实例变量,也不需要在JSF 2中显式获取会话,而是使用sessionScoped bean.

对于第一个我给出了一个理由,"保持会话作为实例变量不是线程安全的",而对于第二个语句,我给出了"你正在为已经提供的JSF会话做一个解决方法".

我得到的反驳论点是,第一个原因"我们的网络应用是如此,因此不存在多线程问题".第二,我得到了"无论如何,一旦请求被处理,会话变量将被清除,所以这里没有任何错误.

我没有其他强项来纠正它.所以我的问题是,1)他们是否正确?2)我们应该以应该做的方式做任何其他具体原因吗?3)最后,任何可能促使他们使用这种方式的东西?

任何人都可以详细说明吗?请指正.谢谢

sku*_*sel 7

HttpSession 接口,如Java EE 6中所定义,提供了一种用户操作分组方式,用户执行的连续动作的识别以及跨多个页面请求存储有关该用户的信息.

总体概述

由于会话显然是在许多请求之间共享的,因此会引起线程安全问题及其影响.在Servlet 3.0规范第7章Sessions中,人们可能会发现支持处理此类问题的重要性:

执行请求线程的多个servlet可以同时具有对同一会话对象的活动访问权.容器必须确保以线程安全的方式执行表示会话属性的内部数据结构的操作.开发人员负责线程安全访问属性对象本身.这将保护HttpSession对象内的属性集合免于并发访问,从而消除了应用程序导致该集合损坏的机会.

Servlet 3.0规范(JSR-315),ch.7.7.1,强调我的.

但所有这些混乱源于何处呢?在AJAX之前的Web应用程序时代,开发人员并没有太多关于同步的问题,因为同一用户访问会话的可能性很小.但随着构建支持AJAX的Web应用程序的上升趋势,来自同一用户的两个请求很可能同时出现,因此同时访问会话.

作为旁注,值得注意的是,通过使用SingleThreadModelservlet的接口可以略微降低线程问题,但是其应用的必要性存在争议(Servlet 3.0 Specification(JSR-315),ch.2.2.1).此外,它已被弃用,因为Servlet 3.0和"一次可访问多个servlet实例的对象,例如HttpSession的实例,可能在任何特定时间可用于多个servlet,包括那些实现SingleThreadModel的servlet".

同步问题

Java EE 6教程明确指出"当多个Web组件访问存储在会话中的对象时,可能会出现并发访问"(Java EE 6 Tutorial,ch.II-15).此外,如果我们仔细研究HttpSession接口,我们会发现一些允许将对象绑定到会话的方法,实质上提供了跨多个用户连接存储用户信息的可能性,从而克服了HTTP协议的无状态.这些方法是:

  • getAttribute(String name)(现在已弃用getValue(String name));
  • setAttribute(String name, Object value)(现在已弃用putValue(String name, Object value);
  • removeAttribute(String name)(现在已弃用removeValue(String name));
  • invalidate()
  • 接口的其他方法.

最后一个方法使这个会话无效并取消绑定绑定它的任何对象,保存用户信息,因此它不是我们所害怕的.最重要的方法是Object从会话中读取,写入和删除s的方法,因为这些方法将被调用以通过不同的线程同时处理/访问数据.

正如Brian Goetz在Java理论和实践中提到的那样,并发访问的明显问题是:所有有状态的Web应用程序都被破坏了吗?:

  • 原子性失败,其中一个线程正在更新多个数据而另一个线程在数据处于不一致状态时读取数据
  • 读取线程和写入线程之间的可见性失败,其中一个线程修改数据但另一个线程看到陈旧数据或数据处于不一致状态.

解决问题的简单方法

他后来推动了5种减少Web应用程序中并发性问题的技术,并最终指出"在HttpSession上序列化请求会使许多并发危险消失".Marty Hall在他关于会话跟踪的在线教程中提出了关于同步的以下内容:"使用会话或者会话中的值作为同步块的标签".所以,基本设置是:

HttpSession session = request.getSession();
synchronized(session) {
    SomeClass value = (SomeClass)session.getAttribute("someID");
    if (value == null) {
        value = new SomeClass(...);
    }
    doSomethingWith(value);
    session.setAttribute("someID", value);
}
Run Code Online (Sandbox Code Playgroud)

使用此设置,将同步访问会话的重叠请求.

非线程安全使用的示例

HttpSession session = request.getSession();
MyClass myClass = (MyClass)session.getAttribute("myClass");
if(myClass != null) {
    myClass.performOperation();
    session.setAttribute("myClass", myClass);
}
Run Code Online (Sandbox Code Playgroud)

需要在JSF中使用session进行显式操作

清楚地理解,对会话对象中的数据的操纵可能导致并发问题.此外,当您选择在JSF框架内进行开发时,它的适用性是可疑的,该框架将隐式地为您管理会话对象.

最后,当它们本身属于那里时,你应该把对象放在会话中.开发人员有时倾向于将对象放在会话中作为解决问题的方法,但通常有更好的方法.JSF最佳实践文章:托马斯·阿塞尔的清洁会议范围管理中涵盖了一些错误.

解决问题缓解的一些方法

HttpSession使用错误的方式时,大多数线程问题都会出现.从这个角度来看,线程问题范围问题的结果.

例如,如果您在一个应该属于较窄范围的会话中放置一个值,即请求范围或视图范围,那么您的代码就会变得容易受到有形概率的cuncurrency问题的影响.相反,如果所有会话数据都属于正确的范围,那么用户遇到并发问题可能性非常低.

@SessionScoped一旦JSF最终将这些bean存储为托管bean名称作为密钥的属性,开发人员必须确保bean线程安全HttpSession.通过名称访问托管bean在JSF中是一种便利,据我所知,它是由封面完成的session.getAttribute("managedBeanName").

在这种情况下,如果用户在会话中放置了正确的数据并正确管理它(我的意思是没有解决应该在没有会话干扰的情况下解决的问题),那么我看到的唯一缺点是在BalusC的答案中提到的,即引用"紧耦合和糟糕的设计".否则(如果我们省略JSF正确管理bean的生命周期和糟糕的设计问题),则用法类似.

一些导致并发问题的范围问题的例子浮现在脑海中:

  • 存储会话中的用户信息无关的任何内容(用户身份验证详细信息,他的首选项,国际化Web应用程序中的区域设置选择;
  • 不应使用会话来在视图之间共享信息:数据的检索应该通过GET请求来完成,数据的处理应该通过POST请求来完成; 在页面转发期间传递数据可以通过例如使用<f:setPropertyActionListener>,在页面重定向期间传递数据来完成,例如,使用#{flash}属性;
  • 在反复的组件设置应该不干扰会议进行信息传递,通过<f:attribute>,<f:setPropertyActionListener>,<f:param>,和检索信息-通过动作(监听)的方法,<f:viewParam>等等.

总而言之,如果你没有密切关注上述列表,你当然会在某一天看到错误输出,但如果选择正确的数据属于会话范围,几乎不会发现任何问题.通常,在回答整个用户会话问题期间所需的信息时,可以进行这种数据选择.

话虽如此,一个常见的错误是将信息放入会话中以使其在后续视图中可用,检索该信息然后将其从会话中删除,这显然是错误的.我坚信这种方法是由你的对话者采取的.

最后,JSF正在不断发展以促进开发任务,所以为什么不使用它的有用功能呢!