web监听器解析

监听器是web三大组件之一,事件监听机制如下:

  • 事件:某个事件,如果初始化上下文
  • 事件源:事件发生的地方
  • 监听器:一个对象,拥有需要执行的逻辑
  • 注册监听:将事件、事件源、监听器绑定在一起。当事件源发生某个事件后,将事件传递给监听器,监听器执行相应代码逻辑

添加监听器

在web项目中的web.xml配置文件中一般有这样一段代码,ContextLoaderListener是一个监听器实现了ServletContextListener接口。在后续解析web.xml文件中会添加到StandardContext上下文的applicationListeners数组中,之后当上下文开启后会根据监听器的全限定名构造监听器实例并初始化监听器。

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

web监听器解析

ServletContextListener能在web应用程序初始化的过程中收到通知,在过滤器filter和servlet初始化之前执行相应的上下文初始化逻辑,也能在servlet上下文关闭时收到通知,在过滤器filter和servlet销毁后执行相应的上下文销毁逻辑。

public interface ServletContextListener extends EventListener {

    /**
     ** Notification that the web application initialization process is starting.

     * All ServletContextListeners are notified of context initialization before
     * any filter or servlet in the web application is initialized.

     * @param sce Information about the ServletContext that was initialized
     */
    public void contextInitialized(ServletContextEvent sce);

    /**
     ** Notification that the servlet context is about to be shut down. All
     * servlets and filters have been destroy()ed before any
     * ServletContextListeners are notified of context destruction.

     * @param sce Information about the ServletContext that was destroyed
     */
    public void contextDestroyed(ServletContextEvent sce);
}

初始化监听器

当StandardContext上下文启动后会调用 listenerStart方法,该方法初始化所有添加到context的监听器,之后构建事件执行ServletContextListener类型的监听器 listener.contextInitialized(event);初始化上下文

