理解Spring Application

我们在创建一个Spring boot应用的时候,通常都是使用SpringApplication或者SpringApplicationBuilder API,通过执行run方法进行启动Spring Boot应用;我们可以将整体的生命周期总结为以下几个阶段:

SpringApplication的初始化阶段

SpringApplication的运行阶段

SpringApplication的结束阶段

Spring Boot应用的退出

大致结构如下:

理解Spring Application

以上几个阶段又有细化的步骤,我们一起来分析下源代码进行查看:

1 初始化阶段

1.1 SpringApplication构造阶段

我们启动项目一般都是通过SpringApplicationBuilder进行初始化SpringApplication或者使用SpringApplication调用静态方法进行初始化启动项目,代码如下所示:

public class SpringBootApplicationBootstrap {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MultipleSpringBootEventListener.class)
                .web(WebApplicationType.NONE)
                .run(args);
    }
}
public class SpringBootApplicationBootstrap {

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

}

两种方式最终都是在SpringApplication的构造方法中进行初始化操作,源码如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

我们所关心的就是最后四行代码了;

 梳理整体的流程为:

理解Spring Application

1.1.1 判断应用类型

this.webApplicationType = WebApplicationType.deduceFromClasspath();

这个方法就是判断当前运行的应用是REACTIVE类型的还是SERVLET类型,或者是非Web应用;

1.1.2 加载ApplicationContextInitializer应用上下文初始化器

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

通过跟踪源码,我们可以看到获取ApplicationContextInitializer实现类的方式,是使用的Spring的SPI机制,从spring.factories文件中获取配置的实现类名称列表,再通过调用SpringBoot新增的一个方法BeanUtils.instantiateClass(constructor, args)去创建实例,再进行排序操作(每一个实现类都实现了Ordered接口),最后将这些实现类实例保存在SpringApplication的initializers属性中;

ApplicationContextInitializer的作用,如下是官网的解释:

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>

Callback interface for initializing a Spring ConfigurableApplicationContext prior to being refreshed.

Typically used within web applications that require some programmatic initialization of the application context. For example, registering property sources or activating profiles against the context's environment. See ContextLoader and FrameworkServlet support for declaring a "contextInitializerClasses" context-param and init-param, respectively.

我在网上查找到一个比较全面的解释,请参考:

ApplicationContextInitializer的实现与使用

1.1.3 加载ApplicationListener应用事件监听器

执行的流程和加载ApplicationContextInitializer方式流程一致;

1.1.4 获取主类(应用引导类)

将获取到的主类信息保存到mainApplicationClass属性中;

1.2 SpringApplication配置阶段

这个阶段介于1.1SpringApplication与2 SpringApplication运行阶段之间,其实就是在我们运行run方法之前,通过SpringApplication提供的公共方法,修改相关的配置信息,来达到我们期望的运行效果,例如:

public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplicationBuilder(MultipleSpringBootEventListener.class)
                .web(WebApplicationType.NONE)
                .bannerMode(Banner.Mode.OFF)
                .application();
        springApplication.getInitializers();
        springApplication.addListeners(new MultipleSpringBootEventListener());
        springApplication.setSources(new HashSet<>());
        springApplication.run(args);
    }

可以获取应用上下文初始化器,也可以添加自定义的应用上下文初始化器,添加事件监听器,添加配置源等信息;

上一篇:使用Druid作为数据源


下一篇:谷粒商城分布式基础(二)——分布式组件SpringCloud & SpringCloud Alibaba