spring-ApplicationContext启动流程和事件机制

喵.jpg

当事猫罪行描述: 事情的经过是这样的,今天一如既往早早来到办公室,开始快乐的一天,本想着温故而知新一下,翻翻Servlet规范,看看之前的笔记, 忽然发现ServletContextListener , 想起我大哥的书中提到的一点 springBoot 的历史性意义,其中一点就包括,嵌入式(Embed)容器.

  • 在springBoot之前 ,web 应用依托web容器, 依赖servlet的监听事件(Event,EventListener) 即ServletContextListener,完成web应用的初始化.
  • 在springBoot时, web应用的启动流程就不再依托servlet容器,而是自发创建、选择 web容器

当然 , 我们今天的重点是SpringBoot之前,web应用是怎么启动的.

前言

​ 前面提到过在SpringBoot之前, web应用的启动是基于servlet的回调,而其中涉及tomcat的生命周期和事件,所以有以下议题:

  • JDK事件机制
  • Tomcat事件机制
  • Spring-web应用启动流程
  • Spring事件机制

JDK中的事件机制

JDK中对事件机制的各个角色提供了完善的抽象,主要包括3个角色:

  • EventObject(事件):事件发布时需要关注的内容。jdk中提供了EventObject接口。
  • EventListener(事件监听者):事件监听对象,也就是对EventObject感兴趣的对象。jdk中提供了EventListener接口。
  • EventSource(事件源,我比较喜欢称为EventPublisher 事件发布者):发布事件的对象,可以在该对象中组册EventListener,然后在特定的条件下发布EventObject给已经注册的EventListener。

Tomcat的事件机制

事件 (LifecycleEvent)

Tomcat容器,定义了很多生命周期相关的事件. org.apache.catalina.LifecycleEvent

主要的事件在org.apache.catalina.Lifecycle

public final class LifecycleEvent extends EventObject {

    //......

    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {

        super(lifecycle);
        this.type = type;
        this.data = data;
    }

    //......
}

public interface Lifecycle {
    /**
     * The LifecycleEvent type for the "component after init" event.
     */
    public static final String BEFORE_INIT_EVENT = "before_init";

    /**
     * The LifecycleEvent type for the "component after init" event.
     */
    public static final String AFTER_INIT_EVENT = "after_init";

    /**
     * The LifecycleEvent type for the "component start" event.
     */
    public static final String START_EVENT = "start";

    //......
}

事件监听者(LifecycleListener)

监听者使用LifecycleEvent参数用来在tomcat的各个阶段处理进行相应处理。org.apache.catalina.LifecycleListener 有众多实现,其中org.apache.catalina.startup.ContextConfig 这个类在解析server.xml的时候用来监听StandardContext的各个阶段的事件,并做出相应处理

public interface LifecycleListener {

    public void lifecycleEvent(LifecycleEvent event);
}

public class ContextConfig implements LifecycleListener {
    
    //......
    @Override
    public void lifecycleEvent(LifecycleEvent event) {

        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            configureStart();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
            // Restore docBase for management tools
            if (originalDocBase != null) {
                context.setDocBase(originalDocBase);
            }
        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
            configureStop();
        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init();
        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
            destroy();
        }
    }

    //......
}

事件发布者(LifecycleBase)

LifecycleBase 把事件管理委派给了LifecycleSupport,它是监听对象的一个管理类,它把对监听对象的添加移除以及发布事件几个操作进行了统一管理,避免EventSource类中出现太多管理监听对象的逻辑。

public abstract class LifecycleBase implements Lifecycle{

    //......
    private LifecycleSupport lifecycle = new LifecycleSupport(this);

    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        lifecycle.removeLifecycleListener(listener);
    }

    protected void fireLifecycleEvent(String type, Object data) {
        lifecycle.fireLifecycleEvent(type, data);
    }
    //......
}

public final class LifecycleSupport {

    //......

    //监听对象集合
    private LifecycleListener listeners[] = new LifecycleListener[0];
    
    private final Object listenersLock = new Object(); // Lock object for changes to listeners

