[Java Web programming Xi] deeply understand Servlet listener

Posted by kpowning on Wed, 05 Jan 2022 14:29:11 +0100

The previous Blog introduced the Servlet filter in detail and learned that the filter is similar to an AOP concept. This Blog will learn about the Servlet listener and understand what the third largest component of Java Web is useful and how it works.

Basic concepts of listener

In JSP technology, we have learned four scopes: pageContext, request, session and application, which are used to realize data sharing. The three scopes of request, session and application correspond to us respectively

  • HttpServletRequest interface: request scope object
  • HttpSession interface: session scope object
  • ServletContext interface: global scope object

Although there is a scope for data sharing, we can't see the specific flow process of data, such as when the scope objects are created and destroyed, accessed, changed and deleted. Therefore, we can't operate the data and objects at the specified time. At this time, we need the Servlet listener to play a role:

  • Events: method call, attribute change, state change, etc., which correspond to the creation and destruction events of objects, attribute change events, and additional events for monitoring the state change of objects in HttpSession.
  • Event source: the monitored object. Here are HttpServletRequest, HttpSession, and ServletContext.
  • Listener: used to listen to the event source object. Changes in the state of the event source object will trigger the listener. Here is a Servlet listener we created
  • Register listener: bind the listener to the event source

So one sentence description is: after registering the listener for the event source, when an event causes a change in the event source, the listener listens to the change and performs logical processing

Listener function

Servlet listener is a special class defined in the servlet specification. It is used to listen to the creation and destruction events of three scope objects: servletContext(application), httpsession(session) and servletRequest(request), as well as the event of attribute modification in these scope objects. In other words, it is to listen to the life cycle of the three scope objects.

Create listener

You can easily create a listener with IDEA. Follow the steps below:

You can see that IDEA automatically generates the listener code template for us:

package com.example.myfirstweb.controller; /**
 * * @Name ${NAME}
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/7/27
 */

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebListener
public class ServletListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {

    public ServletListener() {
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        /* This method is called when the servlet context is initialized(when the Web application is deployed). */
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        /* This method is called when the servlet Context is undeployed or Application Server shuts down. */
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is added to a session. */
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is removed from a session. */
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent sbe) {
        /* This method is called when an attribute is replaced in a session. */
    }
}

You can see that listeners are defined through annotations. Let's look at the source code of annotations:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package javax.servlet.annotation;

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 WebListener {
    String value() default "";
}

Common methods of listener

Eight listener interfaces are defined in the Servlet specification, which can be used to listen to the life cycle and attribute change events of ServletContext, HttpSession and ServletRequest objects. To develop a Servlet listener, you need to implement the corresponding listener interface and rewrite the methods in the interface:

Listener for listener object creation and destruction

The Servlet specification defines listeners that listen to the creation and destruction events of ServletContext, HttpSession and HttpServletRequest objects

Listener listening for property changes

The Servlet specification defines listeners that listen for attribute change events in three objects: ServletContext, HttpSession and HttpServletRequest. The three listener interfaces are ServletContextAttributeListener, HttpSessionAttributeListener and ServletRequestAttributeListener respectively. Three methods are defined in these three interfaces to handle the events of adding, deleting and replacing attributes in the monitored object. The corresponding method names of the same event in the three interfaces are exactly the same, but the parameter types are different

Listener listening for object state changes in Session

Objects in a Session can have a variety of states: bind to the Session, unbind from the Session, persist to the storage device with the Session object (passivation), and recover from the storage device with the Session object (activation). Two special listener interfaces are defined in the Servlet specification to help objects understand their status in the Session: HttpSessionBindingListener interface and HttpSessionActivationListener interface. Classes implementing these two interfaces do not need to be registered

Listener to monitor the number of people online

Monitor the number of online people on the website, that is, count the number of sessions created in the server, and the number of online people created once + 1; session destroys the number of people online once - 1.

JSP page

If the login is successful, jump to index The JSP page is linked with the filter in the previous article. All page requests come to login first JSP, jump only after login. Here we simulate the jump after successful login:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" errorPage="jsp/error.jsp" %>
<%@ include file="jsp/top.jsp" %>
<!DOCTYPE html>
<html>
<head>
  <title>JSP - Hello World</title>
  <%!
    int number = 0;// Declare global variables
    int count() {
      number++;//number self increment
      return number;
    }%>

