Spring Boot - IOC(五)

本篇我们解析第9、12、13步骤:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // ...
        try {
            // ...
            // Initialize other special beans in specific context subclasses.
            // 9. 子类的多态onRefresh
            onRefresh();
            // ...
            // Last step: publish corresponding event.
            // 12. 完成容器的创建工作
            finishRefresh();
        }
        catch (BeansException ex) {
            // ...
        }
        
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            // 13. 清除缓存
            resetCommonCaches();
        }
    }
}

12. finishRefresh:完成容器的创建工作

protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    // 清除资源缓存(如扫描的ASM元数据)
    clearResourceCaches();
    
    // Initialize lifecycle processor for this context.
    // 初始化生命周期处理器
    initLifecycleProcessor();
    
    // Propagate refresh to lifecycle processor first.
    // 将刷新传播到生命周期处理器
    getLifecycleProcessor().onRefresh();
    
    // Publish the final event.
    // 发布容器刷新完成的事件,让监听器去回调各自的方法
    publishEvent(new ContextRefreshedEvent(this));
    
    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}

这些方法可以看得出来都属于最终的步骤了,简单扫一眼:

12.1 clearResourceCaches:清除资源缓存

public void clearResourceCaches() {
    this.resourceCaches.clear();
}

非常简单,不再深入。

12.2 initLifecycleProcessor:初始化生命周期处理器

public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";

protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        this.lifecycleProcessor =
                beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
        }
    }
    else {
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        defaultProcessor.setBeanFactory(beanFactory);
        this.lifecycleProcessor = defaultProcessor;
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
                    "[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
        }
    }
}

可以发现源码中默认使用 DefaultLifecycleProcessor 作为生命周期处理器。它的文档注释原文翻译:

Default implementation of the LifecycleProcessor strategy.

LifecycleProcessor: Strategy interface for processing Lifecycle beans within the ApplicationContext.

用于在 ApplicationContext 中处理 Lifecycle 类型的Bean的策略接口。

从文档注释中又看到了一个新的概念:Lifecycle

12.2.1 LifeCycle

Lifecycle 是一个接口,它的文档注释原文翻译:

A common interface defining methods for start/stop lifecycle control. The typical use case for this is to control asynchronous processing. NOTE: This interface does not imply specific auto-startup semantics. Consider implementing SmartLifecycle for that purpose. Can be implemented by both components (typically a Spring bean defined in a Spring context) and containers (typically a Spring ApplicationContext itself). Containers will propagate start/stop signals to all components that apply within each container, e.g. for a stop/restart scenario at runtime. Can be used for direct invocations or for management operations via JMX. In the latter case, the org.springframework.jmx.export.MBeanExporter will typically be defined with an org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler, restricting the visibility of activity-controlled components to the Lifecycle interface. Note that the present Lifecycle interface is only supported on top-level singleton beans. On any other component, the Lifecycle interface will remain undetected and hence ignored. Also, note that the extended SmartLifecycle interface provides sophisticated integration with the application context's startup and shutdown phases.

定义启动/停止生命周期控制方法的通用接口。典型的用例是控制异步处理。注意:此接口并不意味着特定的自动启动语义。考虑为此目的实施 SmartLifecycle。

可以通过组件(通常是在Spring上下文中定义的 Spring bean)和容器(通常是Spring ApplicationContext 本身)实现。容器会将开始/停止信号传播到每个容器中应用的所有组件,例如在运行时停止/重新启动的情况。

可以用于直接调用或通过JMX进行管理操作。在后一种情况下,通常将使用 InterfaceBasedMBeanInfoAssembler 定义 MBeanExporter,从而将活动控制的组件的可见性限制为 Lifecycle 接口。

请注意,当前的 Lifecycle 接口仅在顶级 Singleton Bean 上受支持。在任何其他组件上,Lifecycle 接口将保持未被检测到并因此被忽略。另外,请注意,扩展的 SmartLifecycle 接口提供了与应用程序上下文的启动和关闭阶段的复杂集成。

到这里我们大概看懂了,实现了 Lifecycle 接口的Bean可以规范化它的生命周期,可以在IOC容器的启动、停止时,自动触发接口中定义的 start 方法和 stop 方法。

12.2.2 【扩展】SmartLifeCycle

Lifecycle 还有一个扩展的接口:SmartLifecycle ,它的文档注释关键部分:

