JavaWeb——会话管理

一、会话

1.1.会话管理概述

(1).为什么需要会话管理:

HTTP是无状态协议,无状态就是不保存状态,即无状态协议(stateless),HTTP协议自身不对请求和响应之间的通信状态进行保存,也就是说,在HTTP协议这个级别,协议对于发送过的请求或者响应都不做持久化处理;简单理解就是浏览器发送请求,服务器接收并响应,但是服务器不记录请求来自哪个浏览器,服务器没记录浏览器的特征,就是客户端的状态;

(2).会话管理实现的手段:

Cookie和Session配合解决:cookie是在客户端保留少量数据的技术,主要通过响应头向客户端响应一些客户端要保留的信息;session是在服务端保留更多数据的技术,主要通过服务端HttpSession对象保存一些和客户端相关的信息;cookie和session配合记录请求状态;

1.2.Cookie

(1).Cookie概述:

Cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去;

服务端创建Cookie,将Cookie放入响应对象中,Tomcat容器将Cookie转化为set-cookie响应头,响应给客户端;客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie;Cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐;由于Cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据;

(2)Cookie的使用:

servletA向响应中增加Cookie:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
    //创建Cookie
    Cookie 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 {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //获取请求中的cookie
        Cookie[] cookies = req.getCookies();
        //迭代cookies数组
        if(null != cookies && cookies.length!= 0){
            for(Cookie cookie :cookies){
                System.out.printn(cookie.getName()+":"+cookie.getValue());
            }
        }
    }
}

(3)Cookie的时效性:

默认情况下Cookie的有效期是一次会话范围内,可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上;cookie.setMaxAge(int expiry)参数单位是秒,表示cookie的持久化时间,如果设置参数为0表示将浏览器中保存的该cookie删除;

会话级Cookie:

服务器端并没有明确指定Cookie的存在时间;

在浏览器端,Cookie数据存在于内存中;

只要浏览器还开着,Cookie数据就一直都在;

浏览器关闭,内存中的Cookie数据就会被释放;

持久化Cookie:

服务器端明确设置了Cookie的存在时间;

在浏览器端,Cookie数据会被保存到硬盘上;

Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响;

持久化Cookie到达了预设的时间会被释放;

servletA设置一个Cookie为持久化cookie:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
    //创建Cookie
    Cookie cookie1 =new Cookie("c1","c1_message");
    cookie1.setMaxAge(60);
    Cookie cookie2 =new Cookie("c2","c2_message");
    //将cookie放入响应对象
    resp.addCookie(cookie1);
    resp.addCookie(cookie2);
    }
}

servletB接收Cookie,浏览器中间发生一次重启再请求servletB测试

@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //获取请求中的cookie
        Cookie[] cookies = req.getCookies();
        //迭代cookies数组
        if(null != cookies && cookies.length!= 0){
            for(Cookie cookie :cookies){
                System.out.printn(cookie.getName()+":"+cookie.getValue());
            }
        }
    }
}

(3).Cookie的提交路径

访问互联网资源时不需要每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,可以通过Cookie的setPath(String path) 对Cookie的路径进行设置;

从ServletA中获取cookie,设置了请求路径,向ServletB请求时便会携带c1而向其他Servlet请求资源时便不会携带c1:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
    //创建Cookie
    Cookie cookie1 =new Cookie("c1","c1_message");
    cookie1.setPath("/webg3_war_exploded/servletB");
    Cookie cookie2 =new Cookie("c2","c2_message");
    //将cookie放入响应对象
    resp.addCookie(cookie1);
    resp.addCookie(cookie2);
    }
}

1.3.Session:

(1).HttpSession概述

HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象;客户端在发送请求时,都可以使用自己的session,这样服务端就可以通过session来记录某个客户端的状态了;