    //添加监听对象
    public void addLifecycleListener(LifecycleListener listener) {

      synchronized (listenersLock) {
          LifecycleListener results[] =
            new LifecycleListener[listeners.length + 1];
          for (int i = 0; i < listeners.length; i++)
              results[i] = listeners[i];
          results[listeners.length] = listener;
          listeners = results;
      }

    }

    //发布监听对象
    public void fireLifecycleEvent(String type, Object data) {

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);

    }

    //移除监听对象
    public void removeLifecycleListener(LifecycleListener listener) {

        synchronized (listenersLock) {
            int n = -1;
            for (int i = 0; i < listeners.length; i++) {
                if (listeners[i] == listener) {
                    n = i;
                    break;
                }
            }
            if (n < 0)
                return;
            LifecycleListener results[] =
              new LifecycleListener[listeners.length - 1];
            int j = 0;
            for (int i = 0; i < listeners.length; i++) {
                if (i != n)
                    results[j++] = listeners[i];
            }
            listeners = results;
        }
    }
}

Spring web应用启动流程

ContextLoaderListener

在一个spring 的web项目中, 通常需要在web.xml配置 ContextLoaderListener

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

首先看下这个类结构

image-20190703093735464

可以看到继承了ContextLoader ,用来创建和初始化WebApplicationContext

同时也实现了EventListener,那么它监听的是什么事件呢,又是哪里发布的?

/**
 * Initialize the root web application context.
 */
public void contextInitialized(ServletContextEvent event) {
   this.contextLoader = createContextLoader();
   if (this.contextLoader == null) {
      this.contextLoader = this;
   }
   this.contextLoader.initWebApplicationContext(event.getServletContext());
}

可以看到是一个ServletContextEvent ,因为这里的Listener是配置在web.xml,所以它是tomcat容器的一个listener,那么事件的发布就是 LifecycleBase

ServletContextEvent

通过查看LifecycleBase的实现 ,最终发现Tomcat 具体的操作在org.apache.catalina.core.StandardContext