public boolean listenerStart() {

    if (log.isDebugEnabled()) {
        log.debug("Configuring application event listeners");
    }

    // Instantiate the required listeners
    // &#x6CE8;&#x518C;&#x76D1;&#x542C;&#x5668;&#x7684;&#x5168;&#x9650;&#x5B9A;&#x540D;&#x6570;&#x7EC4;
    String listeners[] = findApplicationListeners();
    Object results[] = new Object[listeners.length];
    boolean ok = true;
    for (int i = 0; i < results.length; i++) {
        if (getLogger().isDebugEnabled()) {
            getLogger().debug(" Configuring event listener class '" +
                listeners[i] + "'");
        }
        try {
            String listener = listeners[i];
            // &#x5B9E;&#x4F8B;&#x5316;&#x76D1;&#x542C;&#x5668;
            results[i] = getInstanceManager().newInstance(listener);
        } catch (Throwable t) {
            t = ExceptionUtils.unwrapInvocationTargetException(t);
            ExceptionUtils.handleThrowable(t);
            getLogger().error(sm.getString(
                    "standardContext.applicationListener", listeners[i]), t);
            ok = false;
        }
    }
    if (!ok) {
        getLogger().error(sm.getString("standardContext.applicationSkipped"));
        return false;
    }

    // Sort listeners in two arrays
    List<object> eventListeners = new ArrayList<>();
    List<object> lifecycleListeners = new ArrayList<>();
    // &#x5206;&#x7EC4; &#x533A;&#x5206;&#x4E8B;&#x4EF6;&#x76D1;&#x542C;&#x5668;&#x548C;&#x751F;&#x547D;&#x5468;&#x671F;&#x76D1;&#x542C;&#x5668;
    // ServletContextListener&#x4E3A;&#x751F;&#x547D;&#x5468;&#x671F;&#x76D1;&#x542C;&#x5668;
    for (Object result : results) {
        if ((result instanceof ServletContextAttributeListener)
                || (result instanceof ServletRequestAttributeListener)
                || (result instanceof ServletRequestListener)
                || (result instanceof HttpSessionIdListener)
                || (result instanceof HttpSessionAttributeListener)) {
            eventListeners.add(result);
        }
        if ((result instanceof ServletContextListener)
                || (result instanceof HttpSessionListener)) {
            lifecycleListeners.add(result);
        }
    }

    // Listener instances may have been added directly to this Context by
    // ServletContextInitializers and other code via the pluggability APIs.

    // Put them these listeners after the ones defined in web.xml and/or
    // annotations then overwrite the list of instances with the new, full
    // list.

    // &#x8BBE;&#x7F6E;&#x5E94;&#x7528;&#x7684;&#x4E8B;&#x4EF6;&#x76D1;&#x542C;&#x5668;&#x548C;&#x751F;&#x547D;&#x5468;&#x671F;&#x76D1;&#x542C;&#x5668;
    eventListeners.addAll(Arrays.asList(getApplicationEventListeners()));
    setApplicationEventListeners(eventListeners.toArray());
    for (Object lifecycleListener: getApplicationLifecycleListeners()) {
        lifecycleListeners.add(lifecycleListener);
        if (lifecycleListener instanceof ServletContextListener) {
            noPluggabilityListeners.add(lifecycleListener);
        }
    }
    setApplicationLifecycleListeners(lifecycleListeners.toArray());

    // Send application start events

    if (getLogger().isDebugEnabled()) {
        getLogger().debug("Sending application start events");
    }

    // Ensure context is not null
    getServletContext();
    // &#x4E0D;&#x5141;&#x8BB8;&#x8BBE;&#x7F6E;&#x65B0;&#x7684;&#x76D1;&#x542C;
    context.setNewServletContextListenerAllowed(false);

    Object instances[] = getApplicationLifecycleListeners();
    if (instances == null || instances.length == 0) {
        return ok;
    }

    // &#x6839;&#x636E;servlet&#x4E0A;&#x4E0B;&#x6587;&#x6784;&#x5EFA;ServletContextEvent&#x4E8B;&#x4EF6;
    ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
        noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
        tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    // &#x8C03;&#x7528;ServletContextListener&#x76D1;&#x542C;&#x5668;
    for (Object instance : instances) {
        if (!(instance instanceof ServletContextListener)) {
            continue;
        }
        ServletContextListener listener = (ServletContextListener) instance;
        try {
            fireContainerEvent("beforeContextInitialized", listener);
            if (noPluggabilityListeners.contains(listener)) {
                listener.contextInitialized(tldEvent);
            } else {
                // &#x5BB9;&#x5668;&#x4E0A;&#x4E0B;&#x6587;&#x521D;&#x59CB;&#x5316; &#x8C03;&#x7528;&#x76D1;&#x542C;&#x5668;contextInitialized&#x65B9;&#x6CD5;&#x521D;&#x59CB;&#x5316;
                listener.contextInitialized(event);
            }
            fireContainerEvent("afterContextInitialized", listener);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            fireContainerEvent("afterContextInitialized", listener);
            getLogger().error(sm.getString("standardContext.listenerStart",
                    instance.getClass().getName()), t);
            ok = false;
        }
    }
    return ok;

}</object></object>

ContextLoaderListener解析

ContextLoaderListener上下文加载监听器有什么作用呢?它是spring的一个类,主要用来初始化spring容器XmlWebApplicationContext。我们也可以用contextClass指定上下文类使用支持扫描注解的AnnotationConfigWebApplicationContext,如下:

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.monian.study.config.AppConfig</param-value>
</context-param>
@Configuration
@ComponentScan(basePackages = "com.monian.study")
@PropertySource(value = {"classpath:oss.properties", "classpath:datasource.properties"})
public class AppConfig {

}

