例15-5 login.html
<html>
<head>
<title>登录页面</title>
</head>
<body>
<form action="online" method="post">
<table>
<tr>
<td>请输入用户名:</td>
<td><input type="text" name="user"></td>
</tr>
<tr>
<td>请输入密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td><input type="reset" value="重填"></td>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>
Step2
例15-6 UserList.java
1.package org.sunxin.ch15.online;
2.
3.import java.util.Vector;
4.import java.util.Enumeration;
5.
6.public class UserList
7.{
8. private static final UserList userList=new UserList();
9. private Vector<String> v;
10.
11. private UserList()
12. {
13. v=new Vector<String>();
14. }
15.
16. public static UserList getInstance()
17. {
18. return userList;
19. }
20.
21. public void addUser(String name)
22. {
23. if(name!=null)
24. v.addElement(name);
25. }
26.
27. public void removeUser(String name)
28. {
29. if(name!=null)
30. v.remove(name);
31. }
32.
33. public Enumeration<String> getUserList()
34. {
35. return v.elements();
36. }
37.
38. public int getUserCount()
39. {
40. return v.size();
41. }
42.}
在代码的第16~19行,定义了一个静态的方法getInstance(),在这个方法中,返回在类加载时创建的UserList类的对象。因为getInstance()方法本身是静态的,所以可以直接通过类名来调用。
那么为什么要将UserList设计成单例类呢?这是因为UserList类的对象是用于存储和获取在线用户的列表,而这个用户列表对于所有的页面来说都应该是同一个,所以将UserList类设计成单例类,这样,所有的类访问的就是同一个UserList对象了。
注意,在这个地方没有使用ArrayList,是考虑到UserList对象可能会被多个线程同时访问,因为ArrayList不是同步的,而Vector是同步的,所以采用Vector来保存用户列表。
例15-7 User.java
1. package org.sunxin.ch15.online;
2.
3. import javax.servlet.http.HttpSessionBindingListener;
4. import javax.servlet.http.HttpSessionBindingEvent;
5.
6. public class User implements HttpSessionBindingListener
7. {
8. private String name;
9. private UserList ul=UserList.getInstance();
10.
11. public User()
12. {
13. }
14. public User(String name)
15. {
16. this.name=name;
17. }
18. public void setName(String name)
19. {
20. this.name=name;
21. }
22. public String getName()
23. {
24. return name;
25. }
26. public void valueBound(HttpSessionBindingEvent event)
27. {
28. ul.addUser(name);
29. }
30. public void valueUnbound(HttpSessionBindingEvent event)
31. {
32. ul.removeUser(name);
33. }
34.}
User类实现了HttpSessionBindingListener接口,表示登录的用户。代码第9行,通过UserList类的静态方法getInstance()得到UserList类的对象,第26~29行,当User对象加入到Session中时,Servlet容器将调用valueBound()方法,我们将用户的名字保存到用户列表中。第30~33行,当User对象从Session中被删除时,Servlet容器将调用valueUnbound()方法,我们从用户列表中删除该用户。
例15-8 OnlineUserServlet.java
1.package org.sunxin.ch15.online;
2.
3.import javax.servlet.*;
4.import java.io.*;
5.import javax.servlet.http.*;
6.import java.util.Enumeration;
7.
8.public class OnlineUserServlet extends HttpServlet
9.{
10. public void doGet(HttpServletRequest req, HttpServletResponse resp)
11. throws ServletException,IOException
12. {
13. req.setCharacterEncoding("gb2312");
14. String name=req.getParameter("user");
15. String pwd=req.getParameter("password");
16.
17. if(null==name || null==pwd || name.equals("") || pwd.equals(""))
18. {
19. resp.sendRedirect("login.html");
20. }
21. else
22. {
23. HttpSession session=req.getSession();
24. User user=(User)session.getAttribute("user");
25. if(null==user || !name.equals(user.getName()))
26. {
27. user=new User(name);
28. session.setAttribute("user",user);
29. }
30.
31. resp.setContentType("text/html;charset=gb2312");
32. PrintWriter out=resp.getWriter();
33.
34. out.println("欢迎用户<b>"+name+"</b>登录");
35. UserList ul=UserList.getInstance();
36. out.println("<br>当前在线的用户列表:<br>");
37. Enumeration<String> enums=ul.getUserList();
38. int i=0;
39. while(enums.hasMoreElements())
40. {
41. out.println(enums.nextElement());
42. out.println(" ");
43. if(++i==10)
44. {
45. out.println("<br>");
46. }
47. }
48. out.println("<br>当前在线的用户数:"+i);
49. out.println("<p><a href=logout>退出登录</a>");
50. out.close();
51. }
52. }
53.
54. public void doPost(HttpServletRequest req, HttpServletResponse resp)
55. throws ServletException,IOException
56. {
57. doGet(req,resp);
58. }
59.}
OnlineUser类用于向用户显示欢迎信息、当前在线用户列表和在线用户数。代码的第24~29行,首先从Session中获取名为user的属性对象,通过判断它是否为空来判断在此次会话中用户是否已经登录。如果user对象不为null,那么接着判断在同一个会话中,用户是否更换了一个用户名登录。如果user对象为空或者当前登录的用户名和先前登录的用户名不同,则以用户的当前登录名创建一个User对象,将这个对象绑定到Session中,这个时候,Servlet容器就会调用User对象的valueBound()方法,将这个用户的名字保存在用户列表中。第35行,得到UserList类的对象。第37行,得到用户列表的枚举对象。第39~47行,循环取出在线用户的名字并输出,一旦输出超过10个用户名,就输出一个换行(<br>)。第48行,输出当前在线的用户数。第49行,输出“退出登录”的链接。
例15-9 LogoutServlet.java
1. package org.sunxin.ch15.online;
2.
3. import javax.servlet.*;
4. import java.io.*;
5. import javax.servlet.http.*;
6.
7. public class LogoutServlet extends HttpServlet
8. {
9. public void doGet(HttpServletRequest req, HttpServletResponse resp)
10. throws ServletException,IOException
11. {
12. resp.setContentType("text/html;charset=gb2312");
13.
14. HttpSession session=req.getSession();
15. User user=(User)session.getAttribute("user");
16. session.invalidate();
17.
18. PrintWriter out=resp.getWriter();
19. out.println("<html><head><title>退出登录</title></head><body>");
20. out.println(user.getName()+",你已退出登录<br>");
21. out.println("<a href=login.html>重新登录</a>");
22. out.println("</body></html>");
23. out.close();
24. }
25.}
LogoutServlet类用于退出登录。代码第16行,调用HttpSession对象的invalidate()方法,使Session失效,从而删除绑定到这个Session中的User对象,Servlet容器就会调用这个User对象的valueUnbound()方法,从用户列表中删除该用户。
Step3
例15-10 web.xml
…
<servlet>
<servlet-name>OnlineUserServlet</servlet-name>
<servlet-class>
org.sunxin.ch15.online.OnlineUserServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OnlineUserServlet</servlet-name>
<url-pattern>/online/online</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>logout</servlet-name>
<servlet-class>org.sunxin.ch15.online.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>logout</servlet-name>
<url-pattern>/online/logout</url-pattern>
</servlet-mapping>