三 监听器
3.1 监听器概述
监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象
监听器是GOF设计模式中,观察者模式的典型案例
观察者模式: 当被观察的对象发生某些改变时, 观察者自动采取对应的行动的一种设计模式
监听器使用的感受类似JS中的事件,被观察的对象发生某些情况时,自动触发代码的执行
监听器并不监听web项目中的所有组件,仅仅是对三大域对象做相关的事件监听
监听器的分类
web中定义八个监听器接口作为监听器的规范,这八个接口按照不同的标准可以形成不同的分类
按监听的对象划分
application域监听器 ServletContextListener ServletContextAttributeListener
session域监听器 HttpSessionListener HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener
request域监听器 ServletRequestListener ServletRequestAttributeListener
按监听的事件分
域对象的创建和销毁监听器 ServletContextListener HttpSessionListener ServletRequestListener
域对象数据增删改事件监听器 ServletContextAttributeListener HttpSessionAttributeListener ServletRequestAttributeListener
其他监听器 HttpSessionBindingListener HttpSessionActivationListener
3.2 监听器的六个主要接口
3.2.1 application域监听器
ServletContextListener 监听ServletContext对象的创建与销毁
方法名 | 作用 |
contextInitialized(ServletContextEvent sce) | ServletContext创建时调用 |
contextDestroyed(ServletContextEvent sce) | ServletContext销毁时调用 |
ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。
ServletContextAttributeListener 监听ServletContext中属性的添加、移除和修改
方法名 | 作用 |
attributeAdded(ServletContextAttributeEvent scab) | 向ServletContext中添加属性时调用 |
attributeRemoved(ServletContextAttributeEvent scab) | 从ServletContext中移除属性时调用 |
attributeReplaced(ServletContextAttributeEvent scab) | 当ServletContext中的属性被修改时调用 |
- ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getServletContext() | 获取ServletContext对象 |
测试代码
- 定义监听器
package com.atguigu.listeners; import jakarta.servlet.*; import jakarta.servlet.annotation.WebListener; @WebListener public class ApplicationListener implements ServletContextListener , ServletContextAttributeListener { // 监听初始化 @Override public void contextInitialized(ServletContextEvent sce) { ServletContext application = sce.getServletContext(); System.out.println("application"+application.hashCode()+" initialized"); } // 监听销毁 @Override public void contextDestroyed(ServletContextEvent sce) { ServletContext application = sce.getServletContext(); System.out.println("application"+application.hashCode()+" destroyed"); } // 监听数据增加 @Override public void attributeAdded(ServletContextAttributeEvent scae) { String name = scae.getName(); Object value = scae.getValue(); ServletContext application = scae.getServletContext(); System.out.println("application"+application.hashCode()+" add:"+name+"="+value); } // 监听数据移除 @Override public void attributeRemoved(ServletContextAttributeEvent scae) { String name = scae.getName(); Object value = scae.getValue(); ServletContext application = scae.getServletContext(); System.out.println("application"+application.hashCode()+" remove:"+name+"="+value); } // 监听数据修改 @Override public void attributeReplaced(ServletContextAttributeEvent scae) { String name = scae.getName(); Object value = scae.getValue(); ServletContext application = scae.getServletContext(); Object newValue = application.getAttribute(name); System.out.println("application"+application.hashCode()+" change:"+name+"="+value+" to "+newValue); } }
- 定义触发监听器的代码
// ServletA用于向application域中放入数据 @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 向application域中放入数据 ServletContext application = this.getServletContext(); application.setAttribute("k1","v1"); application.setAttribute("k2","v2"); } } // ServletB用于向application域中修改和移除数据 @WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext appliation = getServletContext(); // 修改application域中的数据 appliation.setAttribute("k1","value1"); // 删除application域中的数据 appliation.removeAttribute("k2"); } }
3.2.2 session域监听器
HttpSessionListener 监听HttpSession对象的创建与销毁
方法名 | 作用 |
sessionCreated(HttpSessionEvent hse) | HttpSession对象创建时调用 |
sessionDestroyed(HttpSessionEvent hse) | HttpSession对象销毁时调用 |
- HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。
HttpSessionAttributeListener 监听HttpSession中属性的添加、移除和修改
方法名 | 作用 |
attributeAdded(HttpSessionBindingEvent se) | 向HttpSession中添加属性时调用 |
attributeRemoved(HttpSessionBindingEvent se) | 从HttpSession中移除属性时调用 |
attributeReplaced(HttpSessionBindingEvent se) | 当HttpSession中的属性被修改时调用 |
- HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getSession() | 获取触发事件的HttpSession对象 |
测试代码
- 定义监听器
package com.atguigu.listeners; import jakarta.servlet.*; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.http.*; @WebListener public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener { // 监听session创建 @Override public void sessionCreated(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("session"+session.hashCode()+" created"); } // 监听session销毁 @Override public void sessionDestroyed(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("session"+session.hashCode()+" destroyed"); } // 监听数据增加 @Override public void attributeAdded(HttpSessionBindingEvent se) { String name = se.getName(); Object value = se.getValue(); HttpSession session = se.getSession(); System.out.println("session"+session.hashCode()+" add:"+name+"="+value); } // 监听数据移除 @Override public void attributeRemoved(HttpSessionBindingEvent se) { String name = se.getName(); Object value = se.getValue(); HttpSession session = se.getSession(); System.out.println("session"+session.hashCode()+" remove:"+name+"="+value); } // 监听数据修改 @Override public void attributeReplaced(HttpSessionBindingEvent se) { String name = se.getName(); Object value = se.getValue(); HttpSession session = se.getSession(); Object newValue = session.getAttribute(name); System.out.println("session"+session.hashCode()+" change:"+name+"="+value+" to "+newValue); } }
- 定义触发监听器的代码
// servletA用于创建session并向session中放数据 @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 创建session,并向session中放入数据 HttpSession session = req.getSession(); session.setAttribute("k1","v1"); session.setAttribute("k2","v2"); } } // servletB用于修改删除session中的数据并手动让session不可用 @WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); // 修改session域中的数据 session.setAttribute("k1","value1"); // 删除session域中的数据 session.removeAttribute("k2"); // 手动让session不可用 session.invalidate(); } }
3.2.3 request域监听器
ServletRequestListener 监听ServletRequest对象的创建与销毁
方法名 | 作用 |
requestInitialized(ServletRequestEvent sre) | ServletRequest对象创建时调用 |
requestDestroyed(ServletRequestEvent sre) | ServletRequest对象销毁时调用 |
- ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。
ServletRequestAttributeListener 监听ServletRequest中属性的添加、移除和修改
方法名 | 作用 |
attributeAdded(ServletRequestAttributeEvent srae) | 向ServletRequest中添加属性时调用 |
attributeRemoved(ServletRequestAttributeEvent srae) | 从ServletRequest中移除属性时调用 |
attributeReplaced(ServletRequestAttributeEvent srae) | 当ServletRequest中的属性被修改时调用 |
- ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
getName() | 获取修改或添加的属性名 |
getValue() | 获取被修改或添加的属性值 |
getServletRequest () | 获取触发事件的ServletRequest对象 |
- 定义监听器
package com.atguigu.listeners; import jakarta.servlet.*; import jakarta.servlet.annotation.WebListener; @WebListener public class RequestListener implements ServletRequestListener , ServletRequestAttributeListener { // 监听初始化 @Override public void requestInitialized(ServletRequestEvent sre) { ServletRequest request = sre.getServletRequest(); System.out.println("request"+request.hashCode()+" initialized"); } // 监听销毁 @Override public void requestDestroyed(ServletRequestEvent sre) { ServletRequest request = sre.getServletRequest(); System.out.println("request"+request.hashCode()+" destoryed"); } // 监听数据增加 @Override public void attributeAdded(ServletRequestAttributeEvent srae) { String name = srae.getName(); Object value = srae.getValue(); ServletRequest request = srae.getServletRequest(); System.out.println("request"+request.hashCode()+" add:"+name+"="+value); } // 监听数据移除 @Override public void attributeRemoved(ServletRequestAttributeEvent srae) { String name = srae.getName(); Object value = srae.getValue(); ServletRequest request = srae.getServletRequest(); System.out.println("request"+request.hashCode()+" remove:"+name+"="+value); } // 监听数据修改 @Override public void attributeReplaced(ServletRequestAttributeEvent srae) { String name = srae.getName(); Object value = srae.getValue(); ServletRequest request = srae.getServletRequest(); Object newValue = request.getAttribute(name); System.out.println("request"+request.hashCode()+" change:"+name+"="+value+" to "+newValue); } }
- 定义触发监听器的代码
// servletA向请求域中放数据 @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 向request中增加数据 req.setAttribute("k1","v1"); req.setAttribute("k2","v2"); // 请求转发 req.getRequestDispatcher("servletB").forward(req,resp); } } // servletB修改删除域中的数据 @WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 修改request域中的数据 req.setAttribute("k1","value1"); // 删除session域中的数据 req.removeAttribute("k2"); } }
3.3 session域的两个特殊监听器
3.3.3 session绑定监听器
HttpSessionBindingListener 监听当前监听器对象在Session域中的增加与移除
方法名 | 作用 |
valueBound(HttpSessionBindingEvent event) | 该类的实例被放到Session域中时调用 |
valueUnbound(HttpSessionBindingEvent event) | 该类的实例从Session中移除时调用 |
- HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 | 作用 |
getName() | 获取当前事件涉及的属性名 |
getValue() | 获取当前事件涉及的属性值 |
getSession() | 获取触发事件的HttpSession对象 |
测试代码
- 定义监听器
package com.atguigu.listeners; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionBindingEvent; import jakarta.servlet.http.HttpSessionBindingListener; public class MySessionBindingListener implements HttpSessionBindingListener { // 监听绑定 @Override public void valueBound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName(); System.out.println("MySessionBindingListener"+this.hashCode()+" binding into session"+session.hashCode()+" with name "+name); } // 监听解除绑定 @Override public void valueUnbound(HttpSessionBindingEvent event) { HttpSession session = event.getSession(); String name = event.getName(); System.out.println("MySessionBindingListener"+this.hashCode()+" unbond outof session"+session.hashCode()+" with name "+name); } }
- 定义触发监听器的代码
@WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); // 绑定监听器 session.setAttribute("bindingListener",new MySessionBindingListener()); // 解除绑定监听器 session.removeAttribute("bindingListener"); } }
3.3.4 钝化活化监听器
HttpSessionActivationListener 监听某个对象在Session中的序列化与反序列化。
方法名 | 作用 |
sessionWillPassivate(HttpSessionEvent se) | 该类实例和Session一起钝化到硬盘时调用 |
sessionDidActivate(HttpSessionEvent se) | 该类实例和Session一起活化到内存时调用 |
HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象。
什么是钝化活化
session对象在服务端是以对象的形式存储于内存的,session过多,服务器的内存也是吃不消的
而且一旦服务器发生重启,所有的session对象都将被清除,也就意味着session中存储的不同客户端的登录状态丢失
为了分摊内存 压力并且为了保证session重启不丢失,我们可以设置将session进行钝化处理
在关闭服务器前或者到达了设定时间时,对session进行序列化到磁盘,这种情况叫做session的钝化
在服务器启动后或者再次获取某个session时,将磁盘上的session进行反序列化到内存,这种情况叫做session的活化
如何配置钝化活化
在web目录下,添加 META-INF下创建Context.xml
- 文件中配置钝化
<?xml version="1.0" encoding="UTF-8"?> <Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> <Store className="org.apache.catalina.session.FileStore" directory="d:\mysession"></Store> </Manager> </Context>
- 请求servletA,获得session,并存入数据,然后重启服务器
@WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); // 添加数据 session.setAttribute("k1","v1"); } }
- 请求servletB获取session,获取重启前存入的数据
@WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Object v1 = session.getAttribute("k1"); System.out.println(v1); } }
如何监听钝化活化
- 定义监听器
package com.atguigu.listeners; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionActivationListener; import jakarta.servlet.http.HttpSessionEvent; import java.io.Serializable; public class ActivationListener implements HttpSessionActivationListener, Serializable { // 监听钝化 @Override public void sessionWillPassivate(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("session with JSESSIONID "+ session.getId()+" will passivate"); } // 监听活化 @Override public void sessionDidActivate(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("session with JSESSIONID "+ session.getId()+" did activate"); } }
- 定义触发监听器的代码
@WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); // 添加数据 session.setAttribute("k1","v1"); // 添加钝化活化监听器 session.setAttribute("activationListener",new ActivationListener()); } }
四 案例开发-日程管理-第三期
4.1 过滤器控制登录校验
需求说明:未登录状态下不允许访问showShedule.html和SysScheduleController相关增删改处理,重定向到login.html,登录成功后可以自由访问
- 开发登录过滤器,对指定资源的请求进行过滤
package com.atguigu.schedule.filters; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.io.IOException; @WebFilter(urlPatterns = {"/showSchedule.html","/schedule/*"}) public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request =(HttpServletRequest) servletRequest; HttpServletResponse response =(HttpServletResponse) servletResponse; HttpSession session = request.getSession(); Object sysUser = session.getAttribute("sysUser"); if(null != sysUser){ // session中如果存在登录的用户 代表用户登录过,则放行 filterChain.doFilter(servletRequest,servletResponse); }else{ //用户未登录,重定向到登录页 response.sendRedirect("/login.html"); } } }
- 修改用户登录请求的login方法,登录成功时,将用户信息存入session
/** * 用户登录的业务接口 * @param req * @param resp * @throws ServletException * @throws IOException */ protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 接收用户请求参数 // 获取要注册的用户名密码 String username = req.getParameter("username"); String userPwd = req.getParameter("userPwd"); // 调用服务层方法,根据用户名查询数据库中是否有一个用户 SysUser loginUser =userService.findByUsername(username); if(null == loginUser){ // 没有根据用户名找到用户,说明用户名有误 resp.sendRedirect("/loginUsernameError.html"); }else if(! loginUser.getUserPwd().equals(MD5Util.encrypt(userPwd))){ // 用户密码有误, resp.sendRedirect("/loginUserPwdError.html"); }else{ // 登录成功,将用户信息存入session req.getSession().setAttribute("sysUser",loginUser); // 登录成功,重定向到日程展示页 resp.sendRedirect("/showSchedule.html"); } }
五 Ajax
4.1 什么是ajax
- AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
- AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
- AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
- AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
- XMLHttpRequest 只是实现 Ajax 的一种方式。
ajax工作原理:
- 简单来说,我们之前发的请求通过类似 form表单标签,a标签 这种方式,现在通过 运行js代码动态决定什么时候发送什么样的请求
- 通过运行JS代码发送的请求浏览器可以不用跳转页面 ,我们可以在JS代码中决定是否要跳转页面
- 通过运行JS代码发送的请求,接收到返回结果后,我们可以将结果通过dom编程渲染到页面的某些元素上,实现局部更新
4.2 如何实现ajax请求
原生javascript方式进行ajax(了解):
<script> function loadXMLDoc(){ var xmlhttp=new XMLHttpRequest(); // 设置回调函数处理响应结果 xmlhttp.onreadystatechange=function(){ if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("myDiv").innerHTML=xmlhttp.responseText; } } // 设置请求方式和请求的资源路径 xmlhttp.open("GET","/try/ajax/ajax_info.txt",true); // 发送请求 xmlhttp.send(); } </script>