startInternal方法

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    if(log.isDebugEnabled())
        log.debug("Starting " + getBaseName());

    // Send j2ee.state.starting notification
    if (this.getObjectName() != null) {
        Notification notification = new Notification("j2ee.state.starting",
                this.getObjectName(), sequenceNumber.getAndIncrement());
        broadcaster.sendNotification(notification);
    }

    setConfigured(false);
    boolean ok = true;

    // Currently this is effectively a NO-OP but needs to be called to
    // ensure the NamingResources follows the correct lifecycle
    if (namingResources != null) {
        namingResources.start();
    }

    // Add missing components as necessary
    if (webappResources == null) {   // (1) Required by Loader
        if (log.isDebugEnabled())
            log.debug("Configuring default Resources");
        try {
            String docBase = getDocBase();
            if (docBase == null) {
                setResources(new EmptyDirContext());
            } else if (docBase.endsWith(".war")
                    && !(new File(getBasePath())).isDirectory()) {
                setResources(new WARDirContext());
            } else {
                setResources(new FileDirContext());
            }
        } catch (IllegalArgumentException e) {
            log.error(sm.getString("standardContext.resourcesInit"), e);
            ok = false;
        }
    }
    if (ok) {
        if (!resourcesStart()) {
            throw new LifecycleException("Error in resourceStart()");
        }
    }

    if (getLoader() == null) {
        WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
        webappLoader.setDelegate(getDelegate());
        setLoader(webappLoader);
    }

    // Initialize character set mapper
    getCharsetMapper();

    // Post work directory
    postWorkDirectory();

    // Validate required extensions
    boolean dependencyCheck = true;
    try {
        dependencyCheck = ExtensionValidator.validateApplication
            (getResources(), this);
    } catch (IOException ioe) {
        log.error(sm.getString("standardContext.extensionValidationError"), ioe);
        dependencyCheck = false;
    }

    if (!dependencyCheck) {
        // do not make application available if dependency check fails
        ok = false;
    }

    // Reading the "catalina.useNaming" environment variable
    String useNamingProperty = System.getProperty("catalina.useNaming");
    if ((useNamingProperty != null)
        && (useNamingProperty.equals("false"))) {
        useNaming = false;
    }

    if (ok && isUseNaming()) {
        if (getNamingContextListener() == null) {
            NamingContextListener ncl = new NamingContextListener();
            ncl.setName(getNamingContextName());
            ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
            addLifecycleListener(ncl);
            setNamingContextListener(ncl);
        }
    }

    // Standard container startup
    if (log.isDebugEnabled())
        log.debug("Processing standard container startup");


    // Binding thread
    ClassLoader oldCCL = bindThread();

    try {
        if (ok) {
            // Start our subordinate components, if any
            Loader loader = getLoaderInternal();
            if ((loader != null) && (loader instanceof Lifecycle))
                ((Lifecycle) loader).start();

            // since the loader just started, the webapp classloader is now
            // created.
            // By calling unbindThread and bindThread in a row, we setup the
            // current Thread CCL to be the webapp classloader
            unbindThread(oldCCL);
            oldCCL = bindThread();

            // Initialize logger again. Other components might have used it
            // too early, so it should be reset.
            logger = null;
            getLogger();

            Cluster cluster = getClusterInternal();
            if ((cluster != null) && (cluster instanceof Lifecycle))
                ((Lifecycle) cluster).start();
            Realm realm = getRealmInternal();
            if ((realm != null) && (realm instanceof Lifecycle))
                ((Lifecycle) realm).start();
            DirContext resources = getResourcesInternal();
            if ((resources != null) && (resources instanceof Lifecycle))
                ((Lifecycle) resources).start();

            // Notify our interested LifecycleListeners
            fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

            // Start our child containers, if not already started
            for (Container child : findChildren()) {
                if (!child.getState().isAvailable()) {
                    child.start();
                }
            }

            // Start the Valves in our pipeline (including the basic),
            // if any
            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).start();
            }

            // Acquire clustered manager
            Manager contextManager = null;
            Manager manager = getManagerInternal();
            if (manager == null) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("standardContext.cluster.noManager",
                            Boolean.valueOf((getCluster() != null)),
                            Boolean.valueOf(distributable)));
                }
                if ( (getCluster() != null) && distributable) {
                    try {
                        contextManager = getCluster().createManager(getName());
                    } catch (Exception ex) {
                        log.error("standardContext.clusterFail", ex);
                        ok = false;
                    }
                } else {
                    contextManager = new StandardManager();
                }
                manager = contextManager;
            }

            // Configure default manager if none was specified
            if (contextManager != null) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("standardContext.manager",
                            contextManager.getClass().getName()));
                }
                setManager(contextManager);
            }

            if (manager!=null && (getCluster() != null) && distributable) {
                //let the cluster know that there is a context that is distributable
                //and that it has its own manager
                getCluster().registerManager(manager);
            }
        }

    } finally {
        // Unbinding thread
        unbindThread(oldCCL);
    }

    if (!getConfigured()) {
        log.error(sm.getString("standardContext.configurationFail"));
        ok = false;
    }

    // We put the resources into the servlet context
    if (ok)
        getServletContext().setAttribute
            (Globals.RESOURCES_ATTR, getResources());

    // Initialize associated mapper
    mapper.setContext(getPath(), welcomeFiles, getResources());

    // Binding thread
    oldCCL = bindThread();

    if (ok ) {
        if (getInstanceManager() == null) {
            javax.naming.Context context = null;
            if (isUseNaming() && getNamingContextListener() != null) {
                context = getNamingContextListener().getEnvContext();
            }
            Map<String, Map<String, String>> injectionMap = buildInjectionMap(
                    getIgnoreAnnotations() ? new NamingResources(): getNamingResources());
            setInstanceManager(new DefaultInstanceManager(context,
                    injectionMap, this, this.getClass().getClassLoader()));
            getServletContext().setAttribute(
                    InstanceManager.class.getName(), getInstanceManager());
        }
    }

    try {
        // Create context attributes that will be required
        if (ok) {
            getServletContext().setAttribute(
                    JarScanner.class.getName(), getJarScanner());
        }

        // Set up the context init params
        mergeParameters();

        // Call ServletContainerInitializers
        for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
            initializers.entrySet()) {
            try {
                entry.getKey().onStartup(entry.getValue(),
                        getServletContext());
            } catch (ServletException e) {
                log.error(sm.getString("standardContext.sciFail"), e);
                ok = false;
                break;
            }
        }

        // Configure and call application event listeners
        if (ok) {
            if (!listenerStart()) {
                log.error(sm.getString("standardContext.listenerFail"));
                ok = false;
            }
        }

        try {
            // Start manager
            Manager manager = getManagerInternal();
            if ((manager != null) && (manager instanceof Lifecycle)) {
                ((Lifecycle) getManager()).start();
            }
        } catch(Exception e) {
            log.error(sm.getString("standardContext.managerFail"), e);
            ok = false;
        }

        // Configure and call application filters
        if (ok) {
            if (!filterStart()) {
                log.error(sm.getString("standardContext.filterFail"));
                ok = false;
            }
        }

        // Load and initialize all "load on startup" servlets
        if (ok) {
            if (!loadOnStartup(findChildren())){
                log.error(sm.getString("standardContext.servletFail"));
                ok = false;
            }
        }

        // Start ContainerBackgroundProcessor thread
        super.threadStart();
    } finally {
        // Unbinding thread
        unbindThread(oldCCL);
    }

    // Set available status depending upon startup success
    if (ok) {
        if (log.isDebugEnabled())
            log.debug("Starting completed");
    } else {
        log.error(sm.getString("standardContext.startFailed", getName()));
    }

    startTime=System.currentTimeMillis();

    // Send j2ee.state.running notification
    if (ok && (this.getObjectName() != null)) {
        Notification notification =
            new Notification("j2ee.state.running", this.getObjectName(),
                             sequenceNumber.getAndIncrement());
        broadcaster.sendNotification(notification);
    }

    // Close all JARs right away to avoid always opening a peak number
    // of files on startup
    if (getLoader() instanceof WebappLoader) {
        ((WebappLoader) getLoader()).closeJARs(true);
    }

    // Reinitializing if something went wrong
    if (!ok) {
        setState(LifecycleState.FAILED);
    } else {
        setState(LifecycleState.STARTING);
    }
}

