前言
这一篇主要介绍了servlet中的session以及一个session的小案例。
一、Session的概念
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
下面画个图来看下session的工作原理:
所以对于session来说,不同的客户的数据,其实是通过一个key为JSSESIONID的cookie来区分的。
二、Session常用API
上图中我们已经看到了几个session相关的API,这里我们具体来说下用处:
- request.getSession() :得到HttpSession对象,在我们设置或者是获取session中的数据时,我们都需要先得到这个对象。
- session.setSession():设置session;比如,我们设置一个key为name的session,就可以这样写:session.setAttribute(“name”, “xxx”);
- session.getSession():拿到session的值,这个很好理解,传入key值,就可以获取对应的value值。
(这里需要说明的一点,session是一个域对象,可以被可以被多个请求共享,只要会话不断,就可以共享;
同样是域对象的还有之前博客里的request和servletContext,他们之前的作用域的大小是不一样的,request的作用域最小,只在一次请求内;session次之,只要是同一个用户且session没有过期,都可以访问到;servletContext最大,是在一整个应用内有效) - session.getId():获取session的id值,通过这个值,我们可以看到,不同的客户端去访问同一个地址,它的session是不同的。这里有一张图,分别用Chrome游览器的普通窗口和无痕窗口来模拟两个不同的用户,我们在servlet中输出session的id:
(忽视图片中其他的字,这是我们案例中的例子。这里我们只观察,不同客户端去访问同一个url,我拿到的id值是不同的,这也印证了session的工作原理,解释了它为什么能为不同的用户提供各自的资源了。) - setMaxInactiveInterval():session过期时间,以秒为单位
- session.incalidate():调用就强制销毁session
三、getSession的内部原理
上面讲解了API的作用,这里我们写个小演示代码:
//在demo1中,通过url传入的name,设置一个session
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
String name = req.getParameter("name");
HttpSession session = req.getSession();
session.setAttribute("name",name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
//这里访问demo2,获取session
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
HttpSession session = req.getSession();
String name = (String)session.getAttribute("name");
writer.print(name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
看了上面两端代码,不免有个疑问,为什么我去获取一个session里面的值,也要用getSession()去得到session对象?这两个session对象里面会存同一个值么?结果我们当然知道,demo1和demo2两个servlet用的是同一个session对象。但是为什么呢?
这里,我们就需要看下getSession的工作原理了,当我们执行getSession() 的时候,实际的执行过程是这样的:
- 获取名为JSESSIONID的cookie值;
- 没有这样的cookie,就创建一个新的HttpSession对象,分配唯一的sessionId,并向客户端回写名为JSESSIONID的cookie
- 如果有这样的cookie,就获取这个cookie的值,从服务器中根据ID找到HttpSession对象
- 找到了,就取出来
- 找不到重新创建,回到步骤2的过程
看完原理,我们还有个小注意点:
getSession()里面是可以传入参数的:getSession(boolean create)
- true:和getSession()功能一样
- false:根据客户端JESSIONID的cookie值,找到对应的HttpSession对象,找不到就返回null,不会创建新的,只是查询(一般不用)
四、Session小案例
最后通过一个购物车的小案例来回顾下session中API的使用:
我们需要达到以下效果:
访问index的时候,列出所有的图书名,以及购物车的链接;未加入商品到购物车的时候,提示购物车为空;点击书籍链接,即视为加入购物车,提示添加成功,并且进入购物车可以查看到这个书籍。
//这是index页面展示的内容
public class IndexHome extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.print("以下是本站在售的书籍:<br/>");
Map<String, Book> books = DBUtils.getAllBooks();
for (Map.Entry<String, Book> book : books.entrySet()) {
String id = book.getKey();
writer.print("<a href='"+req.getContextPath()+"/session/addCart?id="+id+"'>"+book.getValue().getName()+"</a><br/>");
//查询出每本书的名字,并且用一个链接跳转,提示添加购物车结果
}
writer.print("<a href='"+req.getContextPath()+"/session/cart'>你的购物车</a><br/>");
//购物车的跳转链接
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
//购物车的页面,这里主要是拿到session中保存的书籍信息,并展示出来
public class Cart extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
HttpSession session = req.getSession();
writer.print("这是你的购物车:<br/>");
List<Book> books = (List)session.getAttribute("cart");
if(books!=null) {
for (Book book : books) {
writer.print(book.getName() + "<br/>");
}
}else {
writer.print("你还没有加入商品到购物车中哦~");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
//这里主要是为了处理加入购物车这个动作的业务
//我们先拿到session中购物车的对应信息,如果没有书籍,那我们需要创建一个list存放书籍对象
//其次,我们还要做一个判断,我们的有书籍的list中是否有当前这个书籍,有的话,就不重复添加,没有的话,我们就加进购物车
public class AddCart extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
String id = req.getParameter("id");
Book book = DBUtils.getBook(id);
HttpSession session = req.getSession();
List<Book> list = (List)session.getAttribute("cart");
if(list==null){
list = new ArrayList<>();
}
if (!list.contains(book)) {
list.add(book);
session.setAttribute("cart", list);
writer.print("添加成功");
}else {
writer.print("这本书已经在你的购物车中啦~");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
总结
到这里,session的使用就介绍完了,其实session的API还是很好记的,常用的也不是很多。对于session的学习来讲,理解session的工作原理比较重要。