《Servlet、JSP和Spring MVC初学指南》——2.4 HttpSession对象

2.4 HttpSession对象

在所有的会话跟踪技术中,HttpSession 对象是最强大和最通用的。一个用户可以有且最多有一个HttpSession,并且不会被其他用户访问到。

HttpSession对象在用户第一次访问网站的时候自动被创建,你可以通过调用HttpServletRequest的getSession方法获取该对象。getSession有两个重载方法:

HttpSession getSession()

HttpSession getSession(boolean create)

没有参数的getSession方法会返回当前的HttpSession,若当前没有,则创建一个返回。getSession(false)返回当前HttpSession,如当前存在,则返回null。getSession(true)返回当前HttpSession,若当前没有,则创建一个getSession(true)同getSession()一致。

可以通过HttpSession的setAttribute方法将值放入HttpSession,该方法签字如下:

void setAttribute(java.lang.String name, java.lang.Object value)
请注意,不同于URL重新、隐藏域或cookie,放入到HttpSession 的值,是存储在内存中的,因此,不要往HttpSession放入太多对象或大对象。尽管现代的Servlet容器在内存不够用的时候会将保存在HttpSessions的对象转储到二级存储上,但这样有性能问题,因此小心存储。

此外,放到HttpSession的值不限于String类型,可以是任意实现java.io.Serializable的java对象,因为Servlet容器认为必要时会将这些对象放入文件或数据库中,尤其在内存不够用的时候,当然你也可以将不支持序列化的对象放入HttpSession,只是这样,当Servlet容器视图序列化的时候会失败并报错。

调用setAttribute方法时,若传入的name参数此前已经使用过,则会用新值覆盖旧值。
通过调用HttpSession的getAttribute方法可以取回之前放入的对象,该方法的签名如下:

java.lang.Object getAttribute(java.lang.String name)
HttpSession 还有一个非常有用的方法,名为getAttributeNames,该方法会返回一个Enumeration 对象来迭代访问保存在HttpSession中的所有值:

java.util.Enumeration<java.lang.String> getAttributeNames()
注意,所有保存在HttpSession的数据不会被发送到客户端,不同于其他会话管理技术,Servlet容器为每个HttpSession 生成唯一的标识,并将该标识发送给浏览器,或创建一个名为JSESSIONID的cookie,或者在URL后附加一个名为jsessionid 的参数。在后续的请求中,浏览器会将标识提交给服务端,这样服务器就可以识别该请求是由哪个用户发起的。Servlet容器会自动选择一种方式传递会话标识,无须开发人员介入。

可以通过调用 HttpSession的getId方法来读取该标识:

java.lang.String getId()
此外,HttpSession.还定义了一个名为invalidate 的方法。该方法强制会话过期,并清空其保存的对象。默认情况下,HttpSession 会在用户不活动一段时间后自动过期,该时间可以通过部署描述符的 session-timeout 元素配置,若设置为30,则会话对象会在用户最后一次访问30分钟后过期,如果部署描述符没有配置,则该值取决于Servlet容器的设定。

大部分情况下,你应该主动销毁无用的HttpSession,以便释放相应的内存。

可以通过调用HttpSession 的getMaxInactiveInterval 方法来查看会话多久会过期。该方法返回一个数字类型,单位为秒。调用setMaxInactiveInterval 方法来单独对某个HttpSession 设定其超时时间:

void setMaxInactiveInterval(int seconds)
若设置为0,则该HttpSession 永不过期。通常这不是一个好的设计,因此该 HttpSession 所占用的堆内存将永不释放,直到应用重加载或Servlet容器关闭。

清单2.9 ShoppingCartServlet 为一个小的有4个商品的在线商城,用户可以将商品添加到购物车中,并可以查看购物车内容,所用到的Product类可见清单2.7,ShoppingItem 类可见清单2.8,Product类定义了4个属性(id、name、description和price),ShoppingItem 有两个属性,即quantity和Product。
清单2.7 Product类

package app02a.httpsession;
public class Product {
    private int id;
    private String name;
    private String description;
    private float price;

    public Product(int id, String name, String description, float price)
       {
        this.id = id;
        this.name = name;
        this.description = description;
        this.price = price;
    }

    // get and set methods not shown to save space
}

清单2.8 ShoppingItem类

package app02a.httpsession;
public class ShoppingItem {
    private Product product;
    private int quantity;

    public ShoppingItem(Product product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }

    // get and set methods not shown to save space
}

清单2.9 ShoppingCartServlet类