listenerStart方法

注意listenerStart ,Configure and call application event listeners 通知所有listeners

/**
 * Configure the set of instantiated application event listeners
 * for this Context.  Return <code>true</code> if all listeners wre
 * initialized successfully, or <code>false</code> otherwise.
 */
public boolean listenerStart() {
    if (log.isDebugEnabled())
        log.debug("Configuring application event listeners");

    // Instantiate the required listeners
    ApplicationListener listeners[] = applicationListeners;
    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 {
            ApplicationListener listener = listeners[i];
            results[i] = getInstanceManager().newInstance(
                    listener.getClassName());
            if (listener.isPluggabilityBlocked()) {
                noPluggabilityListeners.add(results[i]);
            }
        } catch (Throwable t) {
            t = ExceptionUtils.unwrapInvocationTargetException(t);
            ExceptionUtils.handleThrowable(t);
            getLogger().error
                (sm.getString("standardContext.applicationListener",
                              listeners[i].getClassName()), t);
            ok = false;
        }
    }
    if (!ok) {
        getLogger().error(sm.getString("standardContext.applicationSkipped"));
        return (false);
    }

    // Sort listeners in two arrays
    ArrayList<Object> eventListeners = new ArrayList<Object>();
    ArrayList<Object> lifecycleListeners = new ArrayList<Object>();
    for (int i = 0; i < results.length; i++) {
        if ((results[i] instanceof ServletContextAttributeListener)
            || (results[i] instanceof ServletRequestAttributeListener)
            || (results[i] instanceof ServletRequestListener)
            || (results[i] instanceof HttpSessionAttributeListener)) {
            eventListeners.add(results[i]);
        }
        if ((results[i] instanceof ServletContextListener)
            || (results[i] instanceof HttpSessionListener)) {
            lifecycleListeners.add(results[i]);
        }
    }

    // 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.
    for (Object eventListener: getApplicationEventListeners()) {
        eventListeners.add(eventListener);
    }
    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;
    }

    ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
        noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
        tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    for (int i = 0; i < instances.length; i++) {
        if (instances[i] == null)
            continue;
        if (!(instances[i] instanceof ServletContextListener))
            continue;
        ServletContextListener listener =
            (ServletContextListener) instances[i];
        try {
            fireContainerEvent("beforeContextInitialized", listener);
            if (noPluggabilityListeners.contains(listener)) {
                listener.contextInitialized(tldEvent);
            } else {
                listener.contextInitialized(event);
            }
            fireContainerEvent("afterContextInitialized", listener);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            fireContainerEvent("afterContextInitialized", listener);
            getLogger().error
                (sm.getString("standardContext.listenerStart",
                              instances[i].getClass().getName()), t);
            ok = false;
        }
    }
    return (ok);
}

