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><?>[] 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.
* <p>
* 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><?>[] scanBasePackageClasses() <span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)"> {};
}
看上面代码,除去元注解,主要有 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><?>[] 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)"> {};
}
这是中间比较关键的代码,我们主要看下 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.
* <p>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<ClassLoader, MultiValueMap<String, String>> cache = <span style="color: rgba(0, 0, 255, 1)">new</span> ConcurrentReferenceHashMap<><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.
* <p>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}.
* <p>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> <T> List<T> loadFactories(Class<T><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><String> 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><T> result = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<><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<String> loadFactoryNames(Class<?><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<String, List<String>><span style="color: rgba(0, 0, 0, 1)"> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap</span><String, String> 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><URL> 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<><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<?, ?><span style="color: rgba(0, 0, 0, 1)"> entry : properties.entrySet()) {
List</span><String> 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> <T> T instantiateFactory(String instanceClassName, Class<T><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><?> 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);
}
}
}
基本上注解这块就是说完了,但是中间少说了几个比较重要的东西,这里要说下需要注意的 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();}
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><SpringApplicationEvent>) event-><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><ApplicationEnvironmentPreparedEvent>) event-><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><ApplicationReadyEvent>) event-><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);
}
}
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;
}
写了这么多我忘记放入执行结果了这里补进去:
三、总结
要是想在 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");
}
}
知道启动的流程又懂了扩展,我们接下来开始 spring cloud 吧。
上面有什么的不懂的可以加群:438836709
也可以关注我公众号