🥽 使用过滤器改造OA项目。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <welcome-file-list> <welcome-file>welcome</welcome-file> </welcome-file-list> <!-- 登录验证过滤器 --> <filter> <filter-name>loginFilter</filter-name> <filter-class>cw.javaweb.oa.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>loginFilter</filter-name> <!-- 部门相关的操作需要进行登录验证 --> <url-pattern>/dept/*</url-pattern> </filter-mapping> </web-app>
package cw.javaweb.oa.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 请求与响应对象进行强转 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 进行登录验证 // 获取session对象,这里只是进行验证,如果没有session对象不需要创建 HttpSession session = request.getSession(); // 获取用户登录证明 Object username = session.getAttribute("username"); if (session != null && username != null) { // 验证通过 filterChain.doFilter(request, response); } else { // 跳转到index页面 response.sendRedirect(request.getContextPath()); } } @Override public void destroy() { } }
@WebServlet({"/dept/list", "/dept/detail", "/dept/modify", "/dept/delete", "/dept/add"}) public class DeptServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取servlet路径 String servletPath = request.getServletPath(); if ("/dept/list".equals(servletPath)) { doList(request, response); } else if ("/dept/detail".equals(servletPath)) { doDetail(request, response); } else if ("/dept/modify".equals(servletPath)) { doModify(request, response); } else if ("/dept/delete".equals(servletPath)) { doDel(request, response); } else if ("/dept/add".equals(servletPath)) { doAdd(request, response); } } ...... }
🥽 Listener监听器
- 什么是监听器?
- 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。
- 在Servlet中,所有的监听器接口都是以“Listener”结尾。
- 监听器有什么用?
- 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。
- 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。
- Servlet规范中提供了哪些监听器?
- jakarta.servlet包下:
- ServletContextListener
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- jakarta.servlet.http包下:
- HttpSessionListener
- HttpSessionAttributeListener
- 该监听器需要使用@WebListener注解进行标注。
- 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
package com.bjpowernode.javaweb.listener; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionBindingEvent; @WebListener public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener { // 向session域当中存储数据的时候,以下方法被WEB服务器调用。 @Override public void attributeAdded(HttpSessionBindingEvent se) { System.out.println("session data add"); } // 将session域当中存储的数据删除的时候,以下方法被WEB服务器调用。 @Override public void attributeRemoved(HttpSessionBindingEvent se) { System.out.println("session data remove"); } // session域当中的某个数据被替换的时候,以下方法被WEB服务器调用。 @Override public void attributeReplaced(HttpSessionBindingEvent se) { System.out.println("session data replace"); } }
- HttpSessionBindingListener
- 该监听器不需要使用@WebListener进行标注。
- 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
- 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
import jakarta.servlet.http.HttpSessionBindingEvent; import jakarta.servlet.http.HttpSessionBindingListener; /** * 普通的java类。但是它实现了:HttpSessionBindingListener */ public class User1 implements HttpSessionBindingListener { @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("绑定数据"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("解绑数据"); } private String usercode; private String username; private String password; public User1(String usercode, String username, String password) { this.usercode = usercode; this.username = username; this.password = password; } public User1() { } public String getUsercode() { return usercode; } public void setUsercode(String usercode) { this.usercode = usercode; } 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; } }
- HttpSessionIdListener
- session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
- HttpSessionActivationListener
- 监听session对象的钝化和活化的。
- 钝化:session对象从内存存储到硬盘文件。
- 活化:从硬盘文件把session恢复到内存。
- 实现一个监听器的步骤:以ServletContextListener为例。
- 第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法。
在ServletContext对象被创建的时候调用 void contextInitialized(ServletContextEvent event) 在ServletContext对象被销毁的时候调用 void contextDestroyed(ServletContextEvent event)
- 第二步:在web.xml文件中对ServletContextListener进行配置,如下:
<listener> <listener-class>com.bjpowernode.javaweb.listener.MyServletContextListener</listener-class> </listener>
- 当然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener
- 注意:所有监听器中的方法都是不需要javaweb程序员调用的,由服务器来负责调用?什么时候被调用呢?
- 当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。
- 思考一个业务场景:
- 请编写一个功能,记录该网站实时的在线用户的个数。
- 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了。session对象只要新建,则count++,然后将count存储到ServletContext域当中,在页面展示在线人数即可。有会话但是不一定登录在线。
- 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?
- session.setAttribute(“user”, userObj);
- 用户登录的标志是什么?session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中。页面展示在线人数即可。
🥽 实现oa项目中当前登录在线的人数。
- 什么代表着用户登录了?
- session.setAttribute(“user”, userObj); User类型的对象只要往session中存储过,表示有新用户登录。
- 什么代表着用户退出了?
- session.removeAttribute(“user”); User类型的对象从session域中移除了。
- 或者有可能是session销毁了。(session超时)
package com.bjpowernode.oa.bean; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpSessionBindingEvent; import jakarta.servlet.http.HttpSessionBindingListener; public class User implements HttpSessionBindingListener { @Override public void valueBound(HttpSessionBindingEvent event) { // 用户登录了 // User类型的对象向session中存放了。 // 获取ServletContext对象 ServletContext application = event.getSession().getServletContext(); // 获取在线人数。 Object onlinecount = application.getAttribute("onlinecount"); if (onlinecount == null) { application.setAttribute("onlinecount", 1); } else { int count = (Integer)onlinecount; count++; application.setAttribute("onlinecount", count); } } @Override public void valueUnbound(HttpSessionBindingEvent event) { // 用户退出了 // User类型的对象从session域中删除了。 ServletContext application = event.getSession().getServletContext(); Integer onlinecount = (Integer)application.getAttribute("onlinecount"); onlinecount--; application.setAttribute("onlinecount", onlinecount); } private String username; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } 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; } }
package com.bjpowernode.oa.web.action; import com.bjpowernode.oa.bean.User; import com.bjpowernode.oa.utils.DBUtil; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.*; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; // Servlet负责业务的处理 // JSP负责页面的展示。 @WebServlet({"/user/login","/user/exit"}) public class UserServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String servletPath = request.getServletPath(); if("/user/login".equals(servletPath)){ doLogin(request, response); }else if("/user/exit".equals(servletPath)){ doExit(request, response); } } protected void doExit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取session对象,销毁session HttpSession session = request.getSession(false); if (session != null) { // 从session域中删除user对象 session.removeAttribute("user"); // 手动销毁session对象。 session.invalidate(); // 销毁cookie(退出系统将所有的cookie全部销毁) Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { // 设置cookie的有效期为0,表示删除该cookie cookie.setMaxAge(0); // 设置一个下cookie的路径 cookie.setPath(request.getContextPath()); // 删除cookie的时候注意路径问题。 // 响应cookie给浏览器,浏览器端会将之前的cookie覆盖。 response.addCookie(cookie); } } // 换一种方案 /*Cookie cookie1 = new Cookie("username",""); cookie1.setMaxAge(0); cookie1.setPath(request.getContextPath()); Cookie cookie2 = new Cookie("password", ""); cookie2.setMaxAge(0); cookie2.setPath(request.getContextPath()); response.addCookie(cookie1); response.addCookie(cookie2);*/ // 跳转到登录页面 response.sendRedirect(request.getContextPath()); } } protected void doLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { boolean success = false; // 你要做一件什么事儿?验证用户名和密码是否正确。 // 获取用户名和密码 // 前端你是这样提交的:username=admin&password=123 String username = request.getParameter("username"); String password = request.getParameter("password"); // 连接数据库验证用户名和密码 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConnection(); String sql = "select * from t_user where username = ? and password = ?"; // 编译SQL ps = conn.prepareStatement(sql); // 给?传值 ps.setString(1, username); ps.setString(2, password); // 执行SQL rs = ps.executeQuery(); // 这个结果集当中最多只有一条数据。 if (rs.next()) { // 不需要while循环 // 登录成功 success = true; } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } // 登录成功/失败 if (success) { // 获取session对象(这里的要求是:必须获取到session,没有session也要新建一个session对象。) HttpSession session = request.getSession(); // session对象一定不是null //session.setAttribute("username", username); User user = new User(username, password); session.setAttribute("user", user); // 登录成功了,并且用户确实选择了“十天内免登录”功能。 String f = request.getParameter("f"); if("1".equals(f)){ // 创建Cookie对象存储登录名 Cookie cookie1 = new Cookie("username", username); // 创建Cookie对象存储密码 Cookie cookie2 = new Cookie("password", password); // 真实情况下是加密的。 // 设置cookie的有效期为十天 cookie1.setMaxAge(60 * 60 * 24 * 10); cookie2.setMaxAge(60 * 60 * 24 * 10); // 设置cookie的path(只要访问这个应用,浏览器就一定要携带这两个cookie) cookie1.setPath(request.getContextPath()); cookie2.setPath(request.getContextPath()); // 响应cookie给浏览器 response.addCookie(cookie1); response.addCookie(cookie2); } // 成功,跳转到用户列表页面 response.sendRedirect(request.getContextPath() + "/dept/list"); } else { // 失败,跳转到失败页面 response.sendRedirect(request.getContextPath() + "/error.jsp"); } } }