SpringBoot原理深入及源码剖析
1 依赖管理
问题:(1)为什么导入dependency时不需要指定版本?
在Spring Boot入门程序中,项目pom.xml文件有两个核心依赖,分别是spring-boot-starter-parent和spring-boot-starter-web,关于这两个依赖的相关介绍具体如下:
1.spring-boot-starter-parent依赖
在pom.xml文件中找到spring-boot-starter-parent依赖,示例代码如下:
<!-- Spring Boot父项目依赖管理 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent<11./artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.2.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>
<properties> <activemq.version>5.15.11</activemq.version> ... <solr.version>8.2.0</solr.version> <mysql.version>8.0.18</mysql.version> <kafka.version>2.3.1</kafka.version><spring-amqp.version>2.2.2.RELEASE</spring-amqp.version> <spring-restdocs.version>2.0.4.RELEASE</spring-restdocs.version> <spring-retry.version>1.2.4.RELEASE</spring-retry.version> <spring-security.version>5.2.1.RELEASE</spring-security.version> <spring-session-bom.version>Corn-RELEASE</spring-session-bom.version> <spring-ws.version>3.0.8.RELEASE</spring-ws.version> <sqlite-jdbc.version>3.28.0</sqlite-jdbc.version> <sun-mail.version>${jakarta-mail.version}</sun-mail.version> <tomcat.version>9.0.29</tomcat.version> <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version> <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version> ... </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>tomcat-embed-el</artifactId> <groupId>org.apache.tomcat.embed</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.2.RELEASE</version> <scope>compile</scope> </dependency> </dependencies>
@SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); } }
@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中 @Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时 @Documented //表示注解可以记录在javadoc中 @Inherited //表示可以被子类继承该注解 @SpringBootConfiguration // 标明该类为配置类 @EnableAutoConfiguration // 启动自动配置功能 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes =TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes =AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { // 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。 @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; // 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全类名字符串数组。 @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; // 指定扫描包,参数是包名的字符串数组。 @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; // 扫描特定的包,参数类似是Class类型数组。 @AliasFor(annotation = ComponentScan.class, attribute ="basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented
@Configuration // 配置类的作用等同于配置文件,配置类也是容器中的一个对象 public @interface SpringBootConfiguration { }

1 // 自动配置包 2 @AutoConfigurationPackage 3 // Spring的底层注解@Import,给容器中导入一个组件; 4 // 导入的组件是AutoConfigurationPackages.Registrar.class 5 @Import(AutoConfigurationImportSelector.class) 6 // 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。 7 public @interface EnableAutoConfiguration { 8 String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; 9 // 返回不会被导入到 Spring 容器中的类 10 Class<?>[] exclude() default {}; 11 // 返回不会被导入到 Spring 容器中的类名 12 String[] excludeName() default {}; 13 }

1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @Import({Registrar.class}) // 导入Registrar中注册的组件 6 public @interface AutoConfigurationPackage { 7 }




