[关闭]
@pastqing 2015-01-08T17:08:43.000000Z 字数 4360 阅读 2967

javaEE监听器Listener(一)

java


此文指针对小白, 加上日常记录之用。 小牛, 大牛勿进。
Listener是Servlet的一个高级特性, 它能监听java web程序中的事件, 例如创建、 修改、 删除Session, request, context等。

Part1. Listener的基本原理

说到Listener, 就得说说java的事件监听机制(这可是常见面试题):

这个图和咱们的监听器有毛关系?? 聪明的筒子们应该发现, 监听器用上了设计模式中的观察者模式。可见java的学习离不开设计模式的学习, 有兴趣的可以读读jdk源码里面包含了大量的设计模式。

Part2. Listener的基本应用

常规的应用可以参见云姐的博客云姐的监听器学习笔记, 在这里我补充两个。

一、监听对象, 这里以监听session内的对象为例:

这里用到两种Listener,分别是HttpSessionBindingListenerHttpSessionActivationListener, 它们的触发时机分别为:

关于钝化:服务器关闭时, 会将Session里的内容保存到硬盘上(保存到哪?I don‘t know),这个就叫钝化。服务器重新启动时, 会将session内容从硬盘中重新加载。

请看下面的例子:

  1. public class PersonInfo implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable {
  2. private String name;
  3. private Date dateCreated;
  4. /**
  5. * 该对象被存入session前调用
  6. * @see HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
  7. */
  8. public void valueBound(HttpSessionBindingEvent arg0) {
  9. HttpSession session = arg0.getSession(); //记录它所在的session
  10. String name = arg0.getName(); //得到其在session中的属性名称
  11. System.out.println(this + "被绑定到session " + session.getId() + " name: " + name);
  12. this.setDateCreated(new Date());
  13. }
  14. /**
  15. * 从硬盘中读入session后调用
  16. * @see HttpSessionActivationListener#sessionDidActivate(HttpSessionEvent)
  17. */
  18. public void sessionDidActivate(HttpSessionEvent arg0) {
  19. HttpSession session = arg0.getSession();
  20. System.out.println(this + "已经成功从硬盘中加载 sessionID:" + session.getId() );
  21. }
  22. /**
  23. * 即将被钝化到硬盘时调用(服务器关闭时, session信息会被保存到硬盘上)
  24. * @see HttpSessionActivationListener#sessionWillPassivate(HttpSessionEvent)
  25. */
  26. public void sessionWillPassivate(HttpSessionEvent arg0) {
  27. HttpSession session = arg0.getSession();
  28. System.out.println(this + "已经成功钝化到硬盘 sessionID:" + session.getId() );
  29. }
  30. /**
  31. * 从session中移除对象时调用
  32. * @see HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
  33. */
  34. public void valueUnbound(HttpSessionBindingEvent arg0) {
  35. HttpSession session = arg0.getSession();
  36. String name = arg0.getName();
  37. System.out.println(this + "从session中移除的session, sessionID: " + session.getId() + " name: " + name);
  38. }

这里我定义了一个personInfo类, 实现了上面所说的监听器。经过我的测试在服务器热部署,或者部署的时候会调用sessionDidActivate, 热部署的时候也会调用钝化方法。

这里提一下java序列化Serializable, 这里要用到序列化的原因大概是要往硬盘里写, 那么怎么把对象写进硬盘里呢, 就是将对象序列化后放入内存, 从内存中写到硬盘里。至于Serializable我也不是很懂, 有兴趣可以参考这个文章java序列化


二、 单态登陆小例子

单态登陆就是一个账号只能在一台机器上登陆, 如果在其他机器上登陆, 则原来的登陆自动失效。以下只贴出Listener的实现代码, 至于前后交互大家自己设计就好。

实现单态登陆有很多方法, 这里我们用HttpSessionAttributeListener这个Listener来实现。

  1. public class LoginListener implements HttpSessionAttributeListener {
  2. //定义一个map用来存放session对象
  3. Map<String, HttpSession> map = new HashMap<String, HttpSession>();
  4. public void attributeAdded(HttpSessionBindingEvent arg0) {
  5. System.out.println("start add");
  6. String name = arg0.getName();
  7. if("personInfo".equals(name)) {
  8. LoginPerson loginPerson = (LoginPerson)arg0.getValue();
  9. if( map.get(loginPerson.getAccount()) != null ) { //若map中存在账号
  10. //账号在不同的会话中登陆, 注销一个, 实现单态登陆
  11. HttpSession session = map.get(loginPerson.getAccount());
  12. LoginPerson oldPerson = (LoginPerson)session.getAttribute("personInfo");
  13. System.out.println("账号 " + oldPerson.getAccount() + " 在 " + oldPerson.getIp() + " 已经登陆, 将被迫下线");
  14. session.removeAttribute("personInfo");
  15. session.setAttribute("msg", "您的账号已在其他机器上登陆");
  16. map.put(loginPerson.getAccount(), arg0.getSession());
  17. System.out.println("账号 " + loginPerson.getAccount() + " 在 " + loginPerson.getIp() + " 登陆");
  18. }
  19. }
  20. }
  21. public void attributeRemoved(HttpSessionBindingEvent arg0) {
  22. System.out.println("start remove");
  23. //注销时调用
  24. String name = arg0.getName();
  25. if( "personInfo".equals(name) ) {
  26. LoginPerson loginPerson = (LoginPerson)arg0.getValue();
  27. map.remove(loginPerson.getAccount());
  28. System.out.println("账号: " + loginPerson.getAccount() + "已注销");
  29. }
  30. }
  31. public void attributeReplaced(HttpSessionBindingEvent arg0) {
  32. System.out.println("start replace");
  33. String name = arg0.getName();
  34. if("personInfo".equals(name)) { //没有注销的情况下, 使用其他账号登陆
  35. LoginPerson oldPerson = (LoginPerson)arg0.getValue();
  36. map.remove(oldPerson); //移除旧的登陆信息
  37. LoginPerson loginPerson = (LoginPerson)arg0.getSession().getAttribute("personInfo"); //新的登陆信息
  38. //验证新的登陆是否在其他会话中登陆
  39. if(map.get(loginPerson.getAccount()) != null ) {
  40. HttpSession session = map.get(loginPerson.getAccount());
  41. session.removeAttribute("personInfo");
  42. session.setAttribute("msg", "您的账号已在其他机器上登陆");
  43. System.out.println("msg已经存入session");
  44. }
  45. map.put(loginPerson.getAccount(), arg0.getSession());
  46. }
  47. }
  48. }

代码加注释应该很清楚了, 利用监听器可以很方便的实现单态登陆, 但这里也有不少细节的处理, 比如如何管理错误信息等。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注