package app02a.httpsession;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet(name = "ShoppingCartServlet", urlPatterns = {
        "/products", "/viewProductDetails",
        "/addToCart", "/viewCart" })
public class ShoppingCartServlet extends HttpServlet {
    private static final long serialVersionUID = -20L;
    private static final String CART_ATTRIBUTE = "cart";

    private List<Product> products = new ArrayList<Product>();
    private NumberFormat currencyFormat = NumberFormat
            .getCurrencyInstance(Locale.US);

    @Override
    public void init() throws ServletException {
        products.add(new Product(1, "Bravo 32' HDTV",
                "Low-cost HDTV from renowned TV manufacturer",
                159.95F));
        products.add(new Product(2, "Bravo BluRay Player",
                "High quality stylish BluRay player", 99.95F));
        products.add(new Product(3, "Bravo Stereo System",
                "5 speaker hifi system with iPod player",
                129.95F));
        products.add(new Product(4, "Bravo iPod player",
                "An iPod plug-in that can play multiple formats",
                39.95F));
    }

    @Override
    public void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {
        String uri = request.getRequestURI();
        if (uri.endsWith("/products")) {
            sendProductList(response);
        } else if (uri.endsWith("/viewProductDetails")) {
            sendProductDetails(request, response);
        } else if (uri.endsWith("viewCart")) {
            showCart(request, response);
        }
    }

    @Override
    public void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {
        // add to cart
        int productId = 0;
        int quantity = 0;
        try {
            productId = Integer.parseInt(
                    request.getParameter("id"));
            quantity = Integer.parseInt(request
                    .getParameter("quantity"));
        } catch (NumberFormatException e) {
        }

        Product product = getProduct(productId);
        if (product != null && quantity >= 0) {

            ShoppingItem shoppingItem = new ShoppingItem(product,
                    quantity);
            HttpSession session = request.getSession();
            List<ShoppingItem> cart = (List<ShoppingItem>) session
                    .getAttribute(CART_ATTRIBUTE);
            if (cart == null) {
                cart = new ArrayList<ShoppingItem>();
                session.setAttribute(CART_ATTRIBUTE, cart);
            }
            cart.add(shoppingItem);
        }
        sendProductList(response);
    }

    private void sendProductList(HttpServletResponse response)
            throws IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html><head><title>Products</title>" +
                    "</head><body><h2>Products</h2>");
        writer.println("<ul>");
        for (Product product : products) {
            writer.println("<li>" + product.getName() + "("
                    + currencyFormat.format(product.getPrice())
                    + ") (" + "<a href='viewProductDetails?id="
                    + product.getId() + "'>Details</a>)");
        }
        writer.println("</ul>");
        writer.println("<a href='viewCart'>View Cart</a>");
        writer.println("</body></html>");
    }

    private Product getProduct(int productId) {
        for (Product product : products) {
            if (product.getId() == productId) {
                return product;
            }
        }
        return null;
    }

    private void sendProductDetails(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        int productId = 0;
        try {
            productId = Integer.parseInt(
                    request.getParameter("id"));
        } catch (NumberFormatException e) {
        }
        Product product = getProduct(productId);
        if (product != null) {
            writer.println("<html><head>"
                    + "<title>Product Details</title></head>"
                    + "<body><h2>Product Details</h2>"
                    + "<form method='post' action='addToCart'>");
            writer.println("<input type='hidden' name='id' "
                    + "value='" + productId + "'/>");
            writer.println("<table>");
            writer.println("<tr><td>Name:</td><td>"
                    + product.getName() + "</td></tr>");
            writer.println("<tr><td>Description:</td><td>"
                    + product.getDescription() + "</td></tr>");
            writer.println("<tr>" + "<tr>"
                    + "<td><input name='quantity'/></td>"
                    + "<td><input type='submit' value='Buy'/>"
                    + "</td>"
                    + "</tr>");
            writer.println("<tr><td colspan='2'>"
                    + "<a href='products'>Product List</a>"
                    + "</td></tr>");
            writer.println("</table>");
            writer.println("</form></body>");
        } else {
            writer.println("No product found");
        }

    }

    private void showCart(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html><head><title>Shopping Cart</title>"
                    + "</head>");
        writer.println("<body><a href='products'>" +
                    "Product List</a>");
        HttpSession session = request.getSession();
        List<ShoppingItem> cart = (List<ShoppingItem>) session
                .getAttribute(CART_ATTRIBUTE);
        if (cart != null) {
            writer.println("<table>");
            writer.println("<tr><td style='width:150px'>Quantity"
                    + "</td>"
                    + "<td style='width:150px'>Product</td>"
                    + "<td style='width:150px'>Price</td>"
                    + "<td>Amount</td></tr>");
            double total = 0.0;
            for (ShoppingItem shoppingItem : cart) {
                Product product = shoppingItem.getProduct();
                int quantity = shoppingItem.getQuantity();
                if (quantity != 0) {
                    float price = product.getPrice();
                    writer.println("<tr>");
                    writer.println("<td>" + quantity + "</td>");
                    writer.println("<td>" + product.getName()
                            + "</td>");
                    writer.println("<td>"
                            + currencyFormat.format(price)
                            + "</td>");
                    double subtotal = price * quantity;

                    writer.println("<td>"
                            + currencyFormat.format(subtotal)
                            + "</td>");
                    total += subtotal;
                    writer.println("</tr>");
                }
            }
            writer.println("<tr><td colspan='4' "
                    + "style='text-align:right'>"
                    + "Total:"
                    + currencyFormat.format(total)
                    + "</td></tr>");
            writer.println("</table>");
        }
        writer.println("</table></body></html>");

    }
}

