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)

大家都在看

  • podman基础用法

    podman的基础设置和使用 运行示例容器 列出正在运行的容器 检查正在运行的容器 测试httpd服务器 查看容器的日志 查看容器进程pid 对容器执行检查操作 还原容器 迁移容器…

    数据库 2023年6月14日
    0138
  • [springmvc]ssm框架整合超简单

    此整合没有具体的业务,因为ssm整合最难的点就在于配置文件的整合,因此这里只详细记录配置文件的整合 spring和dao整合将mybatis的配置文件的数据库连接和sqlsessi…

    数据库 2023年6月16日
    075
  • markdown语法备忘-七星海棠

    posted @2022-03-31 15:52 七星海棠^_~ 阅读(5 ) 评论() 编辑 Original: https://www.cnblogs.com/qixingha…

    数据库 2023年6月11日
    066
  • 2 Java中 == 和 equals 和 hashCode 的区别

    ==是一个比较运算符; 若比较的是基本数据类型,则比较的是值; 若比较的是引用数据类型,则比较的是它们在内存中的内存地址。 说明:对象是存放在堆中,栈中存放的是对象的引用,因此==…

    数据库 2023年6月6日
    095
  • 2_jQuery

    jQuery, 顾名思义, 也就是JavaScript和查询(Query), 它就是辅助JavaScript开发的js类库 它的核心思想write less, do more(写的…

    数据库 2023年6月11日
    097
  • Docker三种文件系统总结

    概述 容器持久化,相比小伙伴都不陌生。通过Docker的volume,我们可以非常方便的实现容器数据的持久化存储。但volume之下的文件系统,相比许多小伙伴并不是非常清楚。因而本…

    数据库 2023年6月11日
    0137
  • Docker 启动各个应用

    MySQL docker run -p 3306:3306 –name mysql -v /usr/mydata/mysql/log:/var/log/mysql -v /usr…

    数据库 2023年6月6日
    074
  • 软件测试流程是什么?这题我不会啊

    转载请注明出处❤️ 作者:测试蔡坨坨 原文链接:caituotuo.top/ae4fff18.html 你好,我是测试蔡坨坨。 最近收到不少准备转行软件测试的小伙伴私信问真实企业里…

    数据库 2023年6月11日
    0103
  • 分布式消息队列RocketMQ(一)安装与启动

    分布式消息队列RocketMQ 一、RocketMQ简介 RocketMQ(火箭MQ) 出自于阿里,后开源给apache成为apache的顶级开源项目之一,顶住了淘宝10年的 双1…

    数据库 2023年6月6日
    098
  • 视频语义分割基准数据集与评估方法

    概述 本文来源于《A Benchmark Dataset and Evaluation Methodology for Video Object Segmentation》,论文主…

    数据库 2023年6月11日
    078
  • HackerRank第一趴–Basic Select

    ID number NAME VARCHAR2(17) COUNTRYCODE VARCHAR2(3) DISTRICT VARCHAR2(20) POPULATION numbe…

    数据库 2023年5月24日
    052
  • ODA→Teigha胎压–学习笔记

    数据库结构 记录对象表 对象字典 具有固定类型和固定可访问操作的特定对象的固定结构的固定集合 可以存储和操作各种类型的对象,包括它自己。 每个记录都有一个特定的类类型,每个表也有一…

    数据库 2023年6月14日
    067
  • 【黄啊码】小程序:九宫格抽奖如何实现?可控制抽奖率

    黄啊码向来简单粗暴,来,代码伺候 js代码如下: php;gutter:true; //index.js //获取应用实例 const app = getApp()</p&g…

    数据库 2023年6月16日
    0107
  • SQL的语法

    创建: create database [if not exists] 数据库名称 [default charset 字符集] [collate 排序规则]; (PS:方括号(&#…

    数据库 2023年6月16日
    081
  • 基础知识,不是很了解的

    Java中方法参数传递方式是按值传递。对于基本类型(int a, long b),参数传递时传递的是值,例如int a = 5,传递的就是5。如果是引用类型,传递是指向具体对象内存…

    数据库 2023年6月14日
    0100
  • MySQL临时表

    MySQL中临时表主要有两类,包括外部临时表和内部临时表。外部临时表是通过语句create temporary table…创建的临时表,临时表只在本会话有效,会话断开…

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