历史回顾
会话技术
在Javaweb中,指的是打开一个浏览器并且访问某一个服务器的站点进行交互。我们称之为开始会话,如果用户关闭浏览器,称为会话结束。
客户端会话技术: cookie
服务器会话技术: session
Cookie
作用:
1、向客户端本地存储一些比较有用的数据
2、实现会话跟踪技术的一个先决条件
好处:
- 减缓服务器压力
- 增强用户体验
- 可以智能填充数据
如何添加Cookie
Cookie c = new Cookie(”名称”,”对应的值”);
//cookie的存活时间
c.setMaxAge(); //单位:秒
response.addCookie(c);//响应添加
如何获取Cookie
Cookie[] c = request.getCookies();
if(null != c){
//循环遍历
c.getName();
c.getValue();
}
Session 作用域
作用:维护用户的登录状态。
会话跟踪技术
Session本身是一个服务器对象,如果没有会话跟踪那么存活的时间可能很长,为了能够实现与会话同步,借助cookie的默认存储特性,实现了一个与会话同步的session会话的方式。叫做服务端会话。
实现原理:当用户通过浏览器访问服务器的时候,先根据Cookie中的JSEESIONID判断当前是否已经创建了session。如果已经创建,那么直接使用这个Session,如果没有创建,服务器会新建一个session并且将JSESSIONID发送客户的Cookie中,标志一个新的会话开启了。
关于EL获取指定作用域的值
${*Scope.属性名}
监听器
Javaweb来说,Servlet、监听器、过滤器称之为Javaweb的三大组件。
监听器实现了特殊监听接口的类,用来监听作用域与session。
监听器(Listener)共有三组:
第一组:监测ServletContext、request、session的创建和销毁 3个
第二组:监测ServletContext、request、session作用域值的变化 3个
第三组:监测session与类的关系 2个
第一组:
1.ServletContext创建与销毁 ServletContextListener
1)web.xml配置模式(非注解模式)
需要创建web.xml文件然后再创建继承了监听接口的监听类,并在里面配置<listener>下的<listener-class>属性,指向的就是监听类
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0"> <display-name>DAY18</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <listener> <listener-class>servletcontext.MyServletContextListener</listener-class> </listener> </web-app>
package servletcontext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener{ @Override//创建ServletContext监听器 public void contextInitialized(ServletContextEvent sce) { System.out.println("检测到ServletContext监听器创建"); } @Override//销毁ServletContext监听器 public void contextDestroyed(ServletContextEvent sce) { System.out.println("检测到ServletContext监听器销毁"); } }
2)注解模式
直接在监听类上写注解:@WebListener即可
package servletcontext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class MyServletContextListener implements ServletContextListener{ @Override//创建ServletContext监听器 public void contextInitialized(ServletContextEvent sce) { System.out.println("检测到ServletContext监听器创建"); } @Override//销毁ServletContext监听器 public void contextDestroyed(ServletContextEvent sce) { System.out.println("检测到ServletContext监听器销毁"); } }
监听结果
启动服务器是监听创建
关闭Tomcat服务器监听销毁(右键-stop)
作用:可以操作对应的作用域及相关信息。
2.reqeust创建与销毁 ServletRequestListener
理解:request的创建是每次请求都创建。 请求完成就销毁,即使是404,浏览器也请求了服务器,还是会创建和销毁request监听器。
package servletListener; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.annotation.WebListener; @WebListener public class ServletRequestListener implements javax.servlet.ServletRequestListener{ @Override public void requestInitialized(ServletRequestEvent sre) { ServletRequest request = sre.getServletRequest();//获取Servletrequest对象 //获取一个前台的参数 String mark = request.getParameter("mark"); System.out.println("[监听]-获取到参数:"+mark); } @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("[监听]-request已成功销毁"); } }
3.session创建与销毁 HttpSessionListener
理解:session为服务器对象,当使用的时候才创建。
servlet是通过request.getSession()创建
jsp 访问页面就创建,内置对象就有session。
无论是浏览器访问JSP创建还是直接经过后台创建都是会话对象,而且都具备会话功能,所以都会创建session
关闭无法销毁是因为本身session存在超时才执行销毁
停止服务器不执行销毁是因为有活化与钝化操作。
正常销毁:30分钟后
直接访问jsp页面会创建session,因为session就是jsp中的九大对象之一,会自动创建
JESSIONID的值和我们获取的值一样,当浏览器关闭后会重新指定一个session值
直接访问后台Servlet也会创建session,因为直接访问后台也是要通过浏览器,也会创建会话。
Servlet
package HttpservletListener; import java.io.IOException; 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 javax.servlet.http.HttpSession; @WebServlet("/ss") public class SessionListenerServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String id = session.getId(); System.out.println("sessionServlet:"+id); } }
sessionListener
package HttpservletListener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; @WebListener public class SessionListener implements HttpSessionListener{ @Override//创建sessionlisten public void sessionCreated(HttpSessionEvent se) { System.out.println("[监听]-session创建了"); HttpSession session = se.getSession(); System.out.println("session监听器:"+session.getId()); } @Override//销毁sessionlistener public void sessionDestroyed(HttpSessionEvent se) { System.out.println("[监听]-session销毁了"); } }
第二组
1.监测ServletContext值 --- ServletContextAttributeListener
ServletContextAttributeEvent这个对象是ServletContext的子类,我们可以直接调用父类中的方法获得ServletContext对象
getAttribute()方法的返回值是Object,我们已知返回类型的时候可以强制转型
Servlet
package servletListener; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/myServlet") public class MyServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException { ServletContext context = this.getServletContext();//ServletContext是从父类继承下来,可以直接调用 context.setAttribute("con", "哈哈");//添加值 context.setAttribute("con", "吼吼");//替换值 context.removeAttribute("con");//移除 } }
ServletContextAttributeListener
package HttpservletListener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; import javax.servlet.annotation.WebListener; @WebListener public class MyServletContextAttributeListener implements ServletContextAttributeListener{ @Override//添加 public void attributeAdded(ServletContextAttributeEvent event) { //ServletContextAttributeEvent这个对象是ServletContext的子类,我们可以直接调用父类中的方法获得ServletContext对象 String con = (String)event.getServletContext().getAttribute("con"); System.out.println("添加值:"+con); } @Override//替换 public void attributeReplaced(ServletContextAttributeEvent event) { String con = (String)event.getServletContext().getAttribute("con"); System.out.println("移除值:"+con); } @Override//移除 public void attributeRemoved(ServletContextAttributeEvent event) { String con = (String)event.getServletContext().getAttribute("con"); System.out.println("替换值:"+con); } }
2.监测request值---ServletRequestAttributeListener
request添加的时候多了一次替换,可以理解为添加的时候服务器默认给添加的元素赋了一个默认值null
Servlet
package servletListener; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/MyRequestServlet") public class MyRequestServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("req", "我是添加");//添加 request.setAttribute("req", "我是修改");//修改 request.removeAttribute("req");//移除 } }
MyServletRequestListener
package HttpservletListener; 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 req = (String)servletrequestattributeevent.getServletRequest().getAttribute("req"); System.out.println("添加值:"+req); } @Override//替换方法 public void attributeReplaced(ServletRequestAttributeEvent servletrequestattributeevent) { String req = (String)servletrequestattributeevent.getServletRequest().getAttribute("req"); System.out.println("替换值:"+req); } @Override//移除方法 public void attributeRemoved(ServletRequestAttributeEvent servletrequestattributeevent) { String req = (String)servletrequestattributeevent.getServletRequest().getAttribute("req"); System.out.println("移除值:"+req); } }
3.监测session值---HttpSessionAttributeListener
servlet
package servletListener; import java.io.IOException; 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 javax.servlet.http.HttpSession; @WebServlet("/MySessionServlet") public class MySessionServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("session", "我是添加");//添加 session.setAttribute("session", "我是修改");//修改 session.removeAttribute("session");//移除 } }
HTTPSessionAttributeListener
package HttpservletListener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; @WebListener public class MyHttpSessionListener implements HttpSessionAttributeListener { @Override//添加 public void attributeAdded(HttpSessionBindingEvent httpsessionbindingevent) { String session = (String)httpsessionbindingevent.getSession().getAttribute("session"); System.out.println("添加:"+session); } @Override//修改 public void attributeReplaced(HttpSessionBindingEvent httpsessionbindingevent) { String session = (String)httpsessionbindingevent.getSession().getAttribute("session"); System.out.println("修改:"+session); } @Override//移除 public void attributeRemoved(HttpSessionBindingEvent httpsessionbindingevent) { String session = (String)httpsessionbindingevent.getSession().getAttribute("session"); System.out.println("移除:"+session); } }
第三组
1.监听类是否在session中存在---HttpSessionBindingListener
第三组监听是比较特殊的,不需要单独的设定实现类。 所以直接与类绑定即可。在实体类中实现这个接口,改写绑定解绑方法即可。
监听的时机:
只有将绑定的对象放进session或者从session中移除的时候才会进行监听。
重点:只需要在指定的类中实现绑定的监听,不需要使用web.xml配置或者注解标签。
1.我们可以在实体类中绑定监听接口,那么这个类就具有了监听的功能。
2.当具有监听功能的实体类对象被添加到session作用域中后,监听就绑定了,当把这个对象从session中移除后监听解绑。
实体类
package com.offcn.SessionListenerDao; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class Student implements HttpSessionBindingListener{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "[name=" + name + ", age=" + age + "]"; } public Student(String name, int age) { super(); this.name = name; this.age = age; } public Student() {} /重写监听的方法/// @Override public void valueBound(HttpSessionBindingEvent httpsessionbindingevent) { String n = httpsessionbindingevent.getName(); Object v = httpsessionbindingevent.getValue(); System.out.println("n:"+n+"..."+"v:"+v); System.out.println("绑定"); } @Override public void valueUnbound(HttpSessionBindingEvent httpsessionbindingevent) { System.out.println("解绑"); } }
测试用的Servlet
package com.offcn.SessionListenerDao; import java.io.IOException; 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 javax.servlet.http.HttpSession; @WebServlet("/StudentServlet") public class StudentServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Student student = new Student("小明",23); HttpSession session = request.getSession(); session.setAttribute("sess", student);//当把实现监听接口的实体类对象放到session中时就开始绑定 System.out.println("将实体类对象放进session中,完成绑定"); session.removeAttribute("sess"); System.out.println("从session中移除实体类对象,解绑"); } }
2.监听存到session中的类的活化与钝化操作---HttpSessionActivationListener
钝化:是指将活跃在内存中的session中对象持久化到本地叫做钝化。
活化:是指将本地存储的Session信息再次读取到内存中。
牵扯到对象持久化,实体类就要实现serializable接口
存在的意义: 防止意外发生导致内存中数据丢失。
使用方式也同样将这个接口在指定的类中实现,但是需要注意的是因为与本地操作时设计到IO所以必须多实现一个序列化(serializable)接口。
钝化的条件:
将对象存到session并且停止服务器。
钝化的路径:tomcat - work ...
活化的条件:
钝化后再次启动服务器,tomcat会自动的将SESSIONS.ser文件中的内容读取到内存,并且删除钝化后的文件。
测试:钝化的时候存储一个数据。并且打印SessionID
活化后测试这个数据及SesionID变化。
关闭服务器和浏览器之前:
关闭服务器之后:
不关闭浏览器的情况下开始服务器:JSESSIONID不变,文件被活化到了内存中
当我们关闭浏览器重启服务器的时候:
过滤器
过滤器指的是实现了Filter接口(javax.servlet.Filter;)的Java类。用来拦截请求,以做其他处理。此路是我开,此树是我栽,要从此路过,留下买路财!
过滤器是全局生效的。
第一种,非注解配置,在web.xml中配置,和配置Servlet基本相同
xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0"> <display-name>DAY18</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- <listener> <listener-class>servletcontext.MyServletContextListener</listener-class> </listener> --> <filter> <filter-name>fff</filter-name> <filter-class>com.offcn.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>fff</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
测试用Servlet:
package com.offcn.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException { System.out.println("因为xml中配置了/* 所有东西都被我拦截了!"); } }
当xml配置的路径为" /* "时,拦截器会拦截所有,即使该页面不存在也会拦截,不会报404,前提是要在这个项目下访问。
第二种:注解配置。
放行方法:filterchain.doFilter(servletrequest, servletresponse);
案例一:字符编码过滤器
以前写Servlet的时候每一个servlet都需要设置request和Response的编码、现在通过过滤器可以在调用servlet之前进行同意的编码设置工作。
ServletRequest是HttpServletRequest的父类,request==HttpServletRequest
package com.offcn.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; @WebFilter("/*") public class UnitedEncoding implements Filter{ @Override public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException { servletrequest.setCharacterEncoding("utf-8"); servletresponse.setContentType("text/html;charset=utf-8"); //设置完成之后放行,所有请求响应都必须先满足我们设置的字符集要求才能往下走 filterchain.doFilter(servletrequest, servletresponse); } }
案例二:拦截登录
思想:除了登录页面和登录的请求是无条件的放行。其他的任意只要不登录全部调转到登录页面,一旦登录全部放行。
拦截器开头我们拦截了所有,首先要仅允许我们设置的登陆页面可以通行,如果不是与登陆页面有关,我们需要判断是否登陆
session.setAttribute("msg")的值不同目的是不同的错误提示不同的信息
登陆页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <span style="color:red">${msg }</span> <form action="loginServlet" method="post"> 姓名:<input type="text" name="name"><br> 密码:<input type="password" name="pwd"><br> <input type="submit" value="登陆"> </form> </body> </html>
登陆成功页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> welcome aboard captain${sessionScope.name },let's go! </body> </html>
Servlet页面
package com.offcn.filterLogin; import java.io.IOException; 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 javax.servlet.http.HttpSession; @WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String pwd = request.getParameter("pwd"); HttpSession session = request.getSession(); if("张三丰".equals(name) && "123456".equals(pwd)) { session.setAttribute("name", name); session.setAttribute("pwd", pwd);//用处不大。。。 response.sendRedirect("success.jsp"); }else { session.setAttribute("msg", "账号密码错误,别想蒙我"); request.getRequestDispatcher("login.jsp").forward(request, response); } } }
login过滤器
package com.offcn.filterLogin; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebFilter("/*") public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletrequest;// 获得子类HttpServletRequest HttpServletResponse response = (HttpServletResponse) servletresponse; HttpSession session = request.getSession();// 获取session对象,以便获取作用域中的值 Object name = session.getAttribute("name");// name // Object pwd = session.getAttribute("pwd");//pwd,实际上用不着 // 判断 String uri = request.getRequestURI();// 拦截器开头我们拦截了所有,首先要仅允许我们设置的登陆页面可以通行 if (uri.contains("login.jsp") || uri.contains("loginServlet")) { filterchain.doFilter(servletrequest, servletresponse); /* * 如果不是与登陆页面有关,我们需要判断是否登陆,注意判断是否登陆时只有两种情况,登陆和未登录,登陆后name肯定 有值, 只需要判断name是否为空即可 */ } else { if (null != name) { request.getRequestDispatcher("success.jsp").forward(request, response); } else { request.setAttribute("msg", "非法登陆,还想要蒙我"); request.getRequestDispatcher("login.jsp").forward(request, response); } } } }