Day40
监听器
概念:
监听器用于监听web应用中某些对象信息的创建、销毁、增加,修改,删除等动作的
发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用
监听器对象中的方法。
常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等。类别:
第一大类:监听请求域、会话域、全局域对象的创建和销毁。
第二大类:监听请求域、会话域、全局域对象的属性的创建、替换、销毁。
第三大类:监听会话域(String key - Object value)里value的绑定和解绑、钝化和活化。
第一大类
监听请求域:
servlet:
package com.qf.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/Servlet01") public class Servlet01 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("执行Servlet"); } }
web.xml:
<listener> <listener-class>com.qf.listen.MyServletRequestListener</listener-class> </listener>
listener:
package com.qf.listen; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; public class MyServletRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { ServletRequest request = servletRequestEvent.getServletRequest(); System.out.println("请求对象被销毁了"+request); } @Override public void requestInitialized(ServletRequestEvent servletRequestEvent) { ServletRequest request = servletRequestEvent.getServletRequest(); System.out.println("请求对象被创建了"+request); } }
注:请求对象的生命周期为,创建:客户端发送请求,服务器会创建请求对象。销毁:服务器返回响应后,会把请求对象、响应对象一并销毁。
监听会话域
注意:查看生命周期要把index.jsp改名或者删除,因为项目启动会默认进入index.jsp,会创建会话对象。
web.xml:(手动调整session销毁时间)
<listener> <listener-class>com.qf.listen.MyHttpSessionListener</listener-class> </listener> <session-config> <session-timeout>1</session-timeout> </session-config>
创建一个page01.jsp。
MyHttpSessionListener:
package com.qf.listen; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); System.out.println("会话对象被创建了"+session.getId()); } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { System.out.println("会话对象被销毁了"); } }
注:会话对象的什么周期,创建:服务器使用到了session->request.getSession();销毁:session对象设置的过期时间到后就会被销毁。
思考1:客户端访问html资源是否会创建会话对象?答:不会
思考2:客户端访问jsp资源会创建会话对象?为什么?答:会,因为jsp里9大内置对象中有session对象。
思考3:session对象创建后,客户端发送下一次请求是为什么能找到之前的session对象?答:因为客户端在cookie中存储了session的id->JSESSIONID
思考4:session对象是存活在服务器中的,默认为30分钟的生命周期,可客户端存储的cookie里的JSESSIONID为会话结束时,也就意味着关闭浏览器后,再也找不到之前存储在服务器中的session对象,那怎么解决?
答:设置cookie中JSESSION的过期时间。
package com.qf.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; @WebServlet("/Servlet01") public class Servlet01 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Servlet收到请求"); HttpSession session = request.getSession(); Cookie cookie = new Cookie("JSESSION",session.getId()); cookie.setMaxAge(60*30); response.addCookie(cookie); response.getWriter().println("abc"); } }
监听全局域
web.xml:
<context-param> <param-name>info</param-name> <param-value>abc</param-value> </context-param> <listener> <listener-class>com.qf.listen.MyServletContextListener</listener-class> </listener>
MyServletContextListener:
package com.qf.listen; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); String info = servletContext.getInitParameter("info"); System.out.println("全局对象被创建了"+"--"+info); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("全局对象被销毁了"); } }
注:1.生命周期为项目启动时创建,服务器正常关闭时销毁。
经验:应用场景:
监听全局域对象的创建和销毁,而全局域对象在项目启动时创建,就意味着监听器里的初始化方法在项目启动时就会被调用,所以我们可以把项目初始化的工作放在该方法里。在配置文件中context-param里设置初始数据(建议放在最上面),在监听器的初始化方法中可以用getInitParameter()方法获取数据。
第一大类使用案例-统计在线人数
在全局域监听器中创建:
servletContext.setAttribute("count",0);
在会话域监听器中统计:
package com.qf.listen; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); System.out.println("会话对象被创建了"+session.getId()); ServletContext servletContext = session.getServletContext(); int count = (int) servletContext.getAttribute("count"); count++; servletContext.setAttribute("count",count); } @Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { System.out.println("会话对象被销毁了"); HttpSession session = httpSessionEvent.getSession(); ServletContext servletContext = session.getServletContext(); int count = (int) servletContext.getAttribute("count"); count--; servletContext.setAttribute("count",count); } }
第二大类
请求属性监听器
MyServletRequestAttributeListener(用注解配置):
package com.qf.listen02; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.annotation.WebListener; @WebListener public class MyServletRequestAttributeListener implements ServletRequestAttributeListener { @Override public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) { String name = servletRequestAttributeEvent.getName(); Object value = servletRequestAttributeEvent.getValue(); System.out.println("请求域对象添加了属性:"+name+"---"+value); } @Override public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) { String name = servletRequestAttributeEvent.getName(); Object value = servletRequestAttributeEvent.getValue(); System.out.println("请求域对象删除了属性:"+name+"---"+value); } @Override public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) { String name = servletRequestAttributeEvent.getName(); Object value = servletRequestAttributeEvent.getValue();//获取被替换的值 System.out.println("请求域对象替换了属性:"+name+"---"+value); } }
page.jsp:
<% //操作请求域的属性 request.setAttribute("msg","aaabbbccc");//添加属性 request.setAttribute("msg","bbbcccddd");//替换属性 request.removeAttribute("msg");//删除属性 %>
会话属性监听器
MyHttpSessionAttributeListener:
package com.qf.listen02; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener { @Override public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) { String name = httpSessionBindingEvent.getName(); Object value = httpSessionBindingEvent.getValue(); System.out.println("会话域对象添加了属性"); } @Override public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) { String name = httpSessionBindingEvent.getName(); Object value = httpSessionBindingEvent.getValue(); System.out.println("会话域对象删除了属性"); } @Override public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) { String name = httpSessionBindingEvent.getName(); Object value = httpSessionBindingEvent.getValue(); System.out.println("会话域对象替换了属性"); } }
page02.jsp:
<% //操作会话域的属性 session.setAttribute("msg","aaabbbccc");//添加属性 session.setAttribute("msg","bbbcccddd");//替换属性 session.removeAttribute("msg");//删除属性 %>
全局属性监听器
MyServletContextAttributeListener:
package com.qf.listen02; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; public class MyServletContextAttributeListener implements ServletContextAttributeListener { @Override public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) { String name = servletContextAttributeEvent.getName(); Object value = servletContextAttributeEvent.getValue(); System.out.println("全局对象创建了属性:"+"--"+name+"--"+value); } @Override public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) { String name = servletContextAttributeEvent.getName(); Object value = servletContextAttributeEvent.getValue(); System.out.println("全局对象创建了属性:"+"--"+name+"--"+value); } @Override public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) { String name = servletContextAttributeEvent.getName(); Object value = servletContextAttributeEvent.getValue(); System.out.println("全局对象创建了属性:"+"--"+name+"--"+value); } }
page03.jsp:
<% //操作全局域的属性 application.setAttribute("msg","aaabbbccc");//添加属性 application.setAttribute("msg","bbbcccddd");//替换属性 application.removeAttribute("msg");//删除属性 %>
第三大类
HttpSessionBindingListener:监听JavaBean对象在session中的绑定(添加)和解绑(删除),即监听某一个具体的类对象,第二大类是监听所有的类对象。
HttpSessionActivationListener:监听JavaBean对象在session中的钝化(序列化)和活化(反序列化)
User:(实现HttpSessionBindingListener,HttpSessionActivationListener,Serializable)
package com.qf.listen03; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionEvent; import java.io.Serializable; public class User implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable { private String username; private String password; private String name; private String nickName; public User(String username, String password, String name, String nickName) { this.username = username; this.password = password; this.name = name; this.nickName = nickName; } public User() { } 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; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}'; } @Override public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) { System.out.println(this+"被session钝化了"); } @Override public void sessionDidActivate(HttpSessionEvent httpSessionEvent) { System.out.println(this+"被session活化了"); } @Override public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) { System.out.println(this+"绑定到session中了"); } @Override public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) { System.out.println(this+"从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="C:\\text"/> </Manager> </Context>
Servlet02:
package com.qf.servlet; import com.qf.listen03.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/Servlet02") public class Servlet02 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Servlet02接收到请求了..."); HttpSession session = request.getSession(); //注意:将user对象添加到session中,可以叫做User对象绑定到session中 --- 绑定 session.setAttribute("user",new User("1233211234567","123132","zs","fwkt")); //注意:将user对象从session中删除,可以叫做User对象从session中解绑了 --- 解绑 session.removeAttribute("user"); } }
Servlet03:
package com.qf.servlet; import com.qf.listen03.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/Servlet03") public class Servlet03 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Servlet03接收到请求了..."); HttpSession session = request.getSession(); session.setAttribute("user",new User("1233211234567","123132","zs","fwkt")); } }
注意:将User对象添加到session中,可以叫做User对象绑定到session中 — 绑定
将User对象从session中删除,可以叫做User对象从session中解绑 — 解绑
面试题:session的钝化与活化:
过滤器
简介
Filter:过滤器,拦截访问web资源的请求与响应操作
Servlet API中提供了Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个Java类称之为过滤器。
创建与使用
场景:welcome.html向Servlet01发送请求:
package com.qf.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/Servlet01.action") public class Servlet01 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("Servlet收到请求了"); } }
过滤器:
package com.qf.filter; import javax.servlet.*; import java.io.IOException; public class Filter01 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("Filter01 --- init()"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter01 --- doFilter() -- 放行前"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("Filter01 --- doFilter() -- 放行后"); } @Override public void destroy() { System.out.println("Filter01 --- destroy()"); } }
配置文件:
<welcome-file-list> <welcome-file>welcome.html</welcome-file> </welcome-file-list> <filter> <filter-name>Filter01</filter-name> <filter-class>com.qf.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>Filter01</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
生命周期
创建:项目启动时创建 – 构造方法、init()
销毁:项目正常关闭时销毁 – destroy()
注意:Filter对象是单例的。
当有多个过滤器时,
配置文件:
<filter> <filter-name>Filter01</filter-name> <filter-class>com.qf.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>Filter01</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter> <filter-name>Filter02</filter-name> <filter-class>com.qf.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>Filter02</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping> <filter> <filter-name>Filter03</filter-name> <filter-class>com.qf.filter.Filter01</filter-class> </filter> <filter-mapping> <filter-name>Filter03</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
思考:多个Filter的创建顺序?多个Filter调用顺序?
创建顺序:无序,底层是多线程抢资源。调用顺序为web.xml里的配置顺序(推荐)。如果是使用注解配置,是按照类名字典排序。
案例1 编码过滤器
WEB-学生管理系统中写一个Encoderfilter类进行编码过滤(就不必在servlet中设置了)
配置文件(标准写法要求匹配所有的action,但是由于之前项目中servlet后没有加.action,故此处偷懒直接写/*):
<welcome-file-list> <welcome-file>welcome.html</welcome-file> </welcome-file-list> <filter> <filter-name>EncoderFilter</filter-name> <filter-class>com.qf.filter.EncoderFilter</filter-class> </filter> <filter-mapping> <filter-name>EncoderFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
过滤器:
package com.qf.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class EncoderFilter implements Filter { private String code; @Override public void init(FilterConfig filterConfig) throws ServletException { code = filterConfig.getInitParameter("code"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; request.setCharacterEncoding(code); response.setContentType("text/html;charset="+code); filterChain.doFilter(request,response); } @Override public void destroy() { } }
案例2 登录权限过滤器
写一个LoginFilter。
配置文件:
<filter> <filter-name>LoginFilter</filter-name> <filter-class>com.qf.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
LoginFilter:
package com.qf.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; String requestURI = request.getRequestURI(); System.out.println(requestURI); // StringBuffer requestURL = request.getRequestURL(); // System.out.println(requestURL); HttpSession session = request.getSession(); String username = (String) session.getAttribute("username"); String role = (String) session.getAttribute("role"); String name = (String) session.getAttribute("name"); if(requestURI.equals("/Day37_war_exploded/register.jsp")|| requestURI.equals("/Day37_war_exploded/login.jsp")|| requestURI.equals("/Day37_war_exploded/CodeServlet")|| requestURI.equals("/Day37_war_exploded/LoginServlet")|| requestURI.equals("/Day37_war_exploded/")|| requestURI.equals("/Day37_war_exploded/welcome.html") ){ filterChain.doFilter(request,response); }else{ if(username!=null&&role!=null&&name!=null){ if("student".equals(role) && requestURI.contains("QueryAllStuServlet")) { response.sendRedirect("index.jsp"); } else{ filterChain.doFilter(request,response); } } } } @Override public void destroy() { } }
案例3 敏感词过滤器
场景:在学生详情页面写一个建议,跳转到proposal.jsp,里面写一个表单,提交到ProposalServlet
<%-- Created by IntelliJ IDEA. User: Gu Date: 2024-06-17 Time: 17:37 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h2>建议</h2> <form action="ProposalServlet" method="post"> 建议:<input type="text" name="proposal"/> <input type="submit" value="提交"/> <button type="button" οnclick="fun01()">返回</button> </form> <script type="text/javascript"> function fun01(){ window.location = "index.jsp"; } </script> </body> </html>
ProposalServlet(输出建议):
package com.qf.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/ProposalServlet") public class ProposalServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String proposal = (String) request.getParameter("proposal"); response.getWriter().println(proposal); } }
MyHttpServletRequestWrapper包装类,继承HttpServletRequestWrapper类(构造方法、重写getParameter方法):
package com.qf.pojo; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { public MyHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(String name) { String parameter = super.getParameter(name); if(parameter!=null){ parameter = parameter.replaceAll("<","<"); parameter = parameter.replaceAll(">",">"); parameter = parameter.replaceAll("傻逼","**"); } return parameter; } }
SensitiveWordsFilter:
package com.qf.filter; import com.qf.pojo.MyHttpServletRequestWrapper; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class SensitiveWordFilter 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; MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(request); filterChain.doFilter(requestWrapper,servletResponse); } @Override public void destroy() { } }