关于前端:web监听器解析

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

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

增加监听器

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

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

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
    // 注册监听器的全限定名数组
    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];
            // 实例化监听器
            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<>();
    // 分组 辨别事件监听器和生命周期监听器
    // ServletContextListener为生命周期监听器
    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.
    // 设置利用的事件监听器和生命周期监听器
    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();
    // 不容许设置新的监听
    context.setNewServletContextListenerAllowed(false);

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

    // 依据servlet上下文构建ServletContextEvent事件
    ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
        noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
        tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    // 调用ServletContextListener监听器
    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 {
                // 容器上下文初始化 调用监听器contextInitialized办法初始化
                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;

}

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属性为空,不为空阐明可能有反复 报错
    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.
        // 创立contextClass指定的web上下文
        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.
                    // 如果有父容器的话进行设置
                    ApplicationContext parent = loadParentContext(servletContext);
                    cwac.setParent(parent);
                }
                // 配置&刷新容器
                configureAndRefreshWebApplicationContext(cwac, servletContext);
            }
        }
        // 将容器上下文设置到servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中
        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) {
    // 默认雷同 从新设置容器上下文标识
    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
        // 从servlet上下文获取contextId
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam);
        }
        else {
            // Generate default id... 设置上下文id
            // WebApplicationContext:+contextPath
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                    ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    // 设置servlet上下文
    wac.setServletContext(sc);
    // 获取配置文件门路contextConfigLocation
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    // 设置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
    // 初始化环境配置
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        // 初始化servlet属性资源
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
    }

    // 自定义初始化
    customizeContext(sc, wac);

    // 刷新web容器 初始化bean
    wac.refresh();
}

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据