2.4 Java Servlet 会话管理 通过HttpSession对象
在所有的会话跟踪技术中,HttpSession
对象是最强大和最通用的。一个用户可以有且最多有一个HttpSession
,并且不会被其他用户访问到。
谁创建HttpSession对象
HttpSession
对象在用户第一次访问网站的时候自动被创建。
如何获取HttpSession对象
你可以通过调用HttpServletRequest
的getSession
方法获取该对象。getSession
有两个重载方法:
1 2
| HttpSession getSession() HttpSession getSession(boolean create)
|
没有参数的getSession
方法会返回当前的HttpSession
,若当前没有,则创建一个并返回。
getSession(false)
返回当前HttpSession
,如当前不存在,则返回null
。
getSession(true)
返回当前HttpSession
,若当前没有,则创建一个并返回,getSession(true)
同getSession()
的效果一致.
如何往HttpSession中存入数据
可以通过HttpSession
的setAttribute
方法将值放入HttpSession
,该方法的方法签名如下:
1
| void setAttribute(java.lang.String name, java.lang.Object value)
|
请注意,不同于URL
重新、隐藏域或cookie
,放入到HttpSession
的值,是存储在内存中的,因此,不要往HttpSession
放入太多对象或放入大对象。
尽管现代的Servlet
容器在内存不够用的时候会将保存在HttpSessions
的对象转储到二级存储上,但这样有性能问题,因此小心存储。
HttpSession中的数据要满足的条件
此外,放到HttpSession
的值不限于String
类型,可以是任意实现java.io.Serializable
的java
对象,因为在内存不够用的时候,Servlet
容器会将这些对象放入文件或数据库中,当然你也可以将不支持序列化的对象放入HttpSession
,只是这样,当Servlet
容器进行序列化的时候会失败并报错。
调用setAttribute
方法时,若传入的name
参数此前已经使用过,则会用新值覆盖旧值。
从HttpSession对象中取出数据
通过调用HttpSession
的getAttribute
方法可以取回之前放入HttpSession
中的对象,该方法的签名如下:
1
| java.lang.Object getAttribute(java.lang.String name)
|
从HttpSession对象中批量取出数据
HttpSession
还有一个非常有用的方法,名为getAttributeNames
,该方法会返回一个Enumeration
对象来迭代访问保存在HttpSession
中的所有值:
1
| java.util.Enumeration<java.lang.String> getAttributeNames()
|
注意,所有保存在HttpSession
的数据不会被发送到客户端,不同于其他会话管理技术,**Servlet
容器为每个HttpSession
生成唯一的标识,并将该标识发送给浏览器**,或创建一个名为JSESSIONID
的cookie
,或者在URL
后附加一个名为jsessionid
的参数。在后续的请求中,浏览器会将标识提交给服务端,这样服务器就可以识别该请求是由哪个用户发起的。Servlet
容器会自动选择一种方式传递会话标识,无须开发人员介入。
提示:
- 隐藏域技术会往浏览器发送带隐藏域的表单.
Cookie
技术会往浏览器发送Cookie
.
获取Http对象的标识
可以通过调用 HttpSession
的getId
方法来读取该标识:
1
| java.lang.String getId()
|
此外,HttpSession
.还定义了一个名为invalidate
的方法。该方法强制会话过期,并清空其保存的对象。默认情况下,HttpSession
会在用户不活动一段时间后自动过期,该时间可以通过部署描述符的 session-timeout
元素配置,若设置为30
,则会话对象会在用户最后一次访问30
分钟后过期,如果部署描述符没有配置,则该值取决于Servlet
容器的设定。
大部分情况下,你应该主动销毁无用的HttpSession
,以便释放相应的内存。
查看设置超时时间
可以通过调用HttpSession
的getMaxInactiveInterval
方法来查看会话多久会过期。该方法返回一个数字类型,单位为秒。调用setMaxInactiveInterval
方法来单独对某个HttpSession
设定其超时时间:
1
| void setMaxInactiveInterval(int seconds)
|
若设置为0,则该HttpSession
永不过期。通常这不是一个好的设计,因为该 HttpSession
所占用的堆内存将永不释放,直到应用重加载或Servlet
容器关闭。
Demo
一个简单的购物Demo
,具有显示商品列表,商品详情页面,购物车页面.并且在商品详情页面提供添加到购物车的功能.
Product
商品类用来存储商品的信息,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| package session.management.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; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } }
|
购物车中光有商品而已是不行的,还要知道用户买了几个商品,商品
和对应的数量
构成了购物车中记录的数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package session.management.httpsession;
public class ShoppingItem { private Product product; private int quantity; public ShoppingItem(Product product,int quantity) { this.product = product; this.quantity = quantity; } public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } }
|
有了商品
,有了记录
,下面就来写功能代码了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
| package session.management.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 static final String tableStyle = "<style>table{border-right:1px solid #F00;border-bottom:1px solid #F00}\r\n" + "table td{border-left:1px solid #F00;border-top:1px solid #F00}</style>"; private List<Product> products = new ArrayList<Product>(); private NumberFormat currencyFormat = NumberFormat .getCurrencyInstance(Locale.CHINA); @Override public void init() throws ServletException { products.add(new Product(1, "苹果", "新鲜上市的苹果", 5.5F)); products.add(new Product(2, "香蕉", "新鲜上市的香蕉", 6.5F)); products.add(new Product(3, "菠萝", "新鲜上市的菠萝", 4.8F)); products.add(new Product(4, "草莓", "新鲜上市的草莓", 12.5F)); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); 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 { response.setCharacterEncoding("utf-8"); 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(); @SuppressWarnings( "unchecked" ) 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>商品列表</title>" + tableStyle + "</head><body><h2>商品列表</h2>"); writer.println("<ul>"); for (Product product : products) { writer.println("<li>" + product.getName() + "(" + currencyFormat.format(product.getPrice()) + ") (" + "<a href='viewProductDetails?id=" + product.getId() + "'>宝贝详情</a>)"); } writer.println("</ul>"); writer.println("<a href='viewCart'>查看购物车</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>商品详情</title>" + tableStyle + "</head>" + "<body><h2>商品详情</h2>" + "<form method='post' action='addToCart'>"); writer.println("<input type='hidden' name='id' " + "value='" + productId + "'/>"); writer.println("<table>"); writer.println( "<tr><td>商品名称:</td><td>" + product.getName() + "</td></tr>"); writer.println("<tr><td>商品描述:</td><td>" + product.getDescription() + "</td></tr>"); writer.println( "<tr><td>购买数量:</td><td><input name='quantity'/>斤</td></tr>" + "<tr><td colspan='2'><input type='submit' value='加入购物车'/>" + "</td>" + "</tr>"); writer.println("<tr><td colspan='2'>" + "<a href='products'>返回商品列表</a>" + "</td></tr>"); writer.println("</table>"); writer.println("</form></body></html>"); } else { writer.println("<h2>找不到该商品</h2>"); } } private void showCart(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer .println("<html><head><title>购物车</title>" + tableStyle + "</head>"); writer.println("<body><a href='products'>" + "返回商品列表</a>"); HttpSession session = request.getSession(); @SuppressWarnings( "unchecked" ) List<ShoppingItem> cart = (List<ShoppingItem>) session .getAttribute(CART_ATTRIBUTE); if (cart != null) { writer.println("<table>"); writer.println("<tr><td style='width:150px'>数量" + "</td>" + "<td style='width:150px'>商品名称</td>" + "<td style='width:150px'>单品价格</td>" + "<td>合计</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'>" + "总价格:" + currencyFormat.format(total) + "</td></tr>"); writer.println("</table>"); } writer.println("</table></body></html>"); } }
|
代码详解
创建并初始化商品列表
1 2 3 4 5 6 7 8 9 10
| private List<Product> products = new ArrayList<Product>(); @Override public void init() throws ServletException { products.add(new Product(1, "苹果", "新鲜上市的苹果", 5.5F)); products.add(new Product(2, "香蕉", "新鲜上市的香蕉", 6.5F)); products.add(new Product(3, "菠萝", "新鲜上市的菠萝", 4.8F)); products.add(new Product(4, "草莓", "新鲜上市的草莓", 12.5F)); }
|
从商品列表中查找出一个商品
1 2 3 4 5 6 7 8 9 10
| private Product getProduct(int productId) { for (Product product : products) { if (product.getId() == productId) { return product; } } return null; }
|
匹配URL
1 2 3 4 5
| @WebServlet( name = "ShoppingCartServlet", urlPatterns = {"/products", "/viewProductDetails", "/addToCart", "/viewCart"} )
|
doGet方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); 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); } }
|
显示产品列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private void sendProductList(HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html><head><title>商品列表</title>" + tableStyle + "</head><body><h2>商品列表</h2>"); writer.println("<ul>"); for (Product product : products) { writer.println("<li>" + product.getName() + "(" + currencyFormat.format(product.getPrice()) + ") (" + "<a href='viewProductDetails?id=" + product.getId() + "'>宝贝详情</a>)"); } writer.println("</ul>"); writer.println("<a href='viewCart'>查看购物车</a>"); writer.println("</body></html>"); }
|
显示商品详情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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>商品详情</title>" + tableStyle + "</head>" + "<body><h2>商品详情</h2>" + "<form method='post' action='addToCart'>"); writer.println("<input type='hidden' name='id' " + "value='" + productId + "'/>"); writer.println("<table>"); writer.println( "<tr><td>商品名称:</td><td>" + product.getName() + "</td></tr>"); writer.println("<tr><td>商品描述:</td><td>" + product.getDescription() + "</td></tr>"); writer.println( "<tr><td>购买数量:</td><td><input name='quantity'/>斤</td></tr>" + "<tr><td colspan='2'><input type='submit' value='加入购物车'/>" + "</td>" + "</tr>"); writer.println("<tr><td colspan='2'>" + "<a href='products'>返回商品列表</a>" + "</td></tr>"); writer.println("</table>"); writer.println("</form></body></html>"); } else { writer.println("<h2>找不到该商品</h2>"); } }
|
显示商品详情页,表单属性如下:
1
| <form method='post' action='addToCart'>
|
当用户点击加入购物车
时,会使用POST方法把表单发送给.\addToCart
这个URL.
doPost方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); 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(); @SuppressWarnings( "unchecked" ) 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); }
|
显示购物车
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| private void showCart(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer .println("<html><head><title>购物车</title>" + tableStyle + "</head>"); writer.println("<body><a href='products'>" + "返回商品列表</a>"); HttpSession session = request.getSession(); @SuppressWarnings( "unchecked" ) List<ShoppingItem> cart = (List<ShoppingItem>) session .getAttribute(CART_ATTRIBUTE); if (cart != null) { writer.println("<table>"); writer.println("<tr><td style='width:150px'>数量" + "</td>" + "<td style='width:150px'>商品名称</td>" + "<td style='width:150px'>单品价格</td>" + "<td>合计</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'>" + "总价格:" + currencyFormat.format(total) + "</td></tr>"); writer.println("</table>"); } writer.println("</table></body></html>"); }
|