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
也可以关注我公众号