服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以Cookie的形式放入响应对象;后端创建完session后,客户端会收到一个特殊的Cookie,叫做JSESSIONID;客户端下一次请求时携带JSESSIONID,后端收到后根据JSESSIONID找到对应的session对象;通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了;session也是域对象;

(2). HttpSession的使用

用户提交form表单到ServletA,携带用户名,ServletA获取session将用户名存到session,用户再请求其他任意Servlet,获取之间存储的用户;

定义表单页,提交用户名:

<form action="servletA" method="post">
    用户名:
    <input type= text" name="username">
    <input type="submit" value="提交">
</form>

定义ServletA,将用户名存入session,响应中收到了一个JSESSIONID的Cookie:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //获取请求中的参数
        String username =reg.getParameter("username");
        //获取session对象
        HttpSession session=req.getSession();
        //获取Session的ID
        String jSessionId=session.getId();
        System.out.println(jSessionId);
        //判断session是不是新创建的session
        boolean isNew=session.isNew();
        System.out.println(isNew);
        // 向session对象中存入数据
        session.setAttribute("username",username);
    }
}

定义其他Servlet,从session中读取用户名:

@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        // 获取session对象
        HttpSession session =reg.getSession();
        // 获取Session的ID
        String jSessionId= session.getId();
        System.out.println(jSessionId);
        // 判断session是不是新创建的session
        boolean isNew=session.isNew();
        System.out.println(isNew);
        //从session中取出数据
        String username =(String)session.getAttribute("username");
        System.out.println(username);
    }
}

getSession方法的处理逻辑:

(3).HttpSession时效性:

用户量很大之后,Session对象相应的也要创建很多。如果一味创建不释放,那么服务器端的内存
迟早要被耗尽;客户端关闭行为无法被服务端直接侦测,或者客户端较长时间不操作也经常出现,类似这些的情况,就需要对session的时限进行设置了;

默认的session最大闲置时间(两次使用同一个session中的间隔时间)在Tomcat/conf/web.xml配置为30分钟,可以自己在当前项目的web.xml对最大闲置时间进行重新设定:

<session-config>
    <session-timeout>1</session-timeout>
</session-config>

可以通过HttpSession的API 对最大闲置时间进行设定:

session.setMaxInactiveInterval(60);

也可以直接让session失效:

session.invalidate();

1.4.三大域对象

(1).域对象概述:

域对象是一些用于存储数据和传递数据的对象。传递数据不同的范围称之为不同的域。不同的域对象代表不同的域,共享数据的范围也不同。域对象包括请求域、会话域、应用域;

请求域对象是HttpServletRequest,传递数据的范围是一次请求之内及请求转发;

会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求;

应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话;

(2).三大域对象的数据作用范围图解:

请求域:

会话域:

应用域:

 三种域:

(3). 域对象的使用:

域对象的API:

API功能
void setAttribute(Stringname,String value)向域对象中添加/修改数据
Object getAttribute(String name);从域对象中获取数据
void removeAttribute(String name);移除域对象中的数据

ServletA向三大域中放入数据:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        // 向请求域中放入数据
        req.setAttribute("request","request-message");
        //reg.getRequestDispatcher("servletB").forward(reg,resp);
        // 向会话域中放入数据
        HttpSession session=reg.getSession();
        session.setAttribute("session","session-message");
        //向应用域中放入数据
        ServletContext application = getServletContext();
        application.setAttribute("application","application-message");
    }
}

ServletB从三大域中取出数据:

@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //从请求域中获取数据
        String reqMessage =(String)req.getAttribute("request");
        System.out.println(reqMessage);
        //从会话域中获取数据
        HttpSession session =reg.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容器;

二、过滤器

1.过滤器概述

Filter即过滤器,是JAVAEE技术规范之一,作为目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一;

a.Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口;

b.Filter的位置是项目所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后会先调用Filter的doFilter方法;

c.Filter的doFilter方法可以控制请求是否继续,如果放行则请求继续,如果拒绝则请求到此为止,由过滤器本身做出响应;

