会话
会话管理概述
为什么需要会话管理
HTTP是无状态协议
- 无状态就是不保存状态,即无状态协议(stateless),HTTP协议自身不对请求和响应之间的通信状态进行保存,也就是说,在HTTP协议这个级别,协议对于发送过的请求或者响应都不做持久化处理
- 简单理解:浏览器发送请求,服务器接收并响应,但是服务器不记录请求是否来自哪个浏览器,服务器没记录浏览器的特征,就是客户端的状态
举例: 张三去一家饭馆点了几道菜,觉得味道不错,第二天又去了,对老板说,还点上次的那几道菜
- 无状态: 老板没有记录张三是否来过,更没有记录上次他点了那些菜,张三只能重新再点一遍
- 有状态: 老板把每次来吃饭的用户都做好记录,查阅一下之前的记录,查到了张三之前的菜单,直接下单
1.1.2 会话管理实现的手段
Cookie和Session配合解决
- cookie是在客户端保留少量数据的技术,主要通过响应头向客户端响应一些客户端要保留的信息
- session是在服务端保留更多数据的技术,主要通过HttpSession对象保存一些和客户端相关的信息
- cookie和session配合记录请求状态
举例: 张三去银行办业务
- 张三第一次去某个银行办业务,银行会为张三开户(Session),并向张三发放一张银行卡(cookie)
- 张三后面每次去银行,就可以携带之间的银行卡(cookie),银行根据银行卡找到之前张三的账户(session)
Cookie
Cookie概述
cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
- 服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端
- 客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
- cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
- 由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
原理图
应用场景举例
记录用户名
当我们在用户名的输入框中输入完用户名后,浏览器记录用户名,下一次再访问登录页面时,用户名自动填充到用户名的输入框.
保存电影播放进度
在网页上播放电影的时候,如果中途退出浏览器了,下载再打开浏览器播放同一部电影的时候,会自动跳转到上次退出时候的进度,因为在播放的时候会将播放进度保存到cookie中
Cookie的使用
servletA向响应中增加Cookie
1 2 3 4 5 6 7 8 9 10 11 12
| @WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie1 =new Cookie("c1","c1_message"); Cookie cookie2 =new Cookie("c2","c2_message"); resp.addCookie(cookie1); resp.addCookie(cookie2); } }
|
servletB从请求中读取Cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); 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删除
- servletA设置一个Cookie为持久化cookie
1 2 3 4 5 6 7 8 9 10 11 12 13
| @WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie1 =new Cookie("c1","c1_message"); cookie1.setMaxAge(60); Cookie cookie2 =new Cookie("c2","c2_message"); resp.addCookie(cookie1); resp.addCookie(cookie2); } }
|
- servletB接收Cookie,浏览器中间发生一次重启再请求servletB测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); if (null != cookies && cookies.length!= 0) { for (Cookie cookie : cookies) { System.out.println(cookie.getName()+":"+cookie.getValue()); } } } }
|
Cookie的提交路径
访问互联网资源时不能每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,我们可以通过cookie的setPath(String path) 对cookie的路径进行设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie1 =new Cookie("c1","c1_message"); cookie1.setPath("/web03_war_exploded/servletB"); Cookie cookie2 =new Cookie("c2","c2_message"); resp.addCookie(cookie1); resp.addCookie(cookie2); } }
|
Session
HttpSession概述
HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象. 客户端在发送请求时,都可以使用自己的session. 这样服务端就可以通过session来记录某个客户端的状态了
- 服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以cookie的形式放入响应对象
- 后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
- 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象
- 通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了
- session也是域对象(后续详细讲解)
原理图如下
应用场景
记录用户的登录状态
用户登录后,将用户的账号等敏感信息存入session
记录用户操作的历史
例如记录用户的访问痕迹,用户的购物车信息等临时性的信息
HttpSession的使用
用户提交form表单到ServletA,携带用户名,ServletA获取session 将用户名存到Session,用户再请求其他任意Servlet,获取之间存储的用户
1 2 3 4 5
| <form action="servletA" method="post"> 用户名: <input type="text" name="username"> <input type="submit" value="提交"> </form>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); HttpSession session = req.getSession(); String jSessionId = session.getId(); System.out.println(jSessionId); boolean isNew = session.isNew(); System.out.println(isNew); session.setAttribute("username",username);
} }
|
- 响应中收到了一个JSESSIONID的cookie
- 定义其他Servlet,从session中读取用户名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); String jSessionId = session.getId(); System.out.println(jSessionId); boolean isNew = session.isNew(); System.out.println(isNew); String username = (String)session.getAttribute("username"); System.out.println(username); } }
|
- 请求中携带了一个JSESSIONID的cookie
getSession方法的处理逻辑
HttpSession时效性
为什么要设置session的时效
- 用户量很大之后,Session对象相应的也要创建很多。如果一味创建不释放,那么服务器端的内存迟早要被耗尽。
- 客户端关闭行为无法被服务端直接侦测,或者客户端较长时间不操作也经常出现,类似这些的情况,就需要对session的时限进行设置了
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在tomcat/conf/web.xml配置为30分钟
我们可以自己在当前项目的web.xml对最大闲置时间进行重新设定
也可以通过HttpSession的API 对最大闲置时间进行设定
1 2
| session.setMaxInactiveInterval(60);
|
也可以直接让session失效
三大域对象
域对象概述
域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同
- web项目中,我们一定要熟练使用的域对象分别是 请求域,会话域,应用域
- 请求域对象是HttpServletRequest ,传递数据的范围是一次请求之内及请求转发
- 会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求
- 应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话
生活举例: 热水器摆放位置不同,使用的范围就不同
- 摆在张三工位下,就只有张三一个人能用
- 摆在办公室的公共区,办公室内的所有人都可以用
- 摆在楼层的走廊区,该楼层的所有人都可以用
三大域对象的数据作用范围图解
域对象的使用
域对象的API
API |
功能 |
void setAttribute(String name,String value) |
向域对象中添加/修改数据 |
Object getAttribute(String name); |
从域对象中获取数据 |
removeAttribute(String name); |
移除域对象中的数据 |
API测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @WebServlet("/servletA") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("request","request-message"); HttpSession session = req.getSession(); session.setAttribute("session","session-message"); ServletContext application = getServletContext(); application.setAttribute("application","application-message");
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @WebServlet("/servletB") public class ServletB extends HttpServlet { @Override protected 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); } }
|
- 请求转发时,请求域可以传递数据
请求域内一般放本次请求业务有关的数据,如:查询到的所有的部门信息
- 同一个会话内,不用请求转发,会话域可以传递数据
会话域内一般放本次会话的客户端有关的数据,如:当前客户端登录的用户
- 同一个APP内,不同的客户端,应用域可以传递数据
应用域内一般放本程序应用有关的数据 如:Spring框架的IOC容器
过滤器
过滤器概述
Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一
- Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
- Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法
- Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应
- Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
- Filter是GOF中责任链模式的典型案例
- Filter的常用应用包括但不限于: 登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析… …
生活举例: 公司前台,停车场安保,地铁验票闸机
- 公司前台对来访人员进行审核,如果是游客则拒绝进入公司,如果是客户则放行 . 客户离开时提醒客户不要遗忘物品
- 停车场保安对来访车辆进行控制,如果没有车位拒绝进入,如果有车位,发放停车卡并放行,车辆离开时收取请车费
- 地铁验票闸机在人员进入之前检查票,没票拒绝进入,有票验票后放行,人员离开时同样验票
过滤器开发中应用的场景
- 日志的记录
- 性能的分析
- 乱码的处理
- 事务的控制
- 登录的控制
- 跨域的处理
- … …
过滤器工作位置图解
Filter接口API
1 2 3 4 5 6 7 8 9 10 11 12
| package jakarta.servlet; import java.io.IOException;
public interface Filter { default public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; default public void destroy() { } }
|
API |
目标 |
default public void init(FilterConfig filterConfig) |
初始化方法,由容器调用并传入初始配置信息filterConfig对象 |
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中 |
default public void destroy() |
销毁方法,容器在回收过滤器对象之前调用的方法 |
过滤器使用
目标:开发一个日志记录过滤器
- 用户请求到达目标资源之前,记录用户的请求资源路径
- 响应之前记录本次请求目标资源运算的耗时
- 可以选择将日志记录进入文件,为了方便测试,这里将日志直接在控制台打印
定义一个过滤器类,编写功能代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 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"); @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(); 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作为目标资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected 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); }
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected 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); } } }
|
配置过滤器以及过滤器的过滤范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?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>loggingFilter</filter-name> <filter-class>com.atguigu.filters.LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>/servletA</url-pattern> <url-pattern>*.html</url-pattern> <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次 |
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package com.atguigu.filters;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/*") public class LifeCycleFilter implements Filter { public LifeCycleFilter(){ System.out.println("LifeCycleFilter constructor method invoked"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("LifeCycleFilter init method invoked"); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("LifeCycleFilter doFilter method invoked"); filterChain.doFilter(servletRequest,servletResponse); }
@Override public void destroy() { System.out.println("LifeCycleFilter destory method invoked"); } }
|
过滤器链的使用
一个web项目中,可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称之为过滤器链
- 过滤器链中的过滤器的顺序由filter-mapping顺序决定
- 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
- 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
图解过滤器链
过滤器链功能测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletC service method invoked"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Filter1 implements Filter { @Override public 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 { @Override public 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 { @Override public 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");
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?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-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注解的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package jakarta.servlet.annotation;
import jakarta.servlet.DispatcherType; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebFilter { String description() default "";
String displayName() default "";
WebInitParam[] initParams() default {};
String filterName() default "";
String smallIcon() default "";
String largeIcon() default "";
String[] servletNames() default {};
String[] value() default {};
String[] urlPatterns() default {};
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
boolean asyncSupported() default false; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <filter> <filter-name>loggingFilter</filter-name> <filter-class>com.atguigu.filters.LoggingFilter</filter-class> <init-param> <param-name>dateTimePattern</param-name> <param-value>yyyy-MM-dd HH:mm:ss</param-value> </init-param> </filter>
<filter-mapping> <filter-name>loggingFilter</filter-name> <url-pattern>/servletA</url-pattern> <url-pattern>*.html</url-pattern> <servlet-name>servletBName</servlet-name> </filter-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.atguigu.filters;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.annotation.WebInitParam; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date;
@WebFilter( filterName = "loggingFilter", initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")}, urlPatterns = {"/servletA","*.html"}, servletNames = {"servletBName"} ) public class LoggingFilter implements Filter { private SimpleDateFormat dateFormat ;
@Override public void init(FilterConfig filterConfig) throws ServletException { String dateTimePattern = filterConfig.getInitParameter("dateTimePattern"); dateFormat=new SimpleDateFormat(dateTimePattern); } @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(); 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);
} }
|
监听器
监听器概述
监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象
监听器是GOF设计模式中,观察者模式的典型案例
观察者模式: 当被观察的对象发生某些改变时, 观察者自动采取对应的行动的一种设计模式
监听器使用的感受类似JS中的事件,被观察的对象发生某些情况时,自动触发代码的执行
监听器并不监听web项目中的所有组件,仅仅是对三大域对象做相关的事件监听
监听器的分类
监听器的六个主要接口
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对象 |
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| 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); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext application = this.getServletContext(); application.setAttribute("k1","v1"); application.setAttribute("k2","v2"); } }
@WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext appliation = getServletContext(); appliation.setAttribute("k1","value1"); appliation.removeAttribute("k2"); } }
|
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对象 |
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package com.atguigu.listeners;
import jakarta.servlet.*; import jakarta.servlet.annotation.WebListener; import jakarta.servlet.http.*;
@WebListener public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener { @Override public void sessionCreated(HttpSessionEvent se) { HttpSession session = se.getSession(); System.out.println("session"+session.hashCode()+" created"); }
@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); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @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("k2","v2"); } }
@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.setAttribute("k1","value1"); session.removeAttribute("k2"); session.invalidate(); } }
|
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对象 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @WebServlet(urlPatterns = "/servletA",name = "servletAName") public class ServletA extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("k1","v1"); req.setAttribute("k2","v2"); req.getRequestDispatcher("servletB").forward(req,resp); } }
@WebServlet(urlPatterns = "/servletB", name = "servletBName") public class ServletB extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("k1","value1"); req.removeAttribute("k2");
} }
|
session域的两个特殊监听器
session绑定监听器
HttpSessionBindingListener 监听当前监听器对象在Session域中的增加与移除
方法名 |
作用 |
valueBound(HttpSessionBindingEvent event) |
该类的实例被放到Session域中时调用 |
valueUnbound(HttpSessionBindingEvent event) |
该类的实例从Session中移除时调用 |
- HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 |
作用 |
getName() |
获取当前事件涉及的属性名 |
getValue() |
获取当前事件涉及的属性值 |
getSession() |
获取触发事件的HttpSession对象 |
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| @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"); } }
|
钝化活化监听器
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
1 2 3 4 5 6
| <?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,并存入数据,然后重启服务器
1 2 3 4 5 6 7 8 9
| @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,获取重启前存入的数据
1 2 3 4 5 6 7 8 9 10
| @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);
} }
|
如何监听钝化活化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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"); } }
|
1 2 3 4 5 6 7 8 9 10 11
| @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()); } }
|
Ajax
什么是ajax
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
XMLHttpRequest 只是实现 Ajax的一种方式。
ajax工作原理:
- 简单来说,我们之前发的请求通过类似 form表单标签,a标签 这种方式,现在通过 运行js代码动态决定什么时候发送什么样的请求
- 通过运行JS代码发送的请求浏览器可以不用跳转页面 ,我们可以在JS代码中决定是否要跳转页面
- 通过运行JS代码发送的请求,接收到返回结果后,我们可以将结果通过dom编程渲染到页面的某些元素上,实现局部更新
如何实现ajax请求
原生javascript方式进行ajax(了解):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <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>
|