1 protected AutoConfigurationEntry 2 getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 3 AnnotationMetadata annotationMetadata) { 4 //判断EnabledAutoConfiguration注解有没有开启,默认开启 5 if (!isEnabled(annotationMetadata)) { 6 return EMPTY_ENTRY; 7 } 8 //获得注解的属性信息 9 AnnotationAttributes attributes = getAttributes(annotationMetadata); 10 //获取默认支持的自动配置类列表 11 List<String> configurations = 12 getCandidateConfigurations(annotationMetadata, attributes); 13 //去重 14 configurations = removeDuplicates(configurations); 15 //去除一些多余的配置类,根据EnabledAutoConfiguratio的exclusions属性进行排除 16 Set<String> exclusions = getExclusions(annotationMetadata, attributes); 17 checkExcludedClasses(configurations, exclusions); 18 configurations.removeAll(exclusions); 19 //根据pom文件中加入的依赖文件筛选中最终符合当前项目运行环境对应的自动配置类 20 configurations = filter(configurations, autoConfigurationMetadata); 21 //触发自动配置导入监听事件 22 fireAutoConfigurationImportEvents(configurations, exclusions); 23 return new AutoConfigurationEntry(configurations, exclusions); 24 }

1 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable 2 ClassLoader classLoader) { 3 //获取出入的键 4 String factoryClassName = factoryClass.getName(); 5 return 6 (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, 7 Collections.emptyList()); 8 } 9 private static Map<String, List<String>> loadSpringFactories(@Nullable 10 ClassLoader classLoader) { 11 MultiValueMap<String, String> result = 12 (MultiValueMap)cache.get(classLoader); 13 if (result != null) { 14 return result; 15 } else { 16 try { 17 //如果类加载器不为null,则加载类路径下spring.factories文件,将其中设置的 18 配置类的全路径信息封装 为Enumeration类对象 19 Enumeration<URL> urls = classLoader != null ? 20 classLoader.getResources("META-INF/spring.factories") : 21 ClassLoader.getSystemResources("META-INF/spring.factories"); 22 LinkedMultiValueMap result = new LinkedMultiValueMap(); 23 //循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入的 24 键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中 25 while(urls.hasMoreElements()) { 26 URL url = (URL)urls.nextElement(); 27 UrlResource resource = new UrlResource(url); 28 Properties properties = 29 PropertiesLoaderUtils.loadProperties(resource); 30 Iterator var6 = properties.entrySet().iterator(); 31 while(var6.hasNext()) { 32 Entry<?, ?> entry = (Entry)var6.next(); 33 String factoryClassName = 34 ((String)entry.getKey()).trim(); 35 String[] var9 = 36 StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); 37 int var10 = var9.length; 38 for(int var11 = 0; var11 < var10; ++var11) { 39 String factoryName = var9[var11]; 40 result.add(factoryClassName, factoryName.trim()); 41 } 42 } 43 } 44 cache.put(classLoader, result); 45 return result;

1 public final class SpringFactoriesLoader { 2 public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/spring.factories"; 3 }


1 |- @SpringBootConfiguration 2 |- @Configuration //通过javaConfig的方式来添加组件到IOC容器中 3 |- @EnableAutoConfiguration 4 |- @AutoConfigurationPackage //自动配置包,与@ComponentScan扫描到的添加到IOC 5 |- @Import(AutoConfigurationImportSelector.class) //到METAINF/spring.factories中定义的bean添加到IOC容器中 6 |- @ComponentScan //包扫描
3.执行原理
每个Spring Boot项目都有-个主程序启动类,在主程序启动类中有-个启动项目的main()方法,在该 方法中通过执行SpringApplication.run()即可启动
整个Spring Boot程序。
问题:那么SpringApplication.run()方法到底是如何做到启动Spring Boot项目的呢?
SpringApplication.run()方法内部执行了两个操作,分别是SpringApplication实 例的初始化创建和调用run()启动项目,这两个阶段的实现具体说明如下
1 . SpringApplication实例的初始化创建
查看SpringApplication实例对象初始化创建的源码信息,核心代码具体如下
从上述源码可以看出, SpringApplication的初始化过程主要包括4部分,具体说明如下。
(1) this.webApplicationType = WebApplicationType.deduceFromClasspath()
用于判断当前webApplicationType应用的类型。 deduceFromClasspath()方法用于查看Classpath类路 径下是否存在某个特征类,从而判断当前webApplicationType类型是SERVLET应用 (Spring 5之前的传 统MVC应用)还是REACTIVE应用 (Spring 5开始出现的WebFlux交互式应用)
(2) this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
用于SpringApplication应用的初始化器设置。在初始化器设置过程中,会使用Spring类加载器 SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中 获取所有可用的应用初始化器类ApplicationContextInitializer。
(3) this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
用于SpringApplication应用的监听器设置。监听器设置的过程与上-步初始化器设置的过程基本-样,
也是使用SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的 spring.factores文件中获取所有可用的监听器类ApplicationListener。
(4) this.mainApplicationClass = this.deduceMainApplicationClass()
用于推断并设置项目main()方法启动的主程序启动类
2 . 项目的初始化启动
第-步:获取并启动监听器
第二步:根据SpringApplicationRunListeners以及参数来准备环境
第三步:创建Spring容器
第四步: Spring容器前置处理
第五步:刷新容器
第六步: Spring容器后置处理
第七步:发出结束执行的事件
第八步:执行Runners
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)