简单总结下,tomcat容器启动后,发出通知ServletContextEvent, ContextLoaderListener接收到后,初始化web应用

Spring的事件

ApplicationEvent

spring里面的主要实现是ApplicationEvent, 这里我们关注它的容器的事件, ApplicationContextEvent,是spring容器在启动时触发的事件对象:

image-20190703195300527

ApplicationListener

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

   /**
    * Handle an application event.
    * @param event the event to respond to
    */
   void onApplicationEvent(E event);

}

那么在spring框架中是怎么发布这些事件的呢?

ApplicationContext

ApplicationEventPublisher

查看ApplicationContext 发现继承了ApplicationEventPublisher

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
        }


@FunctionalInterface
public interface ApplicationEventPublisher {

    /**
     * Notify all <strong>matching</strong> listeners registered with this
     * application of an application event. Events may be framework events
     * (such as RequestHandledEvent) or application-specific events.
     * @param event the event to publish
     * @see org.springframework.web.context.support.RequestHandledEvent
     */
    default void publishEvent(ApplicationEvent event) {
                publishEvent((Object) event);
    }

    /**
     * Notify all <strong>matching</strong> listeners registered with this
     * application of an event.
     * <p>If the specified {@code event} is not an {@link ApplicationEvent},
     * it is wrapped in a {@link PayloadApplicationEvent}.
     * @param event the event to publish
     * @since 4.2
     * @see PayloadApplicationEvent
     */
    void publishEvent(Object event);
}

AbstractApplicationContext

然后publishEvent的实现在AbstractApplicationContext

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");

        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        }
        else {
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
            }
        }

        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

        // Publish event via parent context as well...
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }

ApplicationEventMulticaster

其中调用了委派给了ApplicationEventMulticaster,具体实现在 SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
}

至此,事件机制完事啦. (啊!信息量有点大)

其他

ClassLoader

信息量有点大,就再附赠点东西吧…. 在ContextLoader#initWebApplicationContext 方法中有关于getContextClassLoader

public class ContextLoader {
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        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.
            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.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);
            }

                   //....
    }
}

其中, ClassLoader ccl = Thread.currentThread().getContextClassLoader();

层次结构是WebAppClassLoader ->URLClassLoader。->AppClassLoader->ExtClassLoader->null

注意⚠️ : 其中URLClassLoader 对应的是Tomcat ClassLoader中的common (可以看之前的一篇文章哈,servlet-tomcat)

img

More details see:

Tomcat Class Loader

Java Class Loader


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com

文章标题:spring-ApplicationContext启动流程和事件机制

字数:3.5k

本文作者:zhengyumin

发布时间:2019-07-03, 09:37:08

最后更新:2020-01-25, 16:47:31

原始链接:http://zyumin.github.io/2019/07/03/spring-ApplicationContext%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E5%92%8C%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。