目录
5、监听器的实例在tomcat中是如何创建和注册的?源码展示
1、什么是监听器,监听器有什么作用?
监听器 Listener 是一个实现特定接口的程序,它基于观察者模式实现,可以在程序运行时对事件进行监控和响应。使用监听器可以使程序更加灵活和可扩展,具有以下几点优点:
- 分离关注点:将事件处理逻辑和业务逻辑分离,使程序更加模块化和易于维护。
- 降低耦合性:将事件的产生和处理解耦,使程序更加灵活和可扩展。
- 提高可重用性:监听器可以重复使用,减少代码冗余。
- 提高程序响应速度:当事件发生时,监听器可以立即响应,提高程序响应速度。
- 提高程序的可靠性:监听器可以对程序中可能发生的异常进行处理,提高程序的健壮性和可靠性。
// 本质来说只是一种代码的设计模式,既然是设计模式,无非就是解耦合,提高程序可拓展性之类的云云。
2、Servlet 中有哪些监听器?
Servlet 中的监听器主要用于监听 ServletContext、HttpSession 和 ServletRequest 等对象的生命周期和属性的变化,这些监听器可以用来处理 Web 应用程序中的各种事件,例如初始化、销毁、属性变化等。
在 Servlet 中,常见的监听器包括以下三种:
- ServletContextListener:用于监听 ServletContext 对象的生命周期和属性变化。当 ServletContext 对象被创建或销毁时,或者 ServletContext 对象的属性发生变化时,ServletContextListener 可以捕获这些事件并进行处理。
- HttpSessionListener:用于监听 HttpSession 对象的生命周期和属性变化。当 HttpSession 对象被创建或销毁时,或者 HttpSession 对象的属性发生变化时,HttpSessionListener 可以捕获这些事件并进行处理。
- ServletRequestListener:用于监听 HttpServletRequest 对象的生命周期和属性变化。当 HttpServletRequest 对象被创建或销毁时,或者 HttpServletRequest 对象的属性发生变化时,ServletRequestListener 可以捕获这些事件并进行处理。
3、如何使用Servlet自带的监听器接口?
使用监听器通常需要以下几个步骤:// 总结起来就是:实现监听器接口并注册
- 实现监听器接口:根据需要,选择要使用的监听器类型,并实现相应的接口,例如实现 HttpSessionListener 接口来监听 HttpSession 对象的创建和销毁事件。
- 注册监听器:在 web.xml 文件中配置监听器,并将其注册到相应的组件中,例如在 web.xml 文件中配置 HttpSessionListener 监听器。// 也可以使用注解进行配置
以下是使用注解方式配置一个监听器的示例:
首先实现监听器接口,MyHttpSessionListener 实现了 HttpSessionListener 接口来监听 HttpSession 对象的创建和销毁事件。然后,使用 @WebListener 注解标注该监听器类:
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("Session Created: " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Session Destroyed: " + se.getSession().getId());
}
}
4、如何自定义一个监听器的实现?
自定义一个监听器,可以按照以下步骤进行:
(1)定义监听器接口:根据需要,首先定义一个监听器接口,并在其中定义相应的事件方法。例如,定义一个名为 MyListener 的监听器接口,其中包含一个名为 handleEvent 的方法用于处理事件。
public interface MyListener {
void handleEvent(MyEvent event);
}
(2)实现事件对象:定义一个事件对象 MyEvent,用于封装事件相关的信息。
public class MyEvent {
private String message;
public MyEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
(3)实现监听器:实现 MyListener 接口,并在其中实现 handleEvent 方法,用于处理事件。
public class MyListenerImpl implements MyListener {
@Override
public void handleEvent(MyEvent event) {
System.out.println("Received event message: " + event.getMessage());
}
}
(4)实现事件源:定义一个事件源对象 MyEventSource,并在其中添加注册、移除和触发监听器事件的方法。// 这一步很重要,把事件和监听器关联起来
import java.util.ArrayList;
import java.util.List;
public class MyEventSource {
private List<MyListener> listeners = new ArrayList<>();
public void addListener(MyListener listener) {
listeners.add(listener);
}
public void removeListener(MyListener listener) {
listeners.remove(listener);
}
public void fireEvent(MyEvent event) {
for (MyListener listener : listeners) {
listener.handleEvent(event);
}
}
}
使用监听器:创建 MyEventSource 对象,并向其注册 MyListener 监听器,然后触发事件并处理监听器事件。
public class TestListener {
public static void main(String[] args) {
MyEventSource source = new MyEventSource();
MyListener listener = new MyListenerImpl();
source.addListener(listener);
source.fireEvent(new MyEvent("Hello, world!"));
}
}
在实现自定义监听器时,需要注意编写良好的代码结构和设计,以便扩展和维护。同时,应该根据实际需求选择合适的事件类型和监听器类型,并编写相应的处理逻辑,以实现所需的功能。
// 整个过程看下来,感觉像不像就是设计模式?不用感觉,它就是!!!
5、监听器的实例在tomcat中是如何创建和注册的?源码展示
@WebListener注解是由Java Servlet规范提供的,其具体实现由Servlet容器厂商完成。以下是Tomcat容器实现@WebListener注解的关键源码展示:// 监听器由Servlet容器创建
首先,Tomcat容器会在启动时扫描应用程序中所有的类,通过解析注解信息来实例化监听器对象:
public void startInternal() throws LifecycleException {
...
// Instantiate and initialize all listeners defined in our context
for (ServletContextListener listener : context.findListeners()) {
instanceSupport.addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
listener.contextInitialized(new ServletContextEvent(context));
} else if (event.getType().equals(Lifecycle.BEFORE_STOP_EVENT)) {
listener.contextDestroyed(new ServletContextEvent(context));
}
}
});
}
...
}
在上述代码中,通过调用context.findListeners()方法获取所有标注了@WebListener注解的监听器类,然后通过反射机制实例化这些类,并将其注册到对应的事件源对象中。
public static List<ServletContextListener> findListeners(
ServletContext servletContext, ClassLoader classLoader) {
List<ServletContextListener> listeners = new ArrayList<>();
Set<Class<?>> listenerClasses = new LinkedHashSet<>();
Set<Class<?>> classes = AnnotationProcessor.scanClasspath(classLoader, true, WebListener.class);
listenerClasses.addAll(classes);
for (Class<?> clazz : listenerClasses) {
WebListener ann = clazz.getAnnotation(WebListener.class);
if (ann == null) {
continue;
}
Class<?> targetClass = clazz;
while (targetClass != null) {
if (isListenerClass(targetClass)) {
break;
}
targetClass = targetClass.getSuperclass();
}
if (targetClass == null) {
targetClass = clazz;
}
try {
listeners.add((ServletContextListener) targetClass.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
return listeners;
}
在上述代码中,通过扫描应用程序中所有的类,并解析其中的@WebListener注解信息。如果找到了标注了该注解的类,容器会将该类转换成对应的监听器类型,并将其实例化。
需要注意的是,不同的Servlet容器对@WebListener注解的实现逻辑可能会略有不同,但大体的原理和实现方法都是类似的。
6、同类型的监听器,如何保障监听器的执行顺序?
使用@WebListener注解声明的监听器的执行顺序是不确定的,因为Servlet规范并没有明确规定使用注解声明的监听器的执行顺序。
如果需要保证监听器的执行顺序,最好还是使用web.xml文件进行声明。在web.xml中声明的监听器顺序就是它们被执行的顺序。但是,在使用注解声明监听器时,也可以通过实现Ordered接口或使用@Order注解来指定监听器的执行顺序。