总流程图
入口类
1 2 3 4 5 6
| @SpringBootApplication public class DevServiceApplication { public static void main(String[] args) { SpringApplication.run(DevServiceApplication.class,args); } }
|
SpringBootApplication注解
Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:
1 2 3 4 5 6 7 8 9 10 11 12
| @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 { ... }
|
从源码可以看出,这个注解是**@SpringBootConfiguration**,@EnableAutoConfiguration以及**@ComponentScan**这三个注解的组合
@SpringBootConfiguration
1 2 3 4 5 6 7 8 9
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true;
}
|
Spring Boot的配置类;标注在某个类上,表示一个类提供了Spring Boot应用程序
看SpringBootConfiguration的源码可以看出来,他其实继承与**@Configuration**,二者功能也一样,标注当前类是配置类,并且会将当前类声明的一个或多个@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。
1 2 3 4 5 6 7 8 9 10 11 12
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { @AliasFor( annotation = Component.class ) String value() default "";
boolean proxyBeanMethods() default true; }
|
配置类相当于配置文件;配置类也是容器中的一个组件,它使用了@Component这个注解。
@Configuration注解使用了@Component注解,但两个注解还是有区别的。
@EnableAutoConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {}; }
|
告诉SpringBoot开启自动配置功能,这样自动配置才能生效,最为重要
使用@EnableAutoConfiguration这个注解开启自动扫描,然后使用select选择挑选满足条件的文件,并且使用SpringFactoriesLoader进行实例化。最后加载到IOC容器里面,即ApplicationContext中。
@Import注解,它会加载AutoConfigurationImportSelector类,然后就会触发这个类的selectImports()方法。根据返回的String数组(配置类的Class的名称)加载配置类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
|
我们一直点下去,就可以找到最后的幕后英雄,就是SpringFactoriesLoader类,通过loadSpringFactories()方法加载META-INF/spring.factories中的配置类。
这里使用了spring.factories文件的方式加载配置类,提供了很好的扩展性。
所以@EnableAutoConfiguration注解的作用其实就是开启自动配置,自动配置主要则依靠这种加载方式来实现。
1 2 3 4 5 6 7
| @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage { }
|
@ComponentScan
@ComponentScan就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IOC容器中去
所以启动类最好定义在Root package下,因为一般我们在使用@SpringBootApplication时,都不指定basePackages的。
SpringApplication类
SpringApplication类实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@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(); }
|
参考文章
- 深入剖析Springboot启动原理的底层源码,再也不怕面试官问了
- 一文搞懂springboot启动原理
- SpringBoot启动流程是怎样的?