boot启动(一)

2019-12-16  本文已影响0人  牧羊人刘俏

sb真正的做到了按照程序猿的思维来启动服务,但是封装的越好,底层的原理更加的难以掌握,
先起个引子,将boot的启动流程搞清楚,boot如何在spring的基础上做到一键启动。
通过main方法启动的时候,可以加入任意自己的代码,如下

public static void main(String[] args) {
MetaCache.init(CURIE_NAMESPACE);
SpringApplication.run(ImportExportApplication.class, args);

}

其实这也算是一个扩展点,在run之间,我直接将项目里面所有的entity,通过第三方包进行扫描,将meta信息进行了缓存,方便后面的直接的使用。

/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified source using default settings.
 * @param primarySource the primary source to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class<?> primarySource,
        String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

如上,通过注释发现是个静态的static helper方法,primarySource 其实就是个简单的Class对象,
等于说我们可以加载任何的一个Class对象作为primary source去启动

一般来说在作为primary source上面我们会通过注解来加载服务需要的信息,如下

@SpringBootApplication()
@EnableAsync
@ComponentScan(basePackages=PackageInfo.APP_BASE_PACKAGE)
@EntityScan(PackageInfo.APP_BASE_PACKAGE)
@EnableDiscoveryClient
@EnableFeignClients
@EnableMetrics
@EnableRateLimit
@EnableEncryptableProperties
@EnableSleuth
@EnableMonitor
public class ImportExportApplication

好接着往下看

/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified sources using default settings and user supplied arguments.
 * @param primarySources the primary sources to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
        String[] args) {
    return new SpringApplication(primarySources).run(args);
}

我们通过primarySources构造了一个SpringApplication,通过名字我们可以看到,在primarySources中包含了SpringApplication需要的信息。
我们还是先看SpringApplication这个类的介绍

/**
SpringApplication是用来引导和启动一个通过main方法的spring的应用

以下忽略,因为spring加载bean的方式很多,推荐使用@Configuration的方式来加载bean,相当于以前的xml文件config化,这样加载出来的bean可以保证都是单例的

接着往下看

/**
* Create a new {@link SpringApplication} instance. The application context will load
等于说创建的SpringApplication加载的bean都与primarySources息息相关
* beans from the specified primary sources (see {@link SpringApplication class-level}
在run之前,可以做一些定制化的功能
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

先来看下,boot是如何的判断我们是哪种webApplicationType的,

static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

如上很简单,通过判断当前的classpath下是否有相关的class,如如果classparth下有DispatcherServlet这个类,那基本就是个servlet服务了。
ok,下面是最关键的了

setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

其实这两句代码,一句是拿到所有的ApplicationContextInitializer.class对象,一个是拿到所有的ApplicationListener.class对象,由于现在spring还没有启动,更不用说通过context的getBean去拿取了,那肯定在某个地方记录了这些ApplicationContextInitializer.class和ApplicationListener.class的信息,无非两种方法,1 hardcode在代码里面,直接的去拿。(感觉不怎么好,毕竟我加一个就要修改源码吗)2 放在一个配置文件里面,大家去配置文件里面拿,貌似这个方法可以。

上面的重要的就是 getSpringFactoriesInstances(ApplicationContextInitializer.class)这个方法。
第二篇,在讲下如何的 getSpringFactoriesInstances(ApplicationContextInitializer.class)。

上一篇下一篇

猜你喜欢

热点阅读