1. Basic concepts
The listener in JavaWeb is implemented through the observer design pattern. Regarding the observer mode, I won’t give too much introduction here, so I will roughly talk about what it means.
Observer mode is also called publish subscription mode or listener mode. There are two characters in this mode: the observer and the observer (usually also called the subject). The observer registers an event of interest in the topic. When this event occurs, the topic will notify the observer through a callback interface.
Let me give you an example in life: subscribe to newspapers. Any family or individual can subscribe to newspapers with the newspaper. Here the newspaper is the "theme" and the family is the "observer". For example, if a family needs to subscribe to the newspaper tomorrow morning, this is an "event". The next morning, the newspaper was produced, and this was the "event". When the incident occurs, the newspaper sends the newspaper to the home mailbox, which is the "callback interface".
For listeners in JavaWeb, the Servlet specification defines some columns of Listener interface classes, exposing events to the application through interface classes. If an application wants to listen for events of interest, it does not have to directly register the corresponding event. Instead, it writes its own listener to implement the corresponding interface class and registers its own listener to the servlet container. When an event that the program cares about occurs, the servlet container will notify the listener and call back the methods in the listener. The customized listener here is the observer, and the servlet container is the theme.
2. Sample analysis
As mentioned above, the servlet container exposes events to the application through the Listener interface class. So we are not so much about registering events, but rather registering listeners. The corresponding programming steps are: 1. Write your own listener and implement a specific Listener interface. 2. Register your listener in web.xml. Here is an example of the simplest listener interface ServletContextListener:
1.TestListener.java
public class TestListener implements ServletContextListener {public TestListener() {}public void contextInitialized(ServletContextEvent scce) {System.out.println("ServletContextListener.contextInitialized");}public void contextDestroyed(ServletContextEvent scce) {System.out.println("ServletContextListener.contextDestroyed");}}2.web.xml
<listener><listener-class>com.nantang.listener.TestListener</listener-class></listener>
When the container starts up, "ServletContextListener.contextInitialized" will be output to the log, and when the container is closed, "ServletContextListener.contextDestroyed" will be output. A detailed explanation will be further analyzed later.
It should be noted here that if you demonstrate the above example in the IDE (Eclipse, STS, etc.), when starting the server, you can see "ServletContextListener.contextInitialized" in the console, and when closing the server, you cannot see "ServletContextListener.contextDestroyed". This is not that the contextDestroyed method is not executed, but that the IDE is not implementing it perfectly. To verify that contextDestroyed is indeed called, you can write a piece of code in contextDestroyed to output the contents of the file instead of outputting it to the console.
3. Source code analysis
Now let's analyze which events the servlet specification defines for us. More precisely, which listening interfaces are defined. The following introductions are based on the servlet 3.0 specification.
servlet3.0 provides us with 8 listener interfaces, which can be divided into three categories according to their scope:
1.Servlet context-related listening interfaces, including: ServletContextListener and ServletContextAttributeListener.
2. http session-related listening interfaces, including: HttpSessionListener, HttpSessionActivationListener, HttpSessionAttributeListener and HttpSessionBindingListener.
3. The listening interface related to servlet request, including: ServletRequestListener and ServletRequestAttributeListener.
In fact, from the naming of the interface, you should be able to guess its basic functions. Let's explain it by category below.
1.Servlet context-related listening interface
When introducing Servlets before, we explained that a web application corresponds to a servlet context. Therefore, the life range of events listened to by ServletContextListener and ServletContextAttributeListener runs through the entire web application. Below is the class diagram hierarchy relationship between these two interfaces.
1.1 EventListener
EventListener is a tag interface, and all event listeners must inherit this interface. This is the servlet specification, and there is no explanation.
1.2 EventObject
Similar to EventListener, EventObject is an event-level class, and all specific event classes must inherit EventObject.
public class EventObject implements java.io.Serializable {protected transient Object source;public EventObject(Object source) {if (source == null)throw new IllegalArgumentException("null source");this.source = source;}public Object getSource() {return source;}public String toString() {return getClass().getName() + "[source=" + source + "]";}}This class is very simple, its essence is just one thing: source. Through the class name EventObject and the property name source, you can see that this class does one thing, holding the "event source object".
1.3 ServletContextEvent
public class ServletContextEvent extends java.util.EventObject { public ServletContextEvent(ServletContext source) {super(source);}public ServletContext getServletContext () { return (ServletContext) super.getSource();}}servlet context event, this event class is a simple inheritance of EventObject. The ServletContext instance is provided as the event source in the constructor. Because the event source is a servlet context, a getServletContext is provided to obtain the ServletContext instance.
When we explain other event classes in the future, they are all the same mold. Each event class provides the corresponding construction method, passes in the corresponding event source object, and provides additional methods to obtain the event source. Therefore, EventObject is the base class of event source. The essence of all event subclasses does one thing, determine the specific event source object.
So the place where we will explain the incident later passes by.
1.4 ServletContextListener
public interface ServletContextListener extends EventListener {public void contextInitialized ( ServletContextEvent sce );public void contextDestroyed ( ServletContextEvent sce );}The servlet context listener interface corresponds to two events: the servlet context initialization event and the servlet context closing event.
When the web application is initialized, the servlet container will construct the ServletContextEven instance and call back the contextInitialize method.
When the servlet context is about to be closed, usually before closing the server, the servlet container will construct the ServletContextEven instance and call back the contextDestroyed method. It should be noted here that the execution of the contextDestroyed method will be performed after all servlets and filters have completed the destroy method.
So if we want to do something when the application starts or closes, we will write our own listener to implement the interface.
All event listeners are also the same model. The corresponding event callback interface method is defined according to the servlet specification. The method entry parameter is the corresponding event source instance. So we will also pass by the place where we will explain the monitor later.
1.5 ServletContextAttributeEvent
public class ServletContextAttributeEvent extends ServletContextEvent { private String name;private Object value;public ServletContextAttributeEvent(ServletContext source, String name, Object value) {super(source);this.name = name;this.value = value;}public String getName() {return this.name;}public Object getValue() {return this.value; }}ServletContextAttributeEvent represents an event related to the servlet context attribute. Generally, the event will be triggered when the attribute changes. This class inherits ServletContextEven, and the event source is also a ServletContext instance. Additional methods for obtaining attribute names and attribute values are provided.
1.6 ServletContextAttributeListener
public interface ServletContextAttributeListener extends EventListener {public void attributeAdded(ServletContextAttributeEvent scab);public void attributeRemoved(ServletContextAttributeEvent scab);public void attributeReplaced(ServletContextAttributeEvent scab);}When the above attributes of the servlet are added, deleted, or modified, the servlet container constructs the ServletContextAttributeEvent event object, and calls back the attributeAdded, attributeRemoved, and attributeReplaced methods respectively.
What you need to note here is the attributeReplaced method, which calls back when the value of the attribute is replaced. At this time, if you call ServletContextAttributeEvent.getValue() method, the return is to replace the previous attribute value.
2 http session related listening interface
2.1 HttpSessionEvent
public class HttpSessionEvent extends java.util.EventObject {public HttpSessionEvent(HttpSession source) {super(source);}public HttpSession getSession () { return (HttpSession) super.getSource();}}http session-related event, which will be triggered when the session changes. The event source is an HttpSession instance and provides additional HttpSession acquisition methods.
2.2 HttpSessionListener
public interface HttpSessionListener extends EventListener {public void sessionCreated ( HttpSessionEvent se );public void sessionDestroyed ( HttpSessionEvent se );}When the session is created and destroyed, the servlet container constructs the HttpSessionEvent event object and calls back the sessionCreated and sessionDestroyed methods.
2.3 HttpSessionActivationListener
public interface HttpSessionActivationListener extends EventListener { public void sessionWillPassivate(HttpSessionEvent se); public void sessionDidActivate(HttpSessionEvent se);}When the session is about to be passivated or has been activated, the servlet container constructs the HttpSessionEvent event object, callback sessionWillPassivate and sessionDidActivate methods.
Passivation and activation are explained here: Passivation refers to the server's memory is insufficient or the activity timeout of the session has arrived, and the recently inactive session is serialized to disk. Activation means that a passive session is accessed again, deserializing the session from disk to memory.
It can be seen here that in order to passivate and activate, the session must be serialized and deserialized first. At the same time, during the programming process, we try to use simple objects such as String and Integer as much as possible, and try not to use collections such as list and map.
2.4 HttpSessionBindingEvent
public class HttpSessionBindingEvent extends HttpSessionEvent {private String name;private Object value;public HttpSessionBindingEvent(HttpSession session, String name) {super(session);this.name = name;}public HttpSessionBindingEvent(HttpSession session, String name, Object value) {super(session);this.name = name;this.value = value;}public HttpSession getSession () { return super.getSession();}public String getName() {return name;}public Object getValue() {return this.value; }}The event related to the http session attribute is triggered when the session attribute changes. The event source is an HttpSession instance and provides additional methods to obtain HttpSession, attribute name, and attribute value.
2.5 HttpSessionAttributeListener
public interface HttpSessionAttributeListener extends EventListener {public void attributeAdded ( HttpSessionBindingEvent se );public void attributeRemoved ( HttpSessionBindingEvent se );public void attributeReplaced ( HttpSessionBindingEvent se );}When the session attribute is added, deleted, or modified, the servlet container constructs the HttpSessionBindingEvent event object and calls back the attributeAdded, attributeRemoved, and attributeReplaced methods respectively.
What you need to note here is the attributeReplaced method, which calls back when the value of the attribute is replaced. At this time, if you call ServletContextAttributeEvent.getValue() method, the return is to replace the previous attribute value.
When the invalidate method of the session is called or the session fails, the attributeRemoved method will also be called back.
2.6 HttpSessionBindingListener
public interface HttpSessionBindingListener extends EventListener {public void valueBound(HttpSessionBindingEvent event);public void valueUnbound(HttpSessionBindingEvent event);}This listener also listens for changes in the session's attribute. When the session attribute is added and deleted, that is, when the attribute value is bound and the attribute value is unbined, the servlet container constructs the HttpSessionBindingEvent event object and calls back the valueBound and valueUnbound methods respectively.
It looks no different from HttpSessionAttributeListener, but it is not the case. One essential difference between the two is the condition of event triggering.
When there is any change in the session property, the servlet container will notify the HttpSessionAttributeListener. But for HttpSessionBindingListener, the servlet container will notify only if the bound or unbinded property value is an instance of the listener. For example:
public class TestListener implements HttpSessionBindingListener{@Overridepublic void valueBound(HttpSessionBindingEvent event) {System.out.println("HttpSessionBindingListener.valueBound");}@Overridepublic void valueUnbound(HttpSessionBindingEvent event) {System.out.println("HttpSessionBindingListener.valueUnbound");}}We customize the listener TestListener to implement HttpSessionBindingListener. Let's set the following session attribute in the code:
HttpSession session = request.getSession();TestListener testListener=new TestListener();session.setAttribute("listener", testListener);session.removeAttribute("listener");Here the attribute value of the session is our listener TestListener instance. So when this code is executed, the servlet container will notify the TestListener and call back the valueBound and valueUnbound methods.
It should be noted here that when the invalidate method of the session is called or the session fails, the valueUnbound method will also be called back.
3 servlet request-related listening interface
3.1 ServletRequestEvent
public class ServletRequestEvent extends java.util.EventObject { private ServletRequest request;public ServletRequestEvent(ServletContext sc, ServletRequest request) {super(sc);this.request = request;}public ServletRequest getServletRequest () { return this.request;}public ServletContext getServletContext () { return (ServletContext) super.getSource();}}The event related to the servlet request will be triggered when the request changes. The event source is a ServletContext instance and provides additional fetching of ServletContext and ServletRequest methods.
3.2 ServletRequestListener
public interface ServletRequestListener extends EventListener {public void requestDestroyed ( ServletRequestEvent sre );public void requestInitialized ( ServletRequestEvent sre );}When the request is initialized or destroyed, the client requests to enter the web application (enter the servlet or the first filter) or the web application returns a response to the client (exit the servlet or the first filter). The servlet container constructs the ServletRequestEvent instance, and calls back the requestInitialized and requestDestroyed methods.
3.3 ServletRequestAttributeEvent
public class ServletRequestAttributeEvent extends ServletRequestEvent { private String name;private Object value;public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request, String name, Object value) {super(sc, request);this.name = name;this.value = value;}public String getName() {return this.name;}public Object getValue() {return this.value; }}The event related to the servlet requests the attribute, which will be triggered when the request attribute changes. The event source is a ServletContext instance and provides additional methods to get attribute names and attribute values.
3.4 ServletRequestAttributeListener
public interface ServletRequestAttributeListener extends EventListener {public void attributeAdded(ServletRequestAttributeEvent srae);public void attributeRemoved(ServletRequestAttributeEvent srae);public void attributeReplaced(ServletRequestAttributeEvent srae);}When the requested attributes are added, deleted, or modified, the servlet container constructs the ServletRequestAttributeEvent event object, and call back the attributeAdded, attributeRemoved, and attributeReplaced methods respectively.
What you need to note here is the attributeReplaced method, which calls back when the value of the attribute is replaced. At this time, if you call ServletRequestAttributeEvent.getValue() method, the return is to replace the previous attribute value.
4. Summary
At this point, listener finished speaking. We can find that listener, servlet and filter have one thing in common, both are scheduled by containers. We just need to write our own listener to implement the listener interface we care about and register. The rest of the job is to write business logic in our own listener.
The listener introduced in this blog post is formulated by the servlet 3.0 specification. 3.1 has added some event listener interfaces, and the principles are similar, readers can understand them by themselves.