会话_过滤器_监听器笔记
一:会话
1:Cookie:
cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
- 服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端
- 客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
- cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
- 由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
使用如下:
servletA向响应中增加Cookie:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 创建CookieCookie cookie1 =new Cookie("c1","c1_message");Cookie cookie2 =new Cookie("c2","c2_message");// 将cookie放入响应对象resp.addCookie(cookie1);resp.addCookie(cookie2);}
}
servletB从请求中读取Cookie:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取请求中的cookieCookie[] cookies = req.getCookies();//迭代cookies数组if (null != cookies && cookies.length!= 0) {for (Cookie cookie : cookies) {System.out.println(cookie.getName()+":"+cookie.getValue());}}}
Cookie的时效性
默认情况下Cookie的有效期是一次会话范围内,我们可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上
- 会话级Cookie
- 服务器端并没有明确指定Cookie的存在时间
- 在浏览器端,Cookie数据存在于内存中
- 只要浏览器还开着,Cookie数据就一直都在
- 浏览器关闭,内存中的Cookie数据就会被释放 - 持久化Cookie
- 服务器端明确设置了Cookie的存在时间
- 在浏览器端,Cookie数据会被保存到硬盘上
- Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
- 持久化Cookie到达了预设的时间会被释放
cookie.setMaxAge(int expiry)参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除
访问互联网资源时不能每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,我们可以通过cookie的setPath(String path) 对cookie的路径进行设置:
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 创建CookieCookie cookie1 =new Cookie("c1","c1_message");// 设置cookie的提交路径cookie1.setPath("/web03_war_exploded/servletB");Cookie cookie2 =new Cookie("c2","c2_message");// 将cookie放入响应对象resp.addCookie(cookie1);resp.addCookie(cookie2);}
}
2:Session:
HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象. 客户端在发送请求时,都可以使用自己的session. 这样服务端就可以通过session来记录某个客户端的状态了
- 服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以cookie的形式放入响应对象
- 后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
- 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象
- 通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了
- session也是域对象(后续详细讲解)
@WebServlet("/servletA")
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求中的参数String username = req.getParameter("username");// 获取session对象HttpSession session = req.getSession();// 获取Session的IDString jSessionId = session.getId();System.out.println(jSessionId);// 判断session是不是新创建的sessionboolean isNew = session.isNew();System.out.println(isNew);// 向session对象中存入数据session.setAttribute("username",username);}
}
定义其他Servlet,从session中读取用户名:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取session对象HttpSession session = req.getSession();// 获取Session的IDString jSessionId = session.getId();System.out.println(jSessionId);// 判断session是不是新创建的sessionboolean isNew = session.isNew();System.out.println(isNew);// 从session中取出数据String username = (String)session.getAttribute("username");System.out.println(username);}
时效性:
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在tomcat/conf/web.xml配置为30分钟,我们可以自己在当前项目的web.xml对最大闲置时间进行重新设定,也可以通过HttpSession的API 对最大闲置时间进行设定:
// 设置最大闲置时间
session.setMaxInactiveInterval(60);
也可以直接让session失效:
// 直接让session失效
session.invalidate();
3:三大域对象
域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同
- web项目中,我们一定要熟练使用的域对象分别是 请求域,会话域,应用域
- 请求域对象是HttpServletRequest ,传递数据的范围是一次请求之内及请求转发
- 会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求
- 应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话
域对象的API:
API | 功能 |
---|---|
void setAttribute(String name,String value) | 向域对象中添加/修改数据 |
Object getAttribute(String name); | 从域对象中获取数据 |
removeAttribute(String name); | 移除域对象中的数据 |
测试代码:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 向请求域中放入数据req.setAttribute("request","request-message");//req.getRequestDispatcher("servletB").forward(req,resp);// 向会话域中放入数据HttpSession session = req.getSession();session.setAttribute("session","session-message");// 向应用域中放入数据ServletContext application = getServletContext();application.setAttribute("application","application-message");}
}
取出数据:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从请求域中获取数据String reqMessage =(String)req.getAttribute("request");System.out.println(reqMessage);// 从会话域中获取数据HttpSession session = req.getSession();String sessionMessage =(String)session.getAttribute("session");System.out.println(sessionMessage);// 从应用域中获取数据ServletContext application = getServletContext();String applicationMessage =(String)application.getAttribute("application");System.out.println(applicationMessage);}
}
二:过滤器:
Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一
- Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
- Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法
- Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应
- Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
- Filter是GOF中责任链模式的典型案例
- Filter的常用应用包括但不限于: 登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析… …
API目标:
API | 目标 |
---|---|
default public void init(FilterConfig filterConfig) | 初始化方法,由容器调用并传入初始配置信息filterConfig对象 |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中 |
default public void destroy() | 销毁方法,容器在回收过滤器对象之前调用的方法 |
使用步骤:
1.类的实现:
package com.atguigu.filters;import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LoggingFilter implements Filter {private SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 参数父转子HttpServletRequest request =(HttpServletRequest) servletRequest;HttpServletResponse response =(HttpServletResponse) servletResponse;// 拼接日志文本String requestURI = request.getRequestURI();String time = dateFormat.format(new Date());String beforeLogging =requestURI+"在"+time+"被请求了";// 打印日志System.out.println(beforeLogging);// 获取系统时间long t1 = System.currentTimeMillis();// 放行请求filterChain.doFilter(request,response);// 获取系统时间long t2 = System.currentTimeMillis();// 拼接日志文本String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒";// 打印日志System.out.println(afterLogging);}
}
- doFilter方法中的请求和响应对象是以父接口的形式声明的,实际传入的实参就是HttpServletRequest和HttpServletResponse子接口级别的,可以安全强转
- filterChain.doFilter(request,response); 这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止
- filterChain.doFilter(request,response);在放行时需要传入request和response,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象
注意在导包的时候别导错了。
定义两个Servlet作为目标资源:
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 处理器请求System.out.println("servletA处理请求的方法,耗时10毫秒");// 模拟处理请求耗时try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 处理器请求System.out.println("servletB处理请求的方法,耗时15毫秒");// 模拟处理请求耗时try {Thread.sleep(15);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
配置过滤器以及过滤器的过滤范围:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"version="5.0"><!--配置filter,并为filter起别名--><filter><filter-name>loggingFilter</filter-name><filter-class>com.atguigu.filters.LoggingFilter</filter-class></filter><!--为别名对应的filter配置要过滤的目标资源--><filter-mapping><filter-name>loggingFilter</filter-name><!--通过映射路径确定过滤资源--><url-pattern>/servletA</url-pattern><!--通过后缀名确定过滤资源--><url-pattern>*.html</url-pattern><!--通过servlet别名确定过滤资源--><servlet-name>servletBName</servlet-name></filter-mapping>
</web-app>
- filter-mapping标签中定义了过滤器对那些资源进行过滤
- 子标签url-pattern通过映射路径确定过滤范围
- /servletA 精确匹配,表示对servletA资源的请求进行过滤
- *.html 表示对以.action结尾的路径进行过滤
- /* 表示对所有资源进行过滤
- 一个filter-mapping下可以配置多个url-pattern
- 子标签servlet-name通过servlet别名确定对那些servlet进行过滤
- 使用该标签确定目标资源的前提是servlet已经起了别名
- 一个filter-mapping下可以定义多个servlet-name
- 一个filter-mapping下,servlet-name和url-pattern子标签可以同时存在
两种确定资源的方法都可以。
过滤器生命周期
过滤器作为web项目的组件之一,和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造
阶段 | 对应方法 | 执行时机 | 执行次数 |
---|---|---|---|
创建对象 | 构造器 | web应用启动时 | 1 |
初始化方法 | void init(FilterConfig filterConfig) | 构造完毕 | 1 |
过滤请求 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) | 每次请求 | 多次 |
销毁 | default void destroy() | web应用关闭时 | 1次 |
过滤器链的使用
一个web项目中,可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称之为过滤器链
- 过滤器链中的过滤器的顺序由filter-mapping顺序决定
- 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
- 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
代码:
目标Servlet资源代码:
package com.atguigu.servlet;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet("/servletC")
public class ServletC extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("servletC service method invoked");}
}
三个过滤器代码:
public class Filter1 implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter1 before chain.doFilter code invoked");filterChain.doFilter(servletRequest,servletResponse);System.out.println("filter1 after chain.doFilter code invoked");}
}public class Filter2 implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter2 before chain.doFilter code invoked");filterChain.doFilter(servletRequest,servletResponse);System.out.println("filter2 after chain.doFilter code invoked");}
}public class Filter3 implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("filter3 before chain.doFilter code invoked");filterChain.doFilter(servletRequest,servletResponse);System.out.println("filter3 after chain.doFilter code invoked");}
}
过滤器配置代码:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"version="5.0"><filter><filter-name>filter1</filter-name><filter-class>com.atguigu.filters.Filter1</filter-class></filter><filter><filter-name>filter2</filter-name><filter-class>com.atguigu.filters.Filter2</filter-class></filter><filter><filter-name>filter3</filter-name><filter-class>com.atguigu.filters.Filter3</filter-class></filter><!--filter-mapping的顺序决定了过滤器的工作顺序--><filter-mapping><filter-name>filter1</filter-name><url-pattern>/servletC</url-pattern></filter-mapping><filter-mapping><filter-name>filter2</filter-name><url-pattern>/servletC</url-pattern></filter-mapping><filter-mapping><filter-name>filter3</filter-name><url-pattern>/servletC</url-pattern></filter-mapping></web-app>
我们也可以用注解的方法来实现配置,此时执行顺序是类名的字典序。
@WebFilter(
filterName = "loggingFilter",
initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")},
urlPatterns = {"/servletA","*.html"},
servletNames = {"servletBName"}
)
public class LoggingFilter implements Filter {
/* 内部代码 略 */
}
三:监听器:
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 {// 监听初始化@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContext application = sce.getServletContext();System.out.println("application"+application.hashCode()+" initialized");}// 监听销毁@Overridepublic void contextDestroyed(ServletContextEvent sce) {ServletContext application = sce.getServletContext();System.out.println("application"+application.hashCode()+" destroyed");}// 监听数据增加@Overridepublic 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);}// 监听数据移除@Overridepublic 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);}// 监听数据修改@Overridepublic 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 {@Overrideprotected 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 {@Overrideprotected 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创建@Overridepublic void sessionCreated(HttpSessionEvent se) {HttpSession session = se.getSession();System.out.println("session"+session.hashCode()+" created");}// 监听session销毁@Overridepublic void sessionDestroyed(HttpSessionEvent se) {HttpSession session = se.getSession();System.out.println("session"+session.hashCode()+" destroyed");}// 监听数据增加@Overridepublic 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);}// 监听数据移除@Overridepublic 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);}// 监听数据修改@Overridepublic 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 {@Overrideprotected 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 {@Overrideprotected 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 {// 监听初始化@Overridepublic void requestInitialized(ServletRequestEvent sre) {ServletRequest request = sre.getServletRequest();System.out.println("request"+request.hashCode()+" initialized");}// 监听销毁@Overridepublic void requestDestroyed(ServletRequestEvent sre) {ServletRequest request = sre.getServletRequest();System.out.println("request"+request.hashCode()+" destoryed");}// 监听数据增加@Overridepublic 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);}// 监听数据移除@Overridepublic 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);}// 监听数据修改@Overridepublic 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 {@Overrideprotected 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 {@Overrideprotected 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 {// 监听绑定@Overridepublic 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);}// 监听解除绑定@Overridepublic 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);}
}
- 定义触发监听器的代码
package com.atguigu.listeners;import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;public class MySessionBindingListener implements HttpSessionBindingListener {// 监听绑定@Overridepublic 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);}// 监听解除绑定@Overridepublic 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);}
}
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 {@Overrideprotected 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 {@Overrideprotected 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 {// 监听钝化@Overridepublic void sessionWillPassivate(HttpSessionEvent se) {HttpSession session = se.getSession();System.out.println("session with JSESSIONID "+ session.getId()+" will passivate");}// 监听活化@Overridepublic 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 {@Overrideprotected 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 {@Overridepublic 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{// 登录成功,将用户信息存入sessionreq.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>