Servlet属性作用域

属性是存在于3个Servlet API对象(ServletContext、HttpSession和HttpServletRequest)中的对象集。简单来说,我们可以认为它们是键值对的集合。

对于属性的作用域也有3个,即Context、Session和Request作用域,属性可以绑定到这三个作用域上。每个作用域都有它的优点和缺点。取决于具体的需求,我们会将一个属性存放到其中的一个作用域中。

用于存储、查询和删除这些属性的方法对于所有的作用域都是相同的。它们是:
Object getAttribute(String name);   
  
void setAttribute(String name, Object value);   
  
void removeAttribute(String name);   
  
Enumeration getAttributeNames();  

请求作用域

用于将属性存储到请求作用域中的类为ServletRequest。

绑定到请求作用域上的属性仅仅在同一个请求中可用。一旦请求完成,所有绑定到该请求上的属性都会被清空。因此对于该作用域中的属性,没有办法在不同的请求间共享。

因此,当你确信该属性不会由其他Servlet或同一的Servlet的不同请求使用时,可以使用该作用域。

会话作用域

用于将属性存储到会话作用域中的类为HttpSession。

绑定到会话作用域上的属性在同一会话中的所有Servlet操作期间都可用(当然它应该在同一个应用中)。

会话作用域中的属性是非线程安全的。因为会话作用域中的属性在会话期间对所有的Servlet操作都是可用的,因此出现两个不同的Servlet修改同一个会话属性是可能的。举例来说:

Servlet1.java
protected void doGet(HttpServletRequest req, HttpServletResponse resp)   
        throws ServletException, IOException {   
     HttpSession session = req.getSession();   
     session.setAttribute("attr1", "1");   
     String attr1 = (String) session.getAttribute("attr1");   
     PrintWriter writer = resp.getWriter();   
     writer.print(attr1);   
     writer.close();   
}
  
Servlet2.java
protected void doGet(HttpServletRequest req, HttpServletResponse resp)   
        throws ServletException, IOException {   
     HttpSession session = req.getSession();   
     String attr1 = (String) session.getAttribute("attr1");   
     session.setAttribute("attr1", "3");   
     attr1 = (String) session.getAttribute("attr1");   
     PrintWriter writer = resp.getWriter();   
     writer.print(attr1);   
     writer.close();   
}   

打开浏览器让后在两个浏览器标签页里输入servlet1和servlet2的URL,然后查看下结果。在很少的情况下,你会看到servlet1的输出结果为3.你可能会很惊讶因为servlet1中将attr1的值设置为1.这个是可能发生的,因为会话作用域属性不是线程安全的。

现在,让我们看一下,该如何解决该问题呢。我们知道对于Servlet的请求,web容器会创建一个新线程。因此大部分开发者会尝试同步doGet方法来避免并发问题。确实,同步doGet方法将保证同时只有一个线程会进入doGet方法但是它仍然不能解决2个不同的Servlet同时更新会话作用域的属性。因此,最好的方法是同步HttpSession对象。修改后的代码如下:

Servlet1.java
protected void doGet(HttpServletRequest req, HttpServletResponse resp)   
        throws ServletException, IOException {   
     HttpSession session = req.getSession();   
     String attr1 = "";   
    synchronized (session) {   
         session.setAttribute("attr1", "1");   
         attr1 = (String) session.getAttribute("attr1");   
     }   
     PrintWriter writer = resp.getWriter();   
     writer.print(attr1);   
     writer.close();   
}  

上述代码将保证在servlet1执行的时候不会有同步问题。无论执行多少次servlet1,输出总是1.

而同步doGet是一个非常大的错误。这将既不能解决该问题也不允许多用户同时访问该Servlet。

上下文作用域

用于将属性存储到上下文作用域的类是ServletContext。

绑定到该作用域的属性在整个应用期间都是可用的(从应用启动和运行后)。因此,要注意,不能绑定任何消耗内存的对象到该作用域上因为这些对象对垃圾回收是不可见的(不会被回收)。

同样,上下文作用域中的属性也是非线程安全的。因此要避免同步问题,我们需要同步ServletContext中存储的对象。

什么时候将属性存储在请求作用域、会话作用域和上下文作用域中?

请求作用域 会话作用域 上下文作用域
当我们需要使请求参数仅仅对当前请求有效时使用该作用域 参数需要在整个会话期间有效时使用该作用域 当我们需要存储对整个应用都有效的属性时,使用该作用域
请求参数被存放到该作用域中 登录信息和有状态的对象 像数据库链接、JNDI查询等资源
线程安全 非线程安全 非线程安全

猜你喜欢

转载自cb-13.iteye.com/blog/1188066
今日推荐