spring boot到底帮我们做了那些事?

一、前言

    上一篇介绍了注解,也是为这一篇做铺垫,传统的都是通过配置文件来启动 spring,那 spring boot 到底是做了什么能让我们快速开发昵?

二、启动原理

    看下程序启动的入口,主要两处地方一是 SpringBootApplication 注解,另外就是 run 方法,首先我们看注解部分,上一篇我们也说过注解应该不难看懂,我们看下这个注解里面有什么神奇的东西;

@SpringBootApplication
public class DemoApplication {
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {
    SpringApplication.run(DemoApplication.</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">, args);
}

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type
= FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type
= FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)})
public @interface SpringBootApplication {

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Exclude specific auto-configuration classes such that they will never be applied.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the classes to exclude
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AliasFor(annotation </span>= EnableAutoConfiguration.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
Class</span>&lt;?&gt;[] exclude() <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Exclude specific auto-configuration class names such that they will never be
 * applied.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the class names to exclude
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.3.0
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AliasFor(annotation </span>= EnableAutoConfiguration.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
String[] excludeName() </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Base packages to scan for annotated components. Use {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> #scanBasePackageClasses}
 * for a type-safe alternative to String-based package names.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> base packages to scan
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.3.0
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AliasFor(annotation </span>= ComponentScan.<span style="color: rgba(0, 0, 255, 1)">class</span>, attribute = "basePackages"<span style="color: rgba(0, 0, 0, 1)">)
String[] scanBasePackages() </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Type-safe alternative to {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> #scanBasePackages} for specifying the packages to
 * scan for annotated components. The package of each class specified will be scanned.
 * &lt;p&gt;
 * Consider creating a special no-op marker class or interface in each package that
 * serves no purpose other than being referenced by this attribute.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> base packages to scan
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.3.0
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AliasFor(annotation </span>= ComponentScan.<span style="color: rgba(0, 0, 255, 1)">class</span>, attribute = "basePackageClasses"<span style="color: rgba(0, 0, 0, 1)">)
Class</span>&lt;?&gt;[] scanBasePackageClasses() <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

}

View Code

   看上面代码,除去元注解,主要有 3 个注解,

   @ComponentScan

   这个不需要我们多说太多,这个主要有 2 个作用,组件扫描和自动装配;

   @SpringBootConfiguration

  这个我们也不需要说太多,这个注解主要是继承 @Configuration 注解,这个我们就是为了加载配置文件用的;

  @EnableAutoConfiguration

  这个是我们的重点:

   看图我们来走一下代码,这里有一个重点就是 @Import 注解,这个里面引入了 AutoConfigurationImportSelector.class 这个文件,所以我们就需要看下这里面有那些玩意,值得我们注意的,这个类里面代码有点多我将重点放到下一个代码片段中,让大家结构清晰一些;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY </span>= "spring.boot.enableautoconfiguration"<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Exclude specific auto-configuration classes such that they will never be applied.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the classes to exclude
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
Class</span>&lt;?&gt;[] exclude() <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Exclude specific auto-configuration class names such that they will never be
 * applied.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the class names to exclude
 * </span><span style="color: rgba(128, 128, 128, 1)">@since</span><span style="color: rgba(0, 128, 0, 1)"> 1.3.0
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
String[] excludeName() </span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};

}

View Code

  这是中间比较关键的代码,我们主要看下 loadFactories 方法,这个里面有个常量的配置,位置如下图所示,整段代码实现了把配置文件中的信息通过反射实例化成为 @Configuration 的配置文件,然后通过 @Configuration 最后汇总到容器当中;

    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
                this.beanClassLoader);}

