SpringBoot:SpringBoot 的底层运行原理解析 旧城等待, 2023-09-24 23:35 71阅读 0赞 * 声明原文出处:狂神说 ### 文章目录 ### * 1. pom.xml * * 1 . 父依赖 * 2 . 启动器 spring-boot-starter * 2. 主启动类的注解 * * 1. 默认的主启动类 * 2. @SpringBootApplication * 3. @ComponentScan * 4. @SpringBootConfiguration * 5. SpringBootApplication 注解 * 6. spring.factories * 7. 结论 * 8. 简单图解 * 3. 主启动类的方法 * * 1. SpringApplication * 2. run方法流程分析 # 1. pom.xml # ## 1 . 父依赖 ## 其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> 点进去,发现还有一个父依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.5.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent> 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心; **以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了** ## 2 . 启动器 spring-boot-starter ## <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-xxx</artifactId> </dependency> **springboot-boot-starter-xxx**:就是spring-boot的场景启动器 比如 : **spring-boot-starter-web**:帮我们导入了web模块正常运行所依赖的组件; SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 ,要用什么功能就导入什么样的场景启动器即可 # 2. 主启动类的注解 # ## 1. 默认的主启动类 ## //@SpringBootApplication 来标注一个主程序类 //说明这是一个Spring Boot应用 @SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { //以为是启动了一个方法,没想到启动了一个服务 SpringApplication.run(SpringbootApplication.class, args); } } ## 2. @SpringBootApplication ## * **作用** 标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用 进入这个注解 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = { @Filter( type = FilterType.CUSTOM, classes = { TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = { AutoConfigurationExcludeFilter.class} )} ) …… ## 3. @ComponentScan ## * 这个注解在Spring中很重要 ,它对应**XML配置中的元素**。 * **作用** 自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中 ## 4. @SpringBootConfiguration ## * **作用** SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类 进入这个注解 // 点进去得到下面的 @Component @Configuration public @interface SpringBootConfiguration {} @Component public @interface Configuration {} * **@Configuration** 说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件 * **@Component** 说明启动类本身也是Spring中的一个组件而已,负责启动应用 ## 5. SpringBootApplication 注解 ## 1. @EnableAutoConfiguration * **作用** **开启自动配置功能** 该注解告诉SpringBoot开启自动配置功能,这样自动配置才能生效 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 进入该注解 @Inherited @AutoConfigurationPackage @Import({ AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { 1. @AutoConfigurationPackage * **作用** 自动配置包 1. @import * **作用**:Spring底层注解@import , 给容器中导入一个组件 1. @Import(\{Registrar.class\})\* * **作用**:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 1. @Import(\{AutoConfigurationImportSelector.class\}) * **作用** :给容器导入组件 * AutoConfigurationImportSelector :自动配置导入选择器 1. 进入AutoConfigurationImportSelector 1. 这个类中有一个这样的方法 // 获得候选的配置 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //这里的getSpringFactoriesLoaderFactoryClass()方法 //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; } 1. 这个方法又调用了 SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); //这里它又调用了 loadSpringFactories 方法 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } 1. 我们继续点击查看 loadSpringFactories 方法 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去获取一个资源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //将读取到的资源遍历,封装成为一个Properties while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } } 1. 发现一个多次出现的文件:**spring.factories** ## 6. spring.factories ## * 打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center][] ## 7. 结论 ## * SpringBoot 所有的自动配置都是在启动的时候扫描并加载的 * **自动配置真正实现** * 从classpath中搜寻所有的META-INF/spring.factories配置文件 * 并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 * 然后将这些都汇总成为一个实例并加载到IOC容器中 * 不过有部分配置类不会一开始就自动加载,只有满足 @ConditionalOnXXX(xxxx) 中的条件才会生效 —— 导入对应的 start 启动器 ## 8. 简单图解 ## ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center 1][] # 3. 主启动类的方法 # * 在主启动类的 main 方法中调用这样一个方法 SpringApplication.run * **SpringApplication.run** 方法主要分两部分 * 一部分是SpringApplication的实例化 * 二是run方法的执行 ## 1. SpringApplication ## * **这个类主要做了以下四件事情** 1. 推断应用的类型是普通的项目还是Web项目 2. 查找并加载所有可用初始化器 , 设置到initializers属性中 3. 找出所有的应用程序监听器,设置到listeners属性中 4. 推断并设置main方法的定义类,找到运行的主类 查看构造器: public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // ...... this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); } ## 2. run方法流程分析 ## ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center 2][] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center]: https://img-blog.csdnimg.cn/20200913140622698.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA==,size_16,color_FFFFFF,t_70#pic_center [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center 1]: https://img-blog.csdnimg.cn/2020092610411980.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA==,size_16,color_FFFFFF,t_70#pic_center [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA_size_16_color_FFFFFF_t_70_pic_center 2]: https://img-blog.csdnimg.cn/20200913141204230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDQ0OTgzOA==,size_16,color_FFFFFF,t_70#pic_center
相关 Tomcat运行原理解析 Tomcat: Tomcat是一个JSP/Servlet容器。其作为Servlet容器,有三种工作模式:独立的Servlet容器、进程内的Servlet容器和进程外的 深藏阁楼爱情的钟/ 2024年02月18日 15:59/ 0 赞/ 60 阅读
相关 HashMap底层实现原理解析 ![6225b9e118544330a17ef8314bc9e2d0.png][] 一、 HashMap中的put()和get()的实现原理: 1、map.put(k 阳光穿透心脏的1/2处/ 2023年10月13日 11:03/ 0 赞/ 68 阅读
相关 Spring底层核心原理解析 class userServiceProxy extends UserService(){ //生成代理类去继承UserService UserSer 偏执的太偏执、/ 2023年10月03日 22:04/ 0 赞/ 9 阅读
相关 SpringBoot:SpringBoot 的底层运行原理解析 声明原文出处:狂神说 文章目录 1. pom.xml 1 . 父依赖 旧城等待,/ 2023年09月24日 23:35/ 0 赞/ 72 阅读
相关 CopyOnWriteArrayList底层原理解析 ArrayList并不是一个线程安全的容器,对于高并发下可以用Vector,或者使用Collections的静态方法将ArrayList包装成一个线程安全的类,但是这些方式都是 不念不忘少年蓝@/ 2022年12月28日 08:22/ 0 赞/ 182 阅读
相关 01-Spring底层核心原理解析 Bean查找先根据Bean的类型去(spring容器中-(map))查找,若类型查询不到再根据类型的名称去查找 名称重复会覆盖 --------------- 野性酷女/ 2022年09月10日 08:19/ 0 赞/ 278 阅读
相关 JavaScript运行原理解析 写在前面的话: 发现使用了那么长时间的Javascript,但是对其运行原理还是不清晰,今天特意总结一下,把大神们的理论和自己的总结都记录到下面; 1. 什么是JavaSc àì夳堔傛蜴生んèń/ 2022年08月19日 08:56/ 0 赞/ 206 阅读
相关 JavaScript引擎运行原理解析 1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。比方说,当 逃离我推掉我的手/ 2022年05月09日 03:22/ 0 赞/ 283 阅读
相关 一文看懂mybatis底层运行原理解析 友情提示 : 本文的重点是解析mybatis执行一次sql 的流程 ,过程中思维比较跳跃,觉得比较难得可以往下查看总结中源码流程 -------------------- 电玩女神/ 2022年02月26日 04:56/ 0 赞/ 424 阅读
还没有评论,来说两句吧...