d.Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理;

2.过滤器工作位置图解:

3.Filter接口API:

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(){}
}

4.Filter的API

API功能
default public void init(Filterconfig filterConfig)初始化方法,由容器调用并传入初始配置信息filterConfig对象
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)过滤方法,核心方法,过滤请求,决定是否放行响应之前的其他处理等都在该方法中
default public void destroy()销毁方法,容器在回收过滤器对象之前调用的方法

5.过滤器的使用:

定义一个过滤器类,编写功能代码:

public class LoggingFilter implements Filter{
    @Override
    private SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH :mm :ss");
    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.printIn(afterLogging);
    }
}

(1)doFilter方法中的请求和响应对象是以父接口的形式声明的,实际传入的实参就是HttpServletRequest和HttpServletResponse子接囗级别的,可以安全强转;

(2).filterchain.doFilter(request,response);这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止;

(3).filterchain.doFilter(request,response);在放行时需要传入request和response,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象;

定义两个Servlet作为目标资源:

@WebServlet(urlPattern="/servletA",name ="servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //处理器请求
        System.out.println("servletA处理请求的方法,耗时10毫秒");
        //模拟处理请求耗时
        try {
            Thread.sleep(10);
        }catch(InterruptedExceptione){
            throw new RuntimeException(e);
        }
    }
}
@WebServlet(urlPattern="/servletB",name ="servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //处理器请求
        System.out.println("servletB处理请求的方法,耗时15毫秒");
        //模拟处理请求耗时
        try {
            Thread.sleep(15);
        }catch(InterruptedExceptione){
            throw new RuntimeException(e);
        }
    }
}

配置过滤器以及过滤器的过滤范围:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmIns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="https://iakarta.ee/xml/ns/iakartaeehttps://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>

(1).filter-mapping标签中定义了过滤器对哪些资源进行过滤;

(2).子标签url-pattern通过映射路径确定过滤范围:

a./servletA精确匹配,表示对servletA资源的请求进行过滤;

b.*.html表示对以.action结尾的路径进行过滤;