public abstract class SpringFactoriesLoader {

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * The location to look for factories.
 * &lt;p&gt;Can be present in multiple JAR files.
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"<span style="color: rgba(0, 0, 0, 1)">;


</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Log logger = LogFactory.getLog(SpringFactoriesLoader.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Map&lt;ClassLoader, MultiValueMap&lt;String, String&gt;&gt; cache = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentReferenceHashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();


</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Load and instantiate the factory implementations of the given type from
 * {</span><span style="color: rgba(128, 128, 128, 1)">@value</span><span style="color: rgba(0, 128, 0, 1)"> #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
 * &lt;p&gt;The returned factories are sorted through {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> AnnotationAwareOrderComparator}.
 * &lt;p&gt;If a custom instantiation strategy is required, use {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> #loadFactoryNames}
 * to obtain all registered factory names.
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> factoryClass the interface or abstract class representing the factory
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> classLoader the ClassLoader to use for loading (can be {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> null} to use the default)
 * </span><span style="color: rgba(128, 128, 128, 1)">@see</span><span style="color: rgba(0, 128, 0, 1)"> #loadFactoryNames
 * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> IllegalArgumentException if any factory implementation class cannot
 * be loaded or if an error occurs while instantiating any factory
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> &lt;T&gt; List&lt;T&gt; loadFactories(Class&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> factoryClass, @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryClass, </span>"'factoryClass' must not be null"<span style="color: rgba(0, 0, 0, 1)">);
    ClassLoader classLoaderToUse </span>=<span style="color: rgba(0, 0, 0, 1)"> classLoader;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (classLoaderToUse == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        classLoaderToUse </span>= SpringFactoriesLoader.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getClassLoader();
    }
    List</span>&lt;String&gt; factoryNames =<span style="color: rgba(0, 0, 0, 1)"> loadFactoryNames(factoryClass, classLoaderToUse);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (logger.isTraceEnabled()) {
        logger.trace(</span>"Loaded [" + factoryClass.getName() + "] names: " +<span style="color: rgba(0, 0, 0, 1)"> factoryNames);
    }
    List</span>&lt;T&gt; result = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(factoryNames.size());
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String factoryName : factoryNames) {
        result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
    }
    AnnotationAwareOrderComparator.sort(result);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Load the fully qualified class names of factory implementations of the
 * given type from {</span><span style="color: rgba(128, 128, 128, 1)">@value</span><span style="color: rgba(0, 128, 0, 1)"> #FACTORIES_RESOURCE_LOCATION}, using the given
 * class loader.
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> factoryClass the interface or abstract class representing the factory
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> classLoader the ClassLoader to use for loading resources; can be
 * {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> null} to use the default
 * </span><span style="color: rgba(128, 128, 128, 1)">@see</span><span style="color: rgba(0, 128, 0, 1)"> #loadFactories
 * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> IllegalArgumentException if an error occurs while loading factory names
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> List&lt;String&gt; loadFactoryNames(Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)"> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName </span>=<span style="color: rgba(0, 0, 0, 1)"> factoryClass.getName();
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Map&lt;String, List&lt;String&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap</span>&lt;String, String&gt; result =<span style="color: rgba(0, 0, 0, 1)"> cache.get(classLoader);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (result != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        Enumeration</span>&lt;URL&gt; urls = (classLoader != <span style="color: rgba(0, 0, 255, 1)">null</span> ?<span style="color: rgba(0, 0, 0, 1)">
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedMultiValueMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
        </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> (urls.hasMoreElements()) {
            URL url </span>=<span style="color: rgba(0, 0, 0, 1)"> urls.nextElement();
            UrlResource resource </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UrlResource(url);
            Properties properties </span>=<span style="color: rgba(0, 0, 0, 1)"> PropertiesLoaderUtils.loadProperties(resource);
            </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Map.Entry&lt;?, ?&gt;<span style="color: rgba(0, 0, 0, 1)"> entry : properties.entrySet()) {
                List</span>&lt;String&gt; factoryClassNames =<span style="color: rgba(0, 0, 0, 1)"> Arrays.asList(
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                result.addAll((String) entry.getKey(), factoryClassNames);
            }
        }
        cache.put(classLoader, result);
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException ex) {
        </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("Unable to load factories from location [" +<span style="color: rgba(0, 0, 0, 1)">
                FACTORIES_RESOURCE_LOCATION </span>+ "]"<span style="color: rgba(0, 0, 0, 1)">, ex);
    }
}

@SuppressWarnings(</span>"unchecked"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> &lt;T&gt; T instantiateFactory(String instanceClassName, Class&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> factoryClass, ClassLoader classLoader) {
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        Class</span>&lt;?&gt; instanceClass =<span style="color: rgba(0, 0, 0, 1)"> ClassUtils.forName(instanceClassName, classLoader);
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">factoryClass.isAssignableFrom(instanceClass)) {
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> IllegalArgumentException(
                    </span>"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]"<span style="color: rgba(0, 0, 0, 1)">);
        }
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Throwable ex) {
        </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("Unable to instantiate factory class: " +<span style="color: rgba(0, 0, 0, 1)"> factoryClass.getName(), ex);
    }
}

}

View Code

   

  基本上注解这块就是说完了,但是中间少说了几个比较重要的东西,这里要说下需要注意的 2 个问题,

 1.exclude 和 excludeName 这个两个主要时排除你不想加载的配置,用法很简答,不需要说他太多;

 2.scanBasePackages 和 scanBasePackageClasses 这个是为了指定运行目录,好多小伙伴做了项目分离以后,会读取不到 Mappr 等,可以考虑下是不是这个错误;

 重点来了,上面说了加载什么东西,那这些东西啥时候被调用被触发,那我们看下我们重点 run 方法:

 1. 调用 run 方法之前,首先初始化 SpringApplication 对象实例, 这个对象初始化的过程中也做了不少事情让我们来慢慢看起来,接上上面思路,继续完成我们的取经;

//初始化 SpringApplication 对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
          //加载 classpatch 文件下面的配置文件
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
                //判断是否是 web 运行环境
        this.webApplicationType = deduceWebApplicationType();
                //使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer。
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
               //使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener。
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
                //获得当前执行 main 方法的类对象
        this.mainApplicationClass = deduceMainApplicationClass();}
View Code