当执行 contextInitialized方法时初始化根web应用上下文:根据contextClass配置的上下文类AnnotationConfigWebApplicationContext若没有配置则默认XmlWebApplicationContext进行类加载后再通过反射实例化web容器,接着对web容器容器id更新、配置文件路径设置等初始化操作,最后调用refresh()方法刷新容器,将 AppConfig 作为初始java配置文件,扫描basePackages包下的所有类解析spring bean构建spring容器

/**
* Initialize the root web application context.

*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    // ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE&#x5C5E;&#x6027;&#x4E3A;&#x7A7A;&#xFF0C;&#x4E0D;&#x4E3A;&#x7A7A;&#x8BF4;&#x660E;&#x53EF;&#x80FD;&#x6709;&#x91CD;&#x590D; &#x62A5;&#x9519;
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException(
                "Cannot initialize context because there is already a root application context present - " +
                "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }

    Log logger = LogFactory.getLog(ContextLoader.class);
    servletContext.log("Initializing Spring root WebApplicationContext");
    if (logger.isInfoEnabled()) {
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // Store context in local instance variable, to guarantee that
        // it is available on ServletContext shutdown.
        // &#x521B;&#x5EFA;contextClass&#x6307;&#x5B9A;&#x7684;web&#x4E0A;&#x4E0B;&#x6587;
        if (this.context == null) {
            this.context = createWebApplicationContext(servletContext);
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent ->
                    // determine parent for root web application context, if any.
                    // &#x5982;&#x679C;&#x6709;&#x7236;&#x5BB9;&#x5668;&#x7684;&#x8BDD;&#x8FDB;&#x884C;&#x8BBE;&#x7F6E;
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                // &#x914D;&#x7F6E;&&#x5237;&#x65B0;&#x5BB9;&#x5668;
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        // &#x5C06;&#x5BB9;&#x5668;&#x4E0A;&#x4E0B;&#x6587;&#x8BBE;&#x7F6E;&#x5230;servletContext&#x7684;ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE&#x5C5E;&#x6027;&#x4E2D;
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
        }
        else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
        }
        if (logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
        }

        return this.context;
    }
    catch (RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
    catch (Error err) {
        logger.error("Context initialization failed", err);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
        throw err;
    }
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    // &#x9ED8;&#x8BA4;&#x76F8;&#x540C; &#x91CD;&#x65B0;&#x8BBE;&#x7F6E;&#x5BB9;&#x5668;&#x4E0A;&#x4E0B;&#x6587;&#x6807;&#x8BC6;
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        // &#x4ECE;servlet&#x4E0A;&#x4E0B;&#x6587;&#x83B7;&#x53D6;contextId
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {
            // Generate default id... &#x8BBE;&#x7F6E;&#x4E0A;&#x4E0B;&#x6587;id
            // WebApplicationContext:+contextPath
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    // &#x8BBE;&#x7F6E;servlet&#x4E0A;&#x4E0B;&#x6587;
    wac.setServletContext(sc);
    // &#x83B7;&#x53D6;&#x914D;&#x7F6E;&#x6587;&#x4EF6;&#x8DEF;&#x5F84;contextConfigLocation
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    // &#x8BBE;&#x7F6E;contextConfigLocation
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam);
    }

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    // &#x521D;&#x59CB;&#x5316;&#x73AF;&#x5883;&#x914D;&#x7F6E;
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        // &#x521D;&#x59CB;&#x5316;servlet&#x5C5E;&#x6027;&#x8D44;&#x6E90;
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    // &#x81EA;&#x5B9A;&#x4E49;&#x521D;&#x59CB;&#x5316;
    customizeContext(sc, wac);

    // &#x5237;&#x65B0;web&#x5BB9;&#x5668; &#x521D;&#x59CB;&#x5316;bean
    wac.refresh();
}

Original: https://www.cnblogs.com/monianxd/p/16617576.html
Author: 默念x
Title: web监听器解析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621838/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • delete-drop语句生成的存储过程

    问题: 有时,您需要在开发过程中操作许多表。 [En] Sometimes you need to manipulate many tables during developmen…

    数据库 2023年5月24日
    0139
  • 图数据挖掘!使用图分析+AI进行保险欺诈检测 ⛵

    💡 作者:韩信子@ShowMeAI📘 机器学习实战系列:https://www.showmeai.tech/tutorials/41📘 本文地址:https://www.showm…

    数据库 2023年6月14日
    0108
  • Minio的安装与使用

    Minio的安装与使用 一、Minio介绍 MinIO 是在 Apache License v2.0 下发布的高性能对象存储. 就是说是个存东西的玩意,比较方便配好启动就能访问,也…

    数据库 2023年6月6日
    0122
  • MySQL 中如何归档数据

    归档,在 MySQL 中,是一个相对高频的操作。 它通常涉及以下两个操作: [En] It usually involves the following two actions: …

    数据库 2023年5月24日
    0120
  • 高并发组件了解

    消息队列 A服务和多个服务耦合,内部维护对多个服务发送数据的接口,那么这些接口如果有的挂了,有的不需要了,那么还得修改A内部的代码,如果使用MQ,A发送消息就好,不必考虑那么多事情…

    数据库 2023年6月16日
    083
  • 如何实现跨域?

    https://blog.csdn.net/meism5/article/details/90414283 Original: https://www.cnblogs.com/cr…

    数据库 2023年6月16日
    0106
  • 配置中心Nacos(服务发现)

    服务演变之路 单体应用架构 在刚开始的时候,企业的用户量、数据量规模都⽐较⼩,项⽬所有的功能模块都放在⼀个⼯程中编码、编译、打包并且部署在⼀个Tomcat容器中的架构模式就是单体应…

    数据库 2023年6月9日
    0140
  • 深入浅出的分析 Properties

    作者:炸鸡可乐原文出处:www.pzblog.cn 一、摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap、LinkedHashMap、TreeMap、Ide…

    数据库 2023年6月14日
    0103
  • Mysql数据库存取原理及性能优化

    一、Mysql的系统架构图 二、Mysql存储引擎 Mysql中的数据是通过一定的方式存储在文件或者内存中的,任何方式都有不同的存储、查找和更新机制,这意味着选择不同的方式对于数据…

    数据库 2023年5月24日
    091
  • Spark学习(2) RDD编程

    RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、弹性、里面的元素可并行计算的集合 R…

    数据库 2023年6月16日
    0130
  • 12、HSSFWorkbook实现多张sheet导出

    转载自 一、封装一个通用的装载数据的实体类: import lombok.AllArgsConstructor; import lombok.Data; import lombok…

    数据库 2023年6月6日
    0129
  • 通过VS下载的NuGet包,如何修改其下载存放路径?

    我们通过NuGet包管理器下载的引用包,默认是存放在C盘的,存储路径一般是: C:\Users\{&#x7CFB;&#x7EDF;&#x7528;&…

    数据库 2023年6月14日
    0223
  • MySQL 数据备份与恢复

    数据备份 使用 mysqldump 命令可以将数据库中的数据备份成一个文本文件,表的结构和数据以 SQL 的形式将存储生成的文本文件 mysqldump -u username -…

    数据库 2023年5月24日
    0156
  • 类加载器及其加载原理

    概述 在之前的文章”类的加载流程”讲了一个Class文件从加载到卸载整个生命周期的过程,并且提到”非数组类在加载阶段是可控性最强的”…

    数据库 2023年6月11日
    0132
  • jenkins 忘记密码

    仅适用centos7 一、 忘记密码 终端输入: vi /root/.jenkins/secrets/initialAdminPassword 复制文本内的密码,进行登录,此密码可…

    数据库 2023年6月14日
    0102
  • VUE 打包测试部署项目到linux服务器,

    干就完了 一、项目根目录输入,如E:\demo>npm install csharp;gutter:true; npm installee</p> <pre…

    数据库 2023年6月6日
    0206
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球