c./*表示对所有资源进行过滤;

d.一个filter-mapping下可以配置多个url-pattern;

(3).子标签servlet-name通过servlet别名确定对哪些servlet进行过滤;

a.使用该标签确定目标资源的前提是servlet已经起了别名;

b.一个filter-mapping下可以定义多个servlet-name;

c.一个filter-mapping下servlet-name和url-pattern子标签可以同时存在;

过滤过程图解:

6.Filter的生命周期:

过滤器作为web项目的组件之一和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造

阶段对应方法执行时机执行次数
创建对象构造器web应用启动时1
初始化方法void init(FilterConfig filterConfig)构造完毕1
过滤请求void doFilter(ServletRequest servletRequest, ServletRespon seservletResponse, FilterChain filterChain)每次请求多次
销毁default void destroy()web应用关闭时1
@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");
    }
}

7.过滤器链的使用:

一个web项目中可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时工作位置有先后,整体形成一个工作链,称为过滤器链。

过滤器链中的过滤器的顺序由filter-mapping顺序决定;

每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的;

如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低;

图解过滤器链:

代码测试:

定义三个过滤器,对目标资源Servlet的请求进行过滤:

a.目标Servlet资源代码:

@WebServlet("/servletC")
public class ServletC extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        System.out.println("servletC service method invoked");
    }
}

b.三个过滤器代码:

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");
    }
}

c.过滤器配置代码: 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmIns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="https://iakarta.ee/xml/ns/iakartaeehttps://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
        version="5.0">
    <!--配置filter,并为filter起别名-->
    <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>

工作流程图解:

2.5.注解方式配置过滤器:@WebFilter注解的使用

一个比较完整的Filter的XML配置:

<!--配置filter,并为filter起别名-->
<filter>
    <filter-name>loggingFilter</filter-name>
    <filter-class>com.atguigu.filters.LoggingFilter</filter-class>
    <!--配置filter的初始参数-->
    <init-param>
        <param-name>dateTimePattern</param-name>
        <param-value>yyyy-MM-dd HH:mm:ss</param-value>
    </init-param>
</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>

将xml配置转换成注解方式实现:

@WebFilter(
    filterName ="loggingFilter"
    initParams ={@WebInitParam(name="dateTimePattern",value="yyyy-MM-ddHH :mm:ss")},
    urlPatterns ={"/servletA","*.html"},
    servletNames ="servletBName"}
)
public class LoggingFilter implements Filter {
}

三、监听器:

1.监听器概述:

监听器是专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象;监听器是GOF设计模式中,观察者模式的典型案例;监听器使用的感受类似JS中的事件,被观察的对象发生某些情况时,自动触发代码的执行;监听器并不监听web项目中的所有组件,仅仅是对三大域对象做相关的事件监听;

监听器的分类:

(1).按监听的对象划分:

a.application域监听器:ServletContextListener、ServletContextAttributeListener;

b.session域监听器:HttpSessionListener、HttpSessionAttributeListener、HttpSessionBindingListener、HttpSessionActivationListener;

c.request域监听器:ServletRequestListener、ServletRequestAttributeListener;

(2).按监听的事件划分:

a.域对象的创建和销毁监听器:ServletContextListener、HttpSessionListener、ServletRequestListener 

b.域对象数据增删改事件监听器: ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener

c.其他监听器:HttpSessionBindingListener、HttpSessionActivationListener;

2.监听器的六个主要接口:

a.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对象

 定义监听器:

@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();
        Obiect value =scae.getValue();
        ServletContext application=scae.getServletContext();
         System.out.println("application"+application.hashCode()+"remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae){
        String name = scae.getName();
        Object value = scae.getValue();
        ServletContext application = scae.getServletContext();
        Object newValue = application.getAttribute(name);
        System.out.println("application"+application.hashCode()+"change :"+name+"="+value+to "+newValue);
    }
}

定义触发监听器的代码:

//ServletA用于向application域中放入数据
@WebServlet(urlPatterns = "/servletA",name ="servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        // 向application域中放入数据
        ServletContext application = this.getServletContext();
        application.setAttribute("k1","v1");
        application.setAttribute("k2","v2");
    }
}
// ServletB用于向application域中修改和移除数据
@WebServlet(urlPatterns="/servletB",name="servletBName")
public class ServletB extends HttpServlet{
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        ServletContext application = getServletContext();
        // 修改application域中的数据
        application.setAttribute("k1"."value1");
        // 删除application域中的数据
        application.removeAttribute("k2");
    }
}

b.session域监听器:

HttpSessionListener:监听HttpSession对象的创建与销毁

方法名作用
sessionCreated(HttpSessionEvent hse)HttpSession对象创建时调用
sessionCreated(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对象

定义监听器:

@WebListener
public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener{
    // 监听session创建
    @Override
    public void sessionCreated(HttpSessionEvent se){
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode( )+" created");
    }
    // 监听session销毁
    @Override
    public void sessionDestroyed(HttpSessionEvent se){
        HttpSession session=se.getSession();
        System.out.println("session"+session.hashCode()+" destroyed");
    }
    // 监听数据增加
    @Override
    public void attributeAdded(HttpSessionBindingEvent se){
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode()+" add:"+name+"="+value);
    }
    // 监听数据移除
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se){
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession():
        System.out.println("session"+session.hashCode()+" remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se){
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession();
        Object newValue = session.getAttribute(name);
        System.out.println("session"+session.hashCode()+" change :"+name+"="+value+"to"+newValue);
    }
}

定义触发监听器:

// servletA用于创建session并向session中放数据
@WebServlet(urlPatterns ="/servletA",name="servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        //创建session,并向session中放入数据
        HttpSession session=reg.getSession();
        session.setAttribute("k1");
        session.setAttribute("k2");
    }
}
// servletB用于修改删除session中的数据并手动让session不可用
@WebServlet(urlPatterns="/servletB",name ="servletBName")
public class ServletB extends HttpServlet{
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        HttpSession session=reg.getSession();
        // 修改session域中的数据
        session.setAttribute("k1","value1");
        // 删除session域中的数据
        session.removeAttribute("k2");
        // 手动让session不可用
        session.invalidate();
    }
}

c.request域监听器

ServletRequestListener:监听ServletRequest对象的创建与销毁

方法名作用
requestinitialized(ServletRequestEvent sre)ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre)ServletRequest对象销毁时调用

ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象:

方法名作用
attributeAdded(ServletRequestAttributeEvent srae)向ServletRequest中添加属性时调用
attributeRemoved(ServletRequestAttributeEvent srae)从ServletRequest中移除属性时调用
attributeReplaced(ServletRequestAttributeEvent srae)当ServletRequest中的属性被修改时调用

ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:

方法名作用
getName()获取修改或添加的属性名
getValue()获取被修改或添加的属性值
getServletRequest ()获取触发事件的ServletRequest对象

定义监听器:

@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.printIn("request"+request.hashCode()+" destoryed");
    }
    // 监听数据增加
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae){
        String name = srae.getName();
        Object value = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        System.out.println("request"+request.hashCode()+" add:"+name+"="+value);
    }
    // 监听数据移除
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae){
        String name=srae.getName();
        Object value =srae.getValue();
        ServletRequest request=srae.getServletRequest();
        System.out.println("request"+request .hashCode()+" remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae){
        String name = srae.getName();
        Object value = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        Object newValue = request.getAttribute(name);
        System.out.println("request"+request.hashCode()+" change:"+name+"="+value+" to"+newValue);
    }
}

定义触发监听器的代码:

// servletA向请求域中放数据
@WebServlet(urlPatterns ="/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        // 向request中增加数据
        req.setAttribute("k1","v1");
        req.setAttribute("k2","v2");
        // 请求转发
        req.getRequestDispatcher("servletB").forward(reg,resp);
    }
// servletB修改删除域中的数据
@WebServlet(urlPatterns ="/servletB",name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        // 修改request域中的数据
        req.setAttribute("k1","value1");
        // 删除session域中的数据
        req.removeAttribute("k2");
    }
}

3.session域的两个特殊监听器

(1).session绑定监听器

HttpSessionBindingListener监听当前监听器对象在Session域中的增加与移除:

方法名作用
valueBound(HttpSessionBindingEvent event)该类的实例被放到Session域中时调用
valueUnbound(HttpSessionBindingEvent event)该类的实例从Session中移除时调用

 HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名作用
getName()获取修改或添加的属性名
getValue()获取被修改或添加的属性值
getSession()获取触发事件的HttpSession对象

定义监听器:

public class MySessionBindingListener implements HttpSessionBindingListener{
    // 监听绑定
    @Override
    public void valueBound(HttpSessionBindingEvent event){
        HttpSession session = event.getSession();
        String name = event.getName();
        System.out.printIn("MySessionBindingListener"+this.hashCode()+" binding intosession"+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 outofsession"+session.hashCode()+" with name "+name);
    }
}

定义触发监听器的代码:

@WebServlet(urlPatterns="/servletA",name="servletAName")
public class ServletA extends HttpServlet{
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        HttpSession session = req.getSession();
        // 绑定监听器
        session.setAttribute("bindingListener",new MySessionBindingListener());
        // 解除绑定监听器
        session.removeAttribute("bindingListener");
    }
}

(2).钝化活化监听器

HttpSessionActivationListener监听某个对象在Session中的序列化与反序列化

方法名作用
sessionWillPassivate(HttpSessionEvent se)该类实例和Session一起钝化到硬盘时调用
sessionDidActivate(HttpSessionEvent se)该类实例和Session一起活化到内存时调用

HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象;

钝化活化:

a.session对象在服务端是以对象的形式存储于内存的,session过多,服务器的内存也是吃不消的;

b.一旦服务器发生重启,所有的session对象都将被清除,也就意味着session中存储的不同客户端的登录状态丢失;

c.为了分摊内存压力并且为了保证session重启不丢失,我们可以设置将session进行钟化处理;

d.在关闭服务器前或者到达了设定时间时,对session进行序列化到磁盘,这种情况叫做session的钝化;

e.在服务器启动后或者再次获取某个session时,将磁盘上的session进行反序列化到内存,这种情况做session的活化;

配置钝化活化:

在web目录下,添加META-INF下创建Context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Manager className = "org.apache.catalina.session.PersistentManager", maxIdleSwap="1">
    <Store className="org.apache.catalina.session.FileStore", directory="d:\mysession"></Store>
</Manager>
</Context>

请求servletA,获得session,并存入数据,然后重启服务器:

@WebServlet(urlPatterns ="/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg,HttpServletResponse resp) throws ServletException,IOException{
        HttpSession session=reg.getSession();
        // 添加数据
        session.setAttribute("k1","v1");
    }
}

请求servletB获取session,获取重启前存入的数据:

@WebServlet(urlPatterns ="/servletB",name = "servletBName")
public class ServletB extends HttpServlet{
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        HttpSession session=reg.getSession();
        Object v1=session.getAttribute("k1");
        System.out.println(v1);
    }
}

监听活化和钝化:

package com.atguigu.listeners;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionActivationListener;
import jakarta.servlet.http.HttpSessionEvent;
import java.io.Serializable;
public class ActivationListener implements HttpSessionActivationListener, Serializable{
    // 监听钝化
    @Override
    public void sessionWillPassivate(HttpSessionEvent se){
            HttpSession session=se.getSession();
            System.out.println("session with SESSIONID "+ session.getId()+" willpassivate");
        }
    // 监听活化
    @Override
    public void sessionDidActivate(HttpSessionEvent se){
        HttpSession session=se.getSession();
        System.out.println("session with JSESSIONID "+ session.getId()+" didactivate");
    }
}
@WebServlet(urlPatterns="/servletA",name="servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException,IOException{
        HttpSession session = req.getSession();
        // 添加数据
        session.setAttribute("k1","v1");
        // 添加钝化活化监听器
        session.setAttribute("activationListener",new ActivationListener());
    }
}

四、过滤器控制登录校验 

需求说明:未登录状态下不允许访问showShedule.html和SysSchedulecontroller相关增删改处理,重定向到login.html,登录成功后可以自由访问。

package com.atguigu.schedule.filters;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns ={"/showSchedule.html","/schedule/*"})
public class LoginFilter implements Filter{
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponseservletResponse, 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");
        }
    }
}
protected void login(HttpServletRequest reg,HttpServletResponse resp) throws ServletException, IOException{
    // 接收用户请求参数
    // 获取要注册的用户名密码
    String username =reg.getParameter("username");
    String userPwd=reg.getParameter("userPwd");
    // 调用服务层方法,根据用户名查询数据库中是否有一个用户
    SysUser loginUser =userService.findByUsername(username);
    if(null == loginUser){
        // 没有根据用户名找到用户,说明用户名有误
        resp.sendRedirect("/loginUsernameError.html");
    }else if(!loginUser.getUserPwd().equals(MD5Util.encrypt(userPwd))){
        // 用户密码有误
        resp.sendRedirect("/loginUserPwdError.html");
    }else{
        // 登录成功,将用户信息存入session
        req.getSession().setAttribute("sysUser",loginUser);
        // 登录成功,重定向到日程展示页
        resp.sendRedirect("/showSchedule.html");
    }
}

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值