ShoppingCartServlet 映射有如下URL:

/products:显示所有商品。
/viewProductDetails:展示一个商品的细节。
/addToCart:将一个商品添加到购物车中。
/viewCart:展示购物车的内容。
除/addToCart外,其他URL都会调用doGet方法。doGet 首先根据所请求的URL来生成相应内容:

String uri = request.getRequestURI();
        if (uri.endsWith("/products")) {
            sendProductList(response);
        } else if (uri.endsWith("/viewProductDetails")) {
            sendProductDetails(request, response);
        } else if (uri.endsWith("viewCart")) {
            showCart(request, response);
        }

如下URL访问应用的主界面:

http://localhost:8080/app02a/products
该URL会展示商品列表,如图2.9所示。

单击Details(详细)链接,Servlet会显示所选产品的详细信息,如图2.10所示。请注意页面上的输入框和Buy按钮,输入一个数字并单击Buy按钮,就可以添加该产品到购物车中。

提交购物表单,Web容器会调用ShoppingCartServlet的doPost方法,该方法将一个商品添加到该用户的HttpSession。

doPost方法首先构造一个ShoppingItem实例,传入用户所编辑的商品和数量:

        ShoppingItem shoppingItem = new ShoppingItem(product,
                quantity);

然后获取当前用户的HttpSession,并检查是否已经有一个名为“cart”的List对象:

        HttpSession session = request.getSession();
        List<ShoppingItem> cart = (List<ShoppingItem>) session
                .getAttribute(CART_ATTRIBUTE);

若不存在,则创建一个并添加到HttpSession中:

if (cart == null) {
                cart = new ArrayList<ShoppingItem>();
                session.setAttribute(CART_ATTRIBUTE, cart);
            }

最后,将所创建的ShoppingItem添加到该list中:

        cart.add(shoppingItem);

当用户单击View Cart(查看购物车)链接时,Web容器调用showCart方法,获取当前用户的HttpSession并调用其getAttribute方法来获取购物商品列表:

    HttpSession session = request.getSession();
    List<ShoppingItem> cart = (List<ShoppingItem>) session
            .getAttribute(CART_ATTRIBUTE);