</head>
<h1><%= "Hello World!" %>
</h1>
<br/>
<body>
<%

  String print = "I am the content of the script statement";
  List<String> strList = new ArrayList<>();
  strList.add("Script statement 1");
  strList.add("Script statement 2");
  strList.add("Script statement 3");
  //  strList.add("script statement 4")// This is a Java annotation used to annotate java code, which is not visible when viewing the source code
%>
<a href="hello-servlet">My first JavaWeb project</a> <!-- This is a html Notes, visible<%=new Date().getTime() %> -->
<br/>

<%--This is a JSP Comments, not visible when viewing the source code--%>
Example declaration statement: This is the second statement <%=count()%> Visit this page for the first time
<br/>
Example of expression statement: <%="I am an expression statement"%>
<br/>
Example script statement: <%=print%>, Print list <%=strList%>

</body>
</html>

Servlet page

We prepare two pages. One page is used to jump from Servlet to index JSP, automatically exit after 60 seconds:

package com.example.MyFirstJavaWeb;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {


    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //Assuming successful login
        HttpSession session = request.getSession();
        session.setMaxInactiveInterval(60); //Auto launch in 60 seconds
        response.sendRedirect("index.jsp");

    }

    public void destroy() {
    }
}

There is also a page for manually exiting a session:

package com.example.MyFirstJavaWeb;

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;
import java.io.IOException;

@WebServlet(name = "StopSession", value = "/stop-servlet")
public class StopSession extends HttpServlet {


    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //Assuming successful exit
        HttpSession session = request.getSession();
        session.invalidate();


    }

    public void destroy() {
    }
}

Servlet listener

The next step is the code of our listener. During application initialization, we add a quantity statistics, and then accumulate it when the session is created and subtract it when it is destroyed:

package com.example.MyFirstJavaWeb.listener;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.util.Date;

@WebListener
public class CountListener implements HttpSessionListener,ServletContextListener {

    public CountListener() {
    }
    //Listen to the application destruction event -- that is, the server is shut down
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("application Destroy~~~~~~~~");

    }
    //Listen to the application creation event -- that is, the server is turned on
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("application establish~~~~~~~~");
        int sessionCount = 0;//When the server is turned on, variables are defined to count the number of online people and save them
        ServletContext application = sce.getServletContext();
        application.setAttribute("sessionCount", sessionCount);
    }

    //Listen to the session creation event -- that is, if someone logs in successfully and enters the main page of the project, the number of online people + 1
    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        System.out.println("Login successful"+hse.getSession().getId()+"Created on:"+new Date());
        ServletContext application = hse.getSession().getServletContext();
        int sessionCount = (int)application.getAttribute("sessionCount");

        application.setAttribute("sessionCount", ++sessionCount);
        System.out.println("The current number of online users is:"+application.getAttribute("sessionCount"));
    }
    //Listen for session destruction events - there are many ways to destroy: forced destruction, automatic expiration, etc. it doesn't matter. You can listen as long as the session is destroyed
    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        System.out.println("Exit successful"+hse.getSession().getId()+"Exit time:"+new Date());
        ServletContext application = hse.getSession().getServletContext();
        int sessionCount = (int)application.getAttribute("sessionCount");

        application.setAttribute("sessionCount", --sessionCount);
        System.out.println("The current number of online users is:"+application.getAttribute("sessionCount"));
    }


}

Realization effect

In this way, every time we visit a page through different browsers, a new user will be added. The whole change process is as follows:

Pay attention to tick this off, otherwise the idea will generate an additional session for you at startup, and there will be two sessions at startup.

To sum up

In fact, the biggest function of Servlet listener is to be regarded as a state machine. In fact, in any process, it is abstracted through the life cycle management of events to event sources. Objects flow in the process driven by events. The same is true in system design, so in fact, the listener can make us understand more about the design idea of event driven life cycle. Just as the filter gives us more understanding about the control of AOP on all services, the more important thing is the idea, and the components are just the landing of ideas. With ideas, we can apply them in more places.