  ApplicationContextInitializer 接口是在 spring 容器刷新之前执行的一个回调函数,主要有 2 点作用:1. 在上下文(ConfigurableApplicationContext)刷新(refresh)之前调用,2. 通常被用作 web 应用,在一些程序设计在 spring 容器初始化使用。比如说注册一些配置或者激活一些配置文件针对(ConfigurableApplicationContext 的 getEnvironment() 方法)。另外这个函数支持支持 Order 注解。并且代表着执行顺序。我在下面也写了一个简单的例子,同时这个也是支持在配置文件中配置的 context.initializer.classes= 后面加上回调函数的全限定名称; 另外假设我们在当前项目中要引入别的 jar, 这个 jar 要在加载前做一些配置,这个时候我们项目下的 resources 下新建 META-INF 文件夹,文件夹下新建 spring.factories 文件,然后写上 org.springframework.context.ApplicationContextInitializer= 后面加上需要回调函数的全限定名称,这个是在主项目启动的时候就会优先加载了;

  ApplicationListener 接口是 spring boot 的监听器,有 7 种类型,我准备好了 demo 大家执行一下,我相信对下面 run 方法的运行就不是很迷惑了;

@Order(3)
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {System.out.println(applicationContext.getBeanDefinitionCount()+applicationContext.getBeanDefinitionNames().toString());
    }
}

@Order(1)
public class TestApplicationContextInitializer2 implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println(applicationContext.getDisplayName());
}
}

