美文网首页
boot启动(一)

boot启动(一)

作者: 牧羊人刘俏 | 来源:发表于2019-12-16 16:26 被阅读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的应用

  • Class that can be used to bootstrap and launch a Spring application from a Java main
    默认情况下,SpringApplication会通过以下的步骤来引导你的服务
  • method. By default class will perform the following steps to bootstrap your
  • application:
  • <ul>
    1 创建一个合适的 ApplicationContext 实例,当然通过你的项目信息(如何的构建这个很关键)
  • <li>Create an appropriate {@link ApplicationContext} instance (depending on your
  • classpath)</li>
    2 注册一个 CommandLinePropertySource ,暴露出command line arguments 这样你可以通过
    command line的方式来注册相关的properties信息
  • <li>Register a {@link CommandLinePropertySource} to expose command line arguments as
  • Spring properties</li>
    3 然后将第一步构建的ApplicationContext refresh下(这个就是springioc的主要内容了),将项目中注册的单例bean全部的加载到第一步创建的 ApplicationContext 中
  • <li>Refresh the application context, loading all singleton beans</li>
    4 Trigger所有的CommandLineRunner bean了,这也是个hook
  • <li>Trigger any {@link CommandLineRunner} beans</li>
  • </ul>
  • In most circumstances the static {@link #run(Class, String[])} method can be called
  • directly from your {@literal main} method to bootstrap your application:
  • <pre class="code">
  • @Configuration
  • @EnableAutoConfiguration
  • public class MyApplication {
  • // ... Bean definitions
  • public static void main(String[] args) throws Exception {
  • SpringApplication.run(MyApplication.class, args);
    
  • }
  • }
  • </pre>
  • <p>
  • For more advanced configuration a {@link SpringApplication} instance can be created and
  • customized before being run:
  • <pre class="code">
  • public static void main(String[] args) throws Exception {
  • SpringApplication application = new SpringApplication(MyApplication.class);
  • // ... customize application settings here
  • application.run(args)
  • }
  • </pre>

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

  • {@link SpringApplication}s can read beans from a variety of different sources. It is
  • generally recommended that a single {@code @Configuration} class is used to bootstrap
  • your application, however, you may also set {@link #getSources() sources} from:
  • <ul>
  • <li>The fully qualified class name to be loaded by
  • {@link AnnotatedBeanDefinitionReader}</li>
  • <li>The location of an XML resource to be loaded by {@link XmlBeanDefinitionReader}, or
  • a groovy script to be loaded by {@link GroovyBeanDefinitionReader}</li>
  • <li>The name of a package to be scanned by {@link ClassPathBeanDefinitionScanner}</li>
  • </ul>
  • Configuration properties are also bound to the {@link SpringApplication}. This makes it
  • possible to set {@link SpringApplication} properties dynamically, like additional
  • sources ("spring.main.sources" - a CSV list) the flag to indicate a web environment
  • ("spring.main.web-application-type=none") or the flag to switch off the banner
  • ("spring.main.banner-mode=off").
    **/

接着往下看

/**
* 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)。

相关文章

网友评论

      本文标题:boot启动(一)

      本文链接:https://www.haomeiwen.com/subject/myrdnctx.html