美文网首页
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