@SpringBootApplication
public class DemoApplication {

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> main(String[] args) {

// SpringApplication.run(DemoApplication.class, args);
SpringApplication springApplication=new SpringApplication(DemoApplication.class);
springApplication.addListeners((ApplicationListener
<ApplicationStartingEvent>) event->{
System.out.println(
"Starting");
});
springApplication.addListeners((ApplicationListener
<ApplicationStartedEvent>) event->{
System.out.println(
"Started");
});
springApplication.addListeners((ApplicationListener
<ApplicationFailedEvent>) event->{
System.out.println(
"Failed");
});
springApplication.addListeners((ApplicationListener
<ApplicationPreparedEvent>) event->{
System.out.println(
"Prepared");
});

    springApplication.addListeners((ApplicationListener</span>&lt;SpringApplicationEvent&gt;) event-&gt;<span style="color: rgba(0, 0, 0, 1)">{
        System.out.println(</span>"SpringApplication"<span style="color: rgba(0, 0, 0, 1)">);
    });

    springApplication.addListeners((ApplicationListener</span>&lt;ApplicationEnvironmentPreparedEvent&gt;) event-&gt;<span style="color: rgba(0, 0, 0, 1)">{
        System.out.println(</span>"EnvironmentPrepare"<span style="color: rgba(0, 0, 0, 1)">);
    });

    springApplication.addListeners((ApplicationListener</span>&lt;ApplicationReadyEvent&gt;) event-&gt;<span style="color: rgba(0, 0, 0, 1)">{
        System.out.println(</span>"Ready"<span style="color: rgba(0, 0, 0, 1)">);
    });
    springApplication.addInitializers(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TestApplicationContextInitializer());
    springApplication.addInitializers(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TestApplicationContextInitializer2());



    springApplication.run(args);
}

}

View Code

2. 实例化完成开始执行 run 方法,这个里面流程比较多,我们先来看一个继承关系,然后结合上面 ApplicationListener 的 demo 我相信大家已经对其广播实现已经有了一个了解,这里我还是提一下通过SpringApplicationRunListener在 ApplicationContext 初始化过程中各个时点发布各种广播事件,并由ApplicationListener负责接收广播事件。接下来我们看下启动流程:

   

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
                //收集异常
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            //设置 Headless 模式为全局
        configureHeadlessProperty();
          //加载所有 classpath 下面的 META-INF/spring.factories SpringApplicationRunListener( 不同的时间点发送事件通知)
        SpringApplicationRunListeners listeners = getRunListeners(args);
          //spring boot 启动初始化开始
        listeners.starting();
        try {
                        //装配参数和环境
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
                        //打印 Banner
            Banner printedBanner = printBanner(environment);
                        //创建 ApplicationContext()
            context = createApplicationContext();
                        //返回异常
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                        //装配 Context
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            //执行 context 的 refresh 方法,并且调用 context 的 registerShutdownHook 方法(这一步执行完成之后,spring 容器加载完成)
            refreshContext(context);
             //回调,获取容器中所有的 ApplicationRunner、CommandLineRunner 接口
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}
             //容器初始化完成
            listeners.started(context);
            //遍历所有注册的 ApplicationRunner 和 CommandLineRunner,并执行其 run() 方法。
           //该过程可以理解为是 SpringBoot 完成 ApplicationContext 初始化前的最后一步工作,
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">容器开始被调用</span>

listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters,
null);
throw new IllegalStateException(ex);
}
return context;
}

View Code

  写了这么多我忘记放入执行结果了这里补进去:

  

、总结

  要是想在 spring boot 初始化的时候搞点事情的化,那么有 3 种方法:

  1. 创建 ApplicationContextInitializer 的实现类

  2. 创建 ApplicationListener 的实现类

  3. 创建 ApplicationRunner 和 CommandLineRunner 的实现类

  上面 2 种已经有了 demo,我再来写一个第 3 种的 demo;

@Order(2)
@Component
public class CommandLineRunnerDemo implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunnerDemo");}
}

@Order(1)
@Component
public class ApplicationRunnerDemo implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(
"ApplicationRunner");
}
}

View Code

  知道启动的流程又懂了扩展,我们接下来开始 spring cloud 吧。

  上面有什么的不懂的可以加群:438836709

  也可以关注我公众号