Listener监听器工作原理和应用详解
1. Listener过滤器原理
1.1 Listener 基本信息
概念:监听器就是一个实现了特定接口的Java类。
分类:
- 一类监听器:监听域对象的创建、销毁
- 二类监听器:监听域对象中的属性变更(属性设置、属性替换、属性移除)
- 三类监听器:监听域对象中的java对象的绑定
Servlet监听器:
事件源
:request、session、servletContext三大域对象
监听器
:Servlet对象(三种监听器)
绑定
:web.xml配置 或 @WebListener注解
事件
:域对象发生改变
1.2 Listener 工作原理
- 实现了特定接口的类为监听器,用来监听另一个Java类的方法调用或者属性改变;
- 当被监听的对象发生了方法调用或者属性改变后,监听器的对应方法就会立即执行。
1.3 Listener 基本使用
一类接口:
ServletContextListener
监听ServletContext域对象的创建、销毁
// 服务器启动,ServletContext域对象创建,该监听器方法则执行
public void contextInitialized(ServletContextEvent servletContextEvent)
// 服务器关闭,ServletContext域对象销毁,该监听器方法则执行
public void contextDestroyed(ServletContextEvent servletContextEvent)
HttpSessionListener
监听HttpSession域对象的创建、销毁
// 服务器第一次调用getSession方法时,该监听器方法被执行
public void sessionCreated(HttpSessionEvent httpSessionEvent)
// session过期/调用了invalidate方法销毁session时,该监听器方法被执行
public void sessionDestroyed(HttpSessionEvent httpSessionEvent)
ServletRequestListener
监听ServletRequest域对象的创建、销毁
// 客户端向服务器发送了一个请求,服务器就会为该请求创建一个request对象,该监听器方法就被执行
public void requestInitialized(ServletRequestEvent servletRequestEvent)
// 当服务器为这次请求做出了响应后,将request对象销毁,该监听器方法就被执行
public void requestDestroyed(ServletRequestEvent servletRequestEvent)
二类接口:
ServletContextAttributeListener
HttpSessionAttributeListener
ServletRequestAttributeListener
// 监听ServletContext域对象中属性的【添加】
void attributeAdded(ServletContextAttributeEvent var1);
// 监听ServletContext域对象中属性的【替换】
void attributeReplaced(ServletContextAttributeEvent var1);
// 监听ServletContext域对象中属性的【移除】
void attributeRemoved(ServletContextAttributeEvent var1);
三类接口:
HttpSessionBindingListener
监听session域对象中的java对象的状态(绑定和解绑)
绑定:将java对象存储到session域对象
解绑:将java对象从session域对象移除
- 该监听器不需要在web.xml中配置
/**
* 事件源:Java对象
* 监听器:HttpSessionBindingListener
* 绑定:java对象实现监听器接口
* 事件:java对象在Session中的状态发生改变
*/
public class User implements HttpSessionBindingListener {
private int id;
private String username;
private String password;
/*构造、get/set、toString*/
@Override
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("HttpSession 与 User 绑定");
}
@Override
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("HttpSession 与 User 解绑");
}
}
1.4 Listener 开发步骤
① 定义类实现监听器接口
② 重写方法
③ 配置 web.xml
<listener>
<listener-class>Listener的全限定名</listener-class>
</listener>
1.5 Listener 使用示例
/**
* 事件源:ServletContext
* 监听器:TestServletContextListener
* 绑定:web.xml配置
* 事件:ServletContext对象的创建、销毁
*/
public class TestServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 监听初始化
System.out.println("ServletContext 初始化");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// 监听销毁
System.out.println("ServletContext 销毁");
}
}
2. Listener 过滤器应用
2.1 案例:Listener 过滤器实现统计在线人数(流程图+核心逻辑)
流程图:
核心逻辑:
login.html
<form action="/demo/login" method="post">
账户:<input type="text" name="username" id=""> <br>
密码:<input type="password" name="password" id=""> <br>
<input type="submit" value="登陆">
</form>
User.java
/**
* 事件源:Java对象
* 监听器:HttpSessionBindingListener
* 绑定:java对象实现监听器接口
* 事件:java对象在Session中的状态发生改变
*/
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("HttpSession 与 User 绑定");
// 有用户登陆成功
// 判断是否是第一个登陆的人
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
// 第一个登陆为1,非第一个则++
count = null == count ? 1 : (count += 1);
servletContext.setAttribute("count", count);
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("HttpSession 与 User 解绑");
// 有用户注销登陆
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
// count--是因为在同浏览器下重复登陆时session.setAttribute(name, value)
// 每次会覆盖value值进而触发监听器valueBound()的count++
count--;
servletContext.setAttribute("count", count);
}
private int id;
private String username;
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
LoginServlet.java
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "1234".equals(password)) {
// 登陆成功,修改登陆状态,转发到showIndex页
User user = new User();
user.setUsername(username);
user.setPassword(password);
request.getSession().setAttribute("existUser", user);
// 转发会导致每次刷新页面都会重新登陆,然后被统计新新用户中
//request.getRequestDispatcher("/showIndex").forward(request, response);
response.sendRedirect("/demo/showIndex");
} else {
// 登陆失败,转发到login.html
request.getRequestDispatcher("/demo/login.html").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
LogoutServlet.java
@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 注销登陆:将existUser从session域中移除 或 销毁session
//request.getSession().removeAttribute("existUser");
request.getSession().invalidate();
// 注销成功
request.getRequestDispatcher("/showIndex").forward(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ShowIndexServlet.java
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null == existUser) {
// 不在登陆状态
responseBody.append("您还没有登陆,<a href='/demo/login.html'>请登录</a>");
} else {
// 在登陆状态
responseBody.append("欢迎回来,").append(existUser.getUsername()).append(" <a href='/demo/logout'>注销</a>");
}
// 获取在线人数
ServletContext servletContext = getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
System.out.println("在线人数:" + count);
if (null == count) {
// 没有人登陆时,0人
count = 0;
}
responseBody.append("<br>在线人数为:").append(count).append("人");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}