An extension of the Lifecycle interface for those objects that require to be started upon ApplicationContext refresh and/or shutdown in a particular order. The isAutoStartup() return value indicates whether this object should be started at the time of a context refresh. The callback-accepting stop(Runnable) method is useful for objects that have an asynchronous shutdown process. Any implementation of this interface must invoke the callback's run() method upon shutdown completion to avoid unnecessary delays in the overall ApplicationContext shutdown.

Lifecycle 接口的扩展,用于那些需要按特定顺序刷新和/或关闭IOC容器时启动的对象。 isAutoStartup() 返回值指示是否应在刷新上下文时启动此对象。接受回调的 stop(Runnable) 方法对于具有异步关闭过程的对象很有用。此接口的任何实现都必须在关闭完成时调用回调的 run() 方法,以避免在整个IOC容器关闭中不必要的延迟。

从文档注释中可以看到一个很关键的信息:stop(Runnable) ,这就意味着可以在 stop 动作中再注入一些自定义逻辑。从它的方法定义中,可以看到它还扩展了几个方法:

  • getPhase - Bean的排序(类似于 @OrderOrdered 接口)

  • isAutoStartup - 如果该方法返回 false ,则不执行 start 方法。

这两个接口比较简单,不再深入研究,有兴趣的小伙伴可以写几个测试Demo体会一下。

12.3 getLifecycleProcessor().onRefresh()

紧接着调用这些 LifecycleProcessoronRefresh 方法。具体到 DefaultLifecycleProcessor 中:

public void onRefresh() {
    startBeans(true);
    this.running = true;
}

private void startBeans(boolean autoStartupOnly) {
    Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
    Map<Integer, LifecycleGroup> phases = new HashMap<>();
    lifecycleBeans.forEach((beanName, bean) -> {
        if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
            int phase = getPhase(bean);
            LifecycleGroup group = phases.get(phase);
            if (group == null) {
                group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
                phases.put(phase, group);
            }
            group.add(beanName, bean);
        }
    });
    if (!phases.isEmpty()) {
        List<Integer> keys = new ArrayList<>(phases.keySet());
        Collections.sort(keys);
        for (Integer key : keys) {
            phases.get(key).start();
        }
    }
}

源码也是比较好理解的,它会从IOC容器中找出所有的 Lifecycle 类型的Bean,遍历回调 start 方法。

12.4 publishEvent(new ContextRefreshedEvent(this))

很明显它发布了 ContextRefreshedEvent 事件,代表IOC容器已经刷新完成。有关事件与监听器的部分,我们在13篇中已经解释过了,不再赘述。

13. resetCommonCaches:清除缓存

protected void resetCommonCaches() {
    ReflectionUtils.clearCache();
    AnnotationUtils.clearCache();
    ResolvableType.clearCache();
    CachedIntrospectionResults.clearClassLoader(getClassLoader());
}

清除缓存也是够简单了,不再深追。


以上就是全部 AbstractApplicationContextrefresh 方法了。之前留了一个章节,说 SpringBootonRefresh 方法有一个扩展,下面咱来看看都扩展了个什么东西:

9. ServletWebServerApplicationContext.onRefresh

在第13篇中,我们说在 AbstractApplicationContext 中没有真正实现这个方法,而是留给了子类。SpringBoot 扩展的IOC容器中对这个方法进行了真正地实现:

protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

它要创建一个WebServer:

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        // 9.1 这一步创建了嵌入式Servlet容器的工厂
        ServletWebServerFactory factory = getWebServerFactory();
        // 9.2 创建嵌入式Servlet容器
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}

9.1 getWebServerFactory:获取嵌入式Servlet容器工厂Bean

protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    //获取IOC容器中类型为ServletWebServerFactory的Bean
    String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                                              + "ServletWebServerFactory bean.");
    }
    if (beanNames.length > 1) {
        throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                                              + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

因为一次创建只能运行在一个 Servlet容器中,说明一次只能取出一个Bean来。

默认的 Tomcat 创建工厂应该从这里取出:TomcatServletWebServerFactory,他实现了 ServletWebServerFactory 接口。

这个 TomcatServletWebServerFactory,应该是在自动配置时注册好的。

9.1.1 自动配置下的 TomcatServletWebServerFactory 注册时机

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
         ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
         ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
         ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration

这个类我们之前看过,它使用 @Import 导入了 ServletWebServerFactoryConfiguration 以及三个内部类:

@Configuration
class ServletWebServerFactoryConfiguration {

    @Configuration
    // 如果classpath下有Servlet的类,有Tomcat的类,有UpgradeProtocol的类,这个配置就生效
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }

    @Configuration
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedJetty {
        @Bean
        public JettyServletWebServerFactory JettyServletWebServerFactory() {
            return new JettyServletWebServerFactory();
        }
    }

    @Configuration
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedUndertow {
        @Bean
        public UndertowServletWebServerFactory undertowServletWebServerFactory() {
            return new UndertowServletWebServerFactory();
        }
    }
}