然后迭代访问List对象,并将购物项发送给浏览器:

    if (cart != null) {
        for (ShoppingItem shoppingItem : cart) {
            Product product = shoppingItem.getProduct();
            int quantity = shoppingItem.getQuantity();
        …
时间: 2024-09-15 06:44:41

《Servlet、JSP和Spring MVC初学指南》——2.4 HttpSession对象的相关文章

《Servlet、JSP和Spring MVC初学指南》——导读

前言 Java Servlet技术简称Servlet技术,是Java开发Web应用的底层技术.由Sun公司于1996年发布,用来代替CGI--当时生成Web动态内容的主流技术.CGI技术的主要问题是每个Web请求都需要新启动一个进程来处理.创建进程会消耗不少CPU周期,导致难以编写可扩展的CGI程序.而Servlet有着比CGI程序更好的性能,因为Servlet在创建后(处理第一个请求时)就一直保持在内存中.此后,SUN公司发布了JavaServer Pages(JSP)技术,以进一步简化ser

《Servlet、JSP和Spring MVC初学指南》——1.3 编写基础的Servlet应用程序

1.3 编写基础的Servlet应用程序 其实,编写Servlet应用程序出奇简单.只需要创建一个目录结构,并把Servlet类放在某个目录下.本节将教你如何编写一个名为app01a的Servlet应用程序.最初,它会包含一个Servlet,即MyServlet,其效果是向用户发出一条问候. 要运行Servlets,还需要一个Servlet容器.Tomcat是一个开源的Servlet容器,它是免费的,并且可以在任何能跑Java的平台上运行.如果你到现在都还没有安装Tomcat,就应该去看看附录A

《Servlet、JSP和Spring MVC初学指南》——1.11 使用部署描述符

1.11 使用部署描述符 如在前面的例子中所见,编写和部署Servlet都是很容易的事情.部署的一个方面是用一个路径配置Servlet的映射.在这些范例中,是利用WebServlet标注类型,用一个路径映射了一个Servlet. 利用部署描述符是配置Servlet应用程序的另一种方法,部署描述符的详情将在第13章"部署描述符"中探讨.部署描述符总是命名为web.xml,并且放在WEB-INF目录下.本章介绍了如何创建一个名为app01c的Servlet应用程序,并为它编写了一个web.

《Servlet、JSP和Spring MVC初学指南》——2.3 Cookies

2.3 Cookies URL重写和隐藏域仅适合保存无须跨越太多页面的信息.如果需要在多个页面间传递信息,则以上两种技术实现成本高昂,因为你不得不在每个页面都进行相应处理.幸运的是,Cookies技术可以帮助我们. Cookies是一个很少的信息片段,可自动地在浏览器和Web服务器间交互,因此cookies可存储在多个页面间传递的信息.Cookie作为HTTP header的一部分,其传输由HTTP协议控制.此外,你可以控制cookies的有效时间.浏览器通常支持每个网站高达20个cookies

《Servlet、JSP和Spring MVC初学指南》——1.6 ServletConfig

1.6 ServletConfig 当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init方法传入一个ServletConfig.ServletConfig封装可以通过@WebServlet或者部署描述符传给Servlet的配置信息.这样传入的每一条信息就叫一个初始参数.一个初始参数有key和value两个元件. 为了从Servlet内部获取到初始参数的值,要在Servlet容器传给Servlet的init方法的ServletConfig中调用getInitP

《Servlet、JSP和Spring MVC初学指南》——1.9 Http Servlets

1.9 Http Servlets 不说全部,至少大多数应用程序都要与HTTP结合起来使用.这意味着可以利用HTTP提供的特性.javax.servlet.http包是Servlet API中的第二个包,其中包含了用于编写Servlet应用程序的类和接口.javax.servlet.http中的许多类型都覆盖了javax.servlet中的类型. 图1.5展示了javax.servlet.http中的主要类型. 图1.5 javax.servlet.http中的主要类型 1.9.1 HttpSe

《Servlet、JSP和Spring MVC初学指南》——2.2 隐藏域

2.2 隐藏域 使用隐藏域来保持状态类似于URL重写技术,但不是将值附加到URL上,而是放到HTML表单的隐藏域中.当表单提交时,隐藏域的值也同时提交到服务器端.隐藏域技术仅当网页有表单时有效.该技术相对于URL重写的优势在于:没有字符数限制,同时无须额外的编码.但该技术同URL重写一样,不适合跨越多个界面. 清单2.3展示了如何通过隐藏域来更新客户信息.清单2.2的Customer类为客户对象模型. 清单2.2 Customer类 package app02a.hiddenfields; pu

《Servlet、JSP和Spring MVC初学指南》——1.4 ServletRequest

1.4 ServletRequest 对于每一个HTTP请求,Servlet容器都会创建一个ServletRequest实例,并将它传给Servlet的Service方法.ServletRequest封装了关于这个请求的信息. ServletRequest接口中有一些方法. public int getContentLength() 返回请求主体的字节数.如果不知道字节长度,这个方法就会返回−1. public java.lang.String getContentType() 返回请求主体的M

《Servlet、JSP和Spring MVC初学指南》——1.10 处理HTML表单

1.10 处理HTML表单 一个Web应用程序中几乎总会包含一个或者多个HTML表单,供用户输入值.你可以轻松地将一个HTML表单从一个Servlet发送到浏览器.当用户提交表单时,在表单元素中输入的值就会被当作请求参数发送到服务器. HTML输入域(文本域.隐藏域或者密码域)或者文本区的值,会被当作字符串发送到服务器.空的输入域或者文本区会发送空的字符串.因此,有输入域名称的,ServletRequest.getParameter绝对不会返回null. HTML的select元素也向heade