一文深度解析 Spring Boot,纯手写一个 stater插件
toqiye 2024-10-12 12:21 18 浏览 0 评论
推荐阅读:
- Spring Boot +Security+Redis+Vue构建后台管理系统源码免费分享
- 同事精通Spring boot2.0跳槽阿里成功,细问之后原来看了这个文档
- SpringBoot+Dubbo技术栈实现微服务分布式电子商务项目源代码分享
什么是 Spring Boot
Spring Boot 基本上是 Spring 框架的扩展,它消除了设置 Spring 应用程序所需的复杂例行配置。我们在使用 Spring 框架的时候,我们接触得比较多的应该是 Spring MVC、 IOC 、 DI 、AOP 等等,而这些框架在使用的过程中会需要配置大量的 XML,或者需要做很多繁琐的配置。Spring Boot 可以帮助我们快速搭建一个基于 Spirng 框架以及 Spring 生态体系的应用解决方案。
我们对着官网来翻译一下:
- 创建独立的 Spring 应用程序
- 直接嵌入Tomcat,Jetty 或 Undertow(无需部署 WAR 文件),java -jar 就可以运行
- 提供 starter 依赖来简化你自己的配置
- 自动装配 Spring 和第三方的依赖只要可能
- 提供可用于生产的功能,例如指标,运行状况检查和外部化配置,比如 Actuator
- 完全没有代码生成,也不需要 XML 配置
看了上面这么多主要有两点,约定大于配置 和 自动装配。
约定大于配置
约定优于配置的体现主要是
- maven 的目录结构,默认有 resources 文件夹存放配置文件,默认打包方式为 jar
- spring-boot-starter-web 中默认包含 spring mvc 相关依赖以及内置的 tomcat 容器,使得构建一个 web 应用更加简单
- 默认提供 application.properties/yml 文件
- 默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件
- EnableAutoConfiguration 默认对于依赖的 starter 进行自动装配
自动装配
讲自动装配首先从注解开始,我们从 @SpringBootApplication 点进去
可以看到它实际上是一个复合注解,上面四个是元注解,下面三个才是重点
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
我们可以直接用这三个注解也可以启动 Spring Boot 应用,只是每次配置三个注解比较繁琐,所以直接用一个复合注解更方便些。下面是官网的截图,刚兴趣的小伙伴可自行翻译。
@Configuration
@Configuration 这个注解大家应该都用过,它是 JavaConfig 形式的基于 Spring IOC 容器的配置类使用的一种注解。所以在启动类里面标注了 @Configuration,意味着它其实也是一个 IoC 容器的配置类。
传统意义上的 Spring 应用都是基于 xml 形式来配置 bean 的依赖关系。但是从 Spring3 开始,Spring 就支持了两种 bean 的配置方式,一种是基于 xml 文件方式,另一种就是 JavaConfig,任何一个标注了@Configuration 的 Java 类定义都是一个JavaConfig 配置类。而在这个配置类中,任何标注了@Bean 的方法,它的返回值都会作为 Bean 定义注册到 Spring 的 IoC 容器,方法名默认成为这个 Bean 的 id。然后通过 spring 容器在启动的时候,把 Bean 进行初始化并且,如果 Bean 之间存在依赖关系,则分析这些已经在 IoC 容器中的 Bean 根据依赖关系进行组装。
@ComponentScan
@ComponentScan 这个注解大家也用过,这个很简单,就是扫包,相当于 xml 配置文件中的 < context:component-scan > 。 它的主要作用就是扫描指定路径下的标识了需要装配的类,自 动装配到 Spring 的 IoC 容器中。
标识需要装配的类的形式主要是:@Component、@Repository、@Service、@Controller这类的注解标识的类。(注:@Repository、@Service、@Controller 的底层还是 @Component)。 ComponentScan 默认会扫描当前 package 下的的所有加了相关注解标识的类到 IoC 容器中。
@EnableAutoConfiguration
好,主角登场了,@EnableAutoConfiguration 是 Spring Boot 的灵魂,是重中之重。从 Spring3.1 开始,提供了一系列的 @Enable 开头的注解,它是在 JavaConfig 框架上更进一步的完善,使用户在使用 Spring 相关的框架避免配置大量的代码从而降低使用的难度。
比如常见的一些 Enable 注解:@EnableWebMvc、@EnableScheduling、@EnableAsync 等等。 每一个涉及到 Enable 开头的注解,都会带有一个 @Import 的注解, @EnableAutoConfiguration 也不例外,我们点进去发现如红框所示。
@Import 注解是什么意思呢? 它对应 XML 形式下的< import resource/ >,就是导入资源,把多个分布在不同容器下的配置合并在一个配置中。@Import 注解可以配置三种不同的 class :
- 普通 Bean 或者带有 @Configuration 的配置文件
- 实现 ImportSelector 接口进行动态注入
- 实现 ImportBeanDefinitionRegistrar 接口进行动态注入
这里导入的是第二种 importSelector,这是一种动态注入 Bean 的技术,我们把AutoConfigurationImportSelector 点进去,发现它实现了 ImportSelector 接口。
找到实现方法selectImports ,该方法的作用就是找到相应的 Bean 注入到容器中。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
再从 getAutoConfigurationEntry 方法点进去,这里面做了许多事情,就是把找到的 Bean 进行排除、过滤、去重,我们可以看到 removeDuplicates、remove、filter 等方法。
那具体这些 Bean 从哪里找呢,我们将 getCandidateConfigurations 方法点进去,发现了一个惊天秘密,那就是在这有一个 META-INF/spring.factories 文件。
当然这是一个报错信息,我们不敢断定就是这里,没关系,我们把 SpringFactoriesLoader.loadFactoryNames 点进去,发现这里有个变量 FACTORIES_RESOURCE_LOCATION。
而这个变量的值还是 META-INF/spring.factories。
看到这里我们很激动,于是我毫不犹豫的在项目中搜索这个文件,原来 SpringFactoriesLoader 的作用就是从 classpath/META-INF/spring.factories 文件中,根据 key来加载对应的类到 Spring IoC 容器中。
看到这里小伙伴就明白了,就是把这么多Configuration下的 Bean 加载到容器里嘛,但是But,怎么还有 RabbitMQ、Elasticsearch这些我都用不到,怎么也给加到容器里来了,那多浪费空间和内存啊?小伙伴莫慌,于是我又带着好奇心理打开了 RabbitMQ 的配置类。
看到这里终于舒了口气,小伙们有没有发现这里多了一些 Conditional 的注解,其实这些就是条件注解,Spring Boot 也不傻,它会发现如果当前的 classpath 环境下没有相关联的依赖,则意味着这些类没必要进行加载。所以,通过这种条件过滤可以有效的减少 @configuration 类的数量从而降低 Spring Boot 的启动时间。
好,有了上面这么多预备知识后,就可以开始手写一个我们自己的 starter 了。
手写 starter
starter 工程的命名
starter 是一个开箱即用的组件,减少不必要的重复代码,重复配置。例如,如果要使用 Spring 和 JPA 进行数据库访问,在项目中引用spring-boot-starter-data-jpa 即可。
Spring 官方定义的 starter 通常命名遵循的格式为 spring-boot-starter-{name},例如 spring-boot-starter-web。非官方 starter 命名应遵循 {name}-spring-boot-starter 的格式,例如,dubbo-spring-boot-starter。
需求
写一个序列化的插件,并且可以自由的选择 fastjson 还是 gson,如果没选的情况下默认选择fastjson。
步骤
1、 创建一个Spring Boot项目,这里项目名字叫 jackformat-spring-boot-starter
2、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- 这个是用来提示用的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
3、先定义一个格式化的接口,分别写两个实现类
public interface FormatProcessor {
/**
* 定义一个格式化的方法
*
* @param obj
* @param <T>
* @return
*/
<T> String format(T obj);
}
4、写一个配置类,这里用了条件注解,如果 fastjson 和 gson 类存在的情况下才加载对应的实现类,因为在 pom 文件里都引用了,所以这里都会被装载。注意这里红框标的 @Primary,对同一个接口,有几种不同的实现类时,@Autowired 是按类型注入的,不知道要选哪一个,按照第二点需求,用户在没选的情况下默认选择 fastjson,所以这里给 fastjson 的实现上打上 @Primary。
5、配置类,用来读取用户的选择,作用和 @Value 一样,只是用了 jackxu.format 的前缀,这样更方便。
6、序列化实现类,这个就是提供给用户用来序列化用的,看名字 Template 大家也能知道,比如我们常用的 RedisTemplate、JdbcTemplate,构造函数的时候直接传入具体的实现。
7、好,现在就是最关键的主类了,我们从上往下看,@Import 之前说过了,导入配置类,就是将该配置类中的 Bean 注入到容器,@EnableConfigurationProperties 这是在将属性类激活,注入到容器中,也可以用 @Bean 的方式,@Configuration 说明这是一个配置类。接下来将 FormatTemplate 注入到容器中,我们看到首先是去属性类中去读属性,如果是 fastjson 就返回 fastjson 的实现,如果是 gson 就返回 gson 的实现,如果没读取到,就用前面设置的 @Primary 的默认实现。
8、最后一步最关键的就是设置,在 resources 文件夹下创建 META-INF/spring.factories 文件,通过上面的知识,Spring Boot 在启动的时候就是读取该文件下的配置类,从而将 Bean 加载到容器中。
测试
1、将自己的 starter 项目进行 install 打包
2、测试项目中引用自己的 starter
3、写一个controller,一个测试类,并把 formatTemplate 注入进来
4、设置我们需要制定的序列化方式,这里选用 fastjson
5、启动Spring Boot 项目
6、通过浏览器访问,发现这里显示的是 fastjson 方式的序列化,成功了!
7、在测试 gson 的方式,返回了默认 gson 的实现,也成功了!
8、最后测试用户不选择的情况下,默认使用 fastjson,圆满成功!
至此,本个需求已经成功做出来了,我力求在做的过程中将手写一个 starter 所需用到的技术都讲到,串联起来,希望大家喜欢!
后记
小伙伴们,Spring Boot 的使用中极大的简化了我们的使用,我也是14年毕业就开始用 SSM,那时候各种配置各种依赖,各种 XML 很是恶心。但是现在有了 Spring Boot 在几分钟内就可以让我们快速搭建一个项目跑起来,时代在变迁,也感谢让我们越来越便利,在便利的过程中,我们还是要对底层的原理有一点的了解,而不是浮在上面只会使用,否则一但有问题找起来也不方便。
本文带大家对 Spring Boot 底层入了个门,还有 SringApplication 实例创建,设置初始化器和监听器,以及 run 方法里干的一些事,内置 Tomcat 如何实现的,由于篇幅有限都没有讲,留给小伙伴自行研究。
作者:jack_xu
链接:https://juejin.im/post/5ec7d2f5e51d45785c693a9e
相关推荐
- Star 17.3k!给它一张屏幕截图,即可一键克隆网页!
-
本文为大家分享一款本周爆火的GPT开源项目。前言你敢信,只凭借着一张屏幕截图即可转换生成HTML/TailwindCSS代码。可以算得上是前端工程师的福音。它就是screenshot-to-...
- AI从截图直接生成代码、前端程序员的福利!
-
简介项目可以将任何屏幕截图或设计转换为干净的代码(支持大多数框架)。来自领先公司的开发人员和设计师使用的排名第一的工具。完全开源,在GitHub上拥有超过35,000颗星。非常受欢迎。各位小伙...
- 一款高颜值、跨平台、自托管的免费开源CRM项目——Twenty
-
前言大家好,这里是可爱的Cherry。作为一个“甲方”,Cherry其实挺知道客户管理的重要的。但是对于客户管理怎么做,以及CRM的作用,我却是一无所知。之前有朋友在评论区留言,说有没有开源的CRM系...
- 解放双手,前端界面再也不用自己写了?
-
随着AI技术的发展,现在有越来越多的尝试将AI应用于UI设计和开发中,以期提高效率和降低成本。今天就给大家介绍一个开源的AI网页生成工具:OpenUIOpenUIOpenUI是一个创...
- 代码调试,教给你(代码调试是什么意思)
-
昨天我和一些朋友一起调试代码,他们做程序员这一行都不太久,我向他们展示了一些代码调试技巧。今天早上我在想,我应该如何教授他们学习代码调试?我在Twitter上发了一条推文说,我从来没有见过任何好的调试...
- Screenshot-to-code:用屏幕截图生成代码
-
Screenshot-to-code是一个简单的工具,可使用AI将屏幕截图、模型和Figma设计转换为干净、实用的代码。现在支持ClaudeSonnet3.5和GPT-4o!Scre...
- next实现原理(next method)
-
Next.js是一个基于React的服务器端渲染(SSR)和静态生成(SSG)框架,它的实现原理涉及多个关键技术点,包括服务端渲染(SSR)、静态生成(SSG)、客户端渲染(CSR)、...
- 可逐步操作的具体流程(可逐步操作的具体流程包括)
-
假设你是一个单人开发者,使用主流技术栈(React+Node.js+MySQL),以下是详细步骤:---###**一、需求分析与原型设计**1.**核心功能清单**-用户能添加、删除、...
- 截图转代码只需1步!你离高效开发只差这款神器
-
引言在现代前端开发中,将设计稿转换为代码是一个既重要又耗时的环节。手动编写HTML结构、调试CSS样式、调整布局对齐,不仅耗费时间,还容易出错。而Screenshot-to-Code这款革...
- web开发 前端 后端(web开发前端后端)
-
区别:1、前端是指用户可见的界面,而后端是指用户看不到的东西,考虑底层业务逻辑的实现,平台的稳定性、性能等。2、前端开发用到的技术有HTML5、CSS3、JS、jQuery、Bootstrap、Nod...
- 手把手教你Dify私有化部署,打造专属AI平台
-
一、Dify是什么?Dify是一款极具创新性的开源LLM应用开发平台,它就像是一把万能钥匙,为开发者们打开了通往生成式AI应用开发新世界的大门。其融合了后端即服务(BackendasS...
- 前后端分离架构设计:提升开发效率与业务支撑力的密钥
-
前后端分离架构设计解析一、定义与核心思想前后端分离是一种将用户界面(前端)与业务逻辑(后端)解耦的架构模式,通过RESTfulAPI或GraphQL实现数据交互。前端专注于视图渲染与交互逻辑...
- Kubernetes最小部署单元Pod(kubernetes最小部署单元)
-
一、Kubernetes与Pod简介在当今云计算和容器化技术盛行的时代,Kubernetes已然成为容器编排领域的中流砥柱。它是一个开源的容器编排平台,由Google基于其内部使用的Bo...
- 【程序员必藏!零基础本地部署DeepSeek大模型保姆级教程】
-
为什么选择本地部署?数据安全:敏感代码/业务数据永不外传闪电响应:局域网推理延迟<100ms,告别云端排队深度定制:自由修改模型代码,打造专属AI助手准备工具(5分钟搞定)1核心工具下载...
- 【Python程序开发系列】使用Flask实现前后端分离(案例)
-
这是我的第398篇原创文章。一、引言随着web开发的不断发展,前后端分离已成为越来越流行的架构设计。Flask是一个轻量级的Pythonweb框架,非常适合用于构建API,然后配合前端框...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- minorgc (62)
- systemproperties (77)
- vue3-template-admin (63)
- electronsqlite3 (65)
- npm版本管理 (61)
- materialtheme (86)
- node-ssh (68)
- 图床搭建 (62)
- vue3addeventlistener (60)
- mybatisselectone (78)
- css圆形进度条 (69)
- androidble蓝牙开发 (62)
- android-gif-drawable (60)
- appender-ref (64)
- springbootmockito (68)
- 依赖注入的方式 (62)
- cookie跨域共享 (63)
- easyexcel导出图片 (77)
- dp数组 (61)
- js获取兄弟节点 (68)
- sysctl-a (60)
- window.target (62)
- apimodel注解的作用 (60)
- window.onerror (66)
- springmvc教程 (65)