不难发现,TomcatServletWebServerFactory 在这里被创建。

9.2 getWebServer:创建嵌入式Servlet容器

// TomcatServletWebServerFactory
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

方法体中第一行:

Tomcat tomcat = new Tomcat();

发现 Tomcat 在此被创建了。

12. ServletWebServerApplicationContext.finishRefresh

ServletWebServerApplicationContext 还重写了 finishRefresh 方法:

protected void finishRefresh() {
    super.finishRefresh();
    WebServer webServer = startWebServer();
    if (webServer != null) {
        publishEvent(new ServletWebServerInitializedEvent(webServer, this));
    }
}

可以发现在此处启动嵌入式Web容器。

private WebServer startWebServer() {
    WebServer webServer = this.webServer;
    if (webServer != null) {
        webServer.start();
    }
    return webServer;
}

这里调用了 WebServer 的start方法真正启动嵌入式Web容器。

嵌入式Tomcat容器的更多原理解读和源码分析,在后面会有专门的篇章来读,此处不作过多解释,只希望小伙伴们知道在这个时机创建的嵌入式Tomcat即可。

回到原来SpringApplication.run的标号

// 4.11 刷新后的处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 4.12 发布started事件
listeners.started(context);
// 4.13 运行器回调
    callRunners(context, applicationArguments);

4.11 afterRefresh:刷新后的处理

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

空方法,且借助IDEA发现没有子类再实现,故不再深究。

4.12 listeners.started:发布started事件

public void started(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.started(context);
    }
}

源码很简单,根据前面的部分可得知直接来到 EventPublishingRunListener

public void started(ConfigurableApplicationContext context) {
    context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}

这部分会回到 AbstractApplicationContext 中:

public void publishEvent(ApplicationEvent event) {
    publishEvent(event, null);
}

之后继续往下调:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    // ......
    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);
        }
    }
}

上面的预处理部分咱们不关心,关键的看这两段if-else:

  • 第一段if-else是在当前IOC容器发布 ApplicationStartedEvent 事件

  • 下面的if结构会向父容器发布 ApplicationStartedEvent 事件

由此可见事件的发布还会影响到父容器

4.13 callRunners:运行器回调

//从容器中获取了ApplicationRunner和CommandLineRunner
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    //ApplicationRunner先回调,CommandLineRunner后回调
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args);
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args.getSourceArgs());
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
    }
}

这部分涉及到两个概念: CommandLineRunnerApplicationRunner

4.13.1 CommandLineRunner

文档注释原文翻译:

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple CommandLineRunner beans can be defined within the same application context and can be ordered using the Ordered interface or @Order annotation. If you need access to ApplicationArguments instead of the raw String array consider using ApplicationRunner.

用于指示bean被包含在 SpringApplication 中时应该运行的接口。可以在同一应用程序上下文中定义多个 CommandLineRunner Bean,并且可以使用 Ordered 接口或 @Order 注解对其进行排序。

如果需要访问 ApplicationArguments 而不是原始String数组,请考虑使用 ApplicationRunner

4.13.2 ApplicationRunner

文档注释原文翻译:

Interface used to indicate that a bean should run when it is contained within a SpringApplication. Multiple ApplicationRunner beans can be defined within the same application context and can be ordered using the Ordered interface or @Order annotation.

用于指示bean被包含在 SpringApplication 中时应该运行的接口。可以在同一应用程序上下文中定义多个 ApplicationRunner Bean,并可以使用 Ordered 接口或 @Order 注解对其进行排序。

文档注释都没有明确的对这两个组件有很好的解释。翻看 SpringBoot 的官方文档:

docs.spring.io/spring-boot…

官方文档甚至没有说明这两个接口到底能干什么,只告诉我们怎么用。那这两个组件到底是干什么的呢?

4.13.3 【扩展】SpringBoot1.x中对这两个组件的应用

其实这两个接口组件,如果翻看它的文档注释中的since,会发现一个没有标注,一个是 SpringBoot1.3.0,说明它们都来自于 SpringBoot1.x 。它们本来是用于监听特定的时机来执行一些操作,奈何 SpringBoot2.x 后扩展了事件,可以通过监听 ApplicationStartedEvent 来实现跟这两个组件一样的效果。换句话说,这两个组件已经被隐式的“淘汰”了,不必过多深究。

至此,SpringBoot应用启动成功。

最后更新于