返回介绍

10.1 Spring Boot 主程序的功能

发布于 2025-04-26 13:26:36 字数 5631 浏览 0 评论 0 收藏

代码清单 10-1 是 Spring Boot 应用的一个典型主程序,它看起来非常简单,使用了一个同样看起来非常简单的注解 @SpringBootApplication,并用一个非常普通的 main 方法运行 SpringApplication 的 run 方法。这个简单的主程序将会加载一个应用所需的所有资源和配置,最后启动一个应用实例。

代码清单 10-1 Spring Boot 应用主程序

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

10.1.1 SpringApplication 的 run 方法

Spring Boot 的主程序有什么神奇的地方呢?可以从 SpringApplication 的 run 方法说起,这是应用主程序开始运行的方法,它的源代码如代码清单 10-2 所示。这个方法让我们能够看清楚 Spring Boot 的一切秘密所在,它首先开启一个 SpringApplicationRun-Listeners 监听器,然后创建一个应用上下文 ConfigurableApplicationContext,通过这个上下文加载应用所需的类和各种环境配置等,最后启动一个应用实例。

代码清单 10-2 SpringApplication 中 run 的源代码

package org.springframework.boot;
......
public class SpringApplication {
    ......
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.started();
        try {
            DefaultApplicationArguments ex = newDefaultApplicationArguments(args);
            context = this.createAndRefreshContext(listeners, ex);
            this.afterRefresh(context, (ApplicationArguments)ex);
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();
            if(this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted
(this.getApplicationLog(), stopWatch);
            }
            return context;
        } catch (Throwable var6) {
            this.handleRunFailure(context, listeners, var6);
            throw new IllegalStateException(var6);
        }
    }
    ......
}

如果你使用过 Spring 框架,就会更加清楚这种加载应用的实现机制。在 Spring 中,加载一个应用,主要是通过一些复杂的配置来实现的。这样看来,Spring Boot 只不过是把这些本来由程序员做的工作,事先帮我们实现罢了。

10.1.2 创建应用上下文

一个应用能够正常运行起来,需要一些环境变量、各种资源和一些相关配置等,从创建应用上下文 ConfigurableApplicationContext 的源代码中,我们可以看到这种实现机制,如代码清单 10-3 所示。其中,this.load(context,sources.toArray(new Object[sources.size()]))将调用 BeanDefinitionLoader 来加载应用定义的和需要的类及各种资源。

代码清单 10-3 创建应用上下文——createAndRefreshContext 的源代码

package org.springframework.boot;
......
public class SpringApplication {
    ......
    private ConfigurableApplicationContext createAndRefreshContext(SpringApp
licationRunListeners listeners, ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment(environment, applicationArguments.getSource
Args());
        listeners.environmentPrepared(environment);
        if(this.isWebEnvironment(environment) && !this.webEnvironment) {
            environment = this.convertToStandardEnvironment(environment);
        }
        if(this.bannerMode != Mode.OFF) {
            this.printBanner(environment);
        }
        ConfigurableApplicationContext context = this.createApplicationContext();
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if(this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
        context.getBeanFactory().registerSingleton("springApplicationArguments",
 applicationArguments);
        Set sources = this.getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
        this.refresh(context);
        if(this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var7) {
                ;
            }
        }
        return context;
    }
   ......
}

10.1.3 自动加载

在 BeanDefinitionLoader 中,有一个 load(Class<?>source)方法用来加载类定义,如代码清单 10-4 所示。这里的 source 就是代码清单 10-1 中定义的 Application.class。在程序中通过 isComponent 检查是否存在注解,如果有注解,则调用注解相关的类定义。这样注解 @SpringBootApplication 将被调用,它不但会导入一系列自动配置的类,还会加载应用中一些自定义的类。

代码清单 10-4 BeanDefinitionLoader 中 load(Class<?>source)源代码

package org.springframework.boot;
......
class BeanDefinitionLoader {
    ......
    private int load(Class<?> source) {
        if(this.isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinition
Source.class.isAssignableFrom(source)) {
            BeanDefinitionLoader.GroovyBeanDefinitionSource loader = (Bean
DefinitionLoader.GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class);
            this.load(loader);
        }
        if(this.isComponent(source)) {
            this.annotatedReader.register(new Class[]{source});
            return 1;
        } else {
            return 0;
        }
    }
   ......
   private boolean isComponent(Class<?> type) {
        return AnnotationUtils.findAnnotation(type, Component.class) != null?
true:!type.getName().matches(".*\\$_.*closure.*") && !type.isAnonymousClass() && type.getConstructors() != null && type.getConstructors().length != 0;
    }
   ......
}

从以上分析可知,一个简单的 Spring Boot 主程序,通过运行一个 run 方法,就将引发一系列复杂的内部调用和加载过程,从而初始化一个应用所需的配置、环境、资源及各种类定义等。特别是导入了一系列自动配置类,实现了强大的自动配置功能,这是 Spring Boot 框架最引人注目的地方。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。