深入理解 Spring Boot Starters 原理(手写Spring boot Start)

一、Spring Boot Starter诞生原因

Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里

Starter POMs are a set of convenient dependency descriptors that you
can include in your application. You get a one-stop-shop for all the
Spring and related technology that you need, without having to hunt
through sample code and copy paste loads of dependency descriptors.
For example, if you want to get started using Spring and JPA for
database access, just include the spring-boot-starter-data-jpa
dependency in your project, and you are good to go.

大概意思就是说starter是一种对依赖的synthesize(合成),这是什么意思呢?我可以举个例子来说明。

传统的做法
在没有starter之前,假如我想要在Spring中使用jpa,那我可能需要做以下操作:

  1. 在Maven中引入使用的数据库的依赖(即JDBC的jar)
  2. 引入jpa的依赖
  3. 在xxx.xml中配置一些属性信息
  4. 反复的调试直到可以正常运行

需要注意的是,这里操作在我们每次新建一个需要用到jpa的项目的时候都需要重复的做一次。也许你在第一次自己建立项目的时候是在Google上自己搜索了一番,花了半天时间解决掉了各种奇怪的问题之后,jpa终于能正常运行了。有些有经验的人会在OneNote上面把这次建立项目的过程给记录下来,包括操作的步骤以及需要用到的配置文件的内容,在下一次再创建jpa项目的时候,就不需要再次去Google了,只需要照着笔记来,之后再把所有的配置文件copy&paste就可以了。

像上面这样的操作也不算不行,事实上我们在没有starter之前都是这么干的,但是这样做有几个问题:

  1. 如果过程比较繁琐,这样一步步操作会增加出错的可能性
  2. 不停地copy&paste不符合Don’t repeat yourself精神
  3. 在第一次配置的时候(尤其如果开发者比较小白),需要花费掉大量的时间

使用Spring Boot Starter提升效率
starter的主要目的就是为了解决上面的这些问题。

starter的理念:starter会把所有用到的依赖都给包含进来,避免了开发者自己去引入依赖所带来的麻烦。需要注意的是不同的starter是为了解决不同的依赖,所以它们内部的实现可能会有很大的差异,例如jpa的starter和Redis的starter可能实现就不一样,这是因为starter的本质在于synthesize,这是一层在逻辑层面的抽象,也许这种理念有点类似于Docker,因为它们都是在做一个“包装”的操作,如果你知道Docker是为了解决什么问题的,也许你可以用Docker和starter做一个类比。

starter的实现:虽然不同的starter实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfiguration。因为Spring Boot坚信“约定大于配置”这一理念,所以我们使用ConfigurationProperties来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter的ConfigurationProperties还使得所有的配置属性被聚集到一个文件中(一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。

starter的整体逻辑:

在这里插入图片描述

上面的starter依赖的jar和我们自己手动配置的时候依赖的jar并没有什么不同,所以我们可以认为starter其实是把这一些繁琐的配置操作交给了自己,而把简单交给了用户。除了帮助用户去除了繁琐的构建操作,在“约定大于配置”的理念下,ConfigurationProperties还帮助用户减少了无谓的配置操作。并且因为 application.properties 文件的存在,即使需要自定义配置,所有的配置也只需要在一个文件中进行,使用起来非常方便。

了解了starter其实就是帮助用户简化了配置的操作之后,要理解starter和被配置了starter的组件之间并不是竞争关系,而是辅助关系,即我们可以给一个组件创建一个starter来让最终用户在使用这个组件的时候更加的简单方便。基于这种理念,我们可以给任意一个现有的组件创建一个starter来让别人在使用这个组件的时候更加的简单方便,事实上Spring Boot团队已经帮助现有大部分的流行的组件创建好了它们的starter,你可以在这里查看这些starter的列表。

二、创建自己的Spring Boot Starter

如果你想要自己创建一个starter,那么基本上包含以下几步

  1. 创建一个starter项目,关于项目的命名你可以参考这里
  2. 创建一个ConfigurationProperties用于保存你的配置信息(如果你的项目不使用配置信息则可以跳过这一步,不过这种情况非常少见)
  3. 创建一个AutoConfiguration,引用定义好的配置信息;在AutoConfiguration中实现所有starter应该完成的操作,并且把这个类加入spring.factories配置文件中进行声明
  4. 打包项目,之后在一个SpringBoot项目中引入该项目依赖,然后就可以使用该starter了
    我们来看一个例子(例子的完整代码位于https://github.com/RitterHou/learn-spring-boot-starter)

首先新建一个Maven项目,设置 pom.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>http-starter</artifactId><version>0.0.1-SNAPSHOT</version><!-- 自定义starter都应该继承自该依赖 --><!-- 如果自定义starter本身需要继承其它的依赖,可以参考 https://stackoverflow.com/a/21318359 解决 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starters</artifactId><version>1.5.2.RELEASE</version></parent><dependencies><!-- 自定义starter依赖此jar包 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- lombok用于自动生成get、set方法 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version></dependency></dependencies></project>

创建proterties类来保存配置信息:

@ConfigurationProperties(prefix = "http") // 自动获取配置文件中前缀为http的属性,把值传入对象参数
@Setter
@Getter
public class HttpProperties {// 如果配置文件中配置了http.url属性,则该默认属性会被覆盖private String url = "http://www.baidu.com/";}

上面这个类就是定义了一个属性,其默认值是 http://www.baidu.com/,我们可以通过在 application.properties 中添加配置 http.url=https://www.zhihu.com 来覆盖参数的值。

创建业务类:

@Setter
@Getter
public class HttpClient {private String url;// 根据url获取网页数据public String getHtml() {try {URL url = new URL(this.url);URLConnection urlConnection = url.openConnection();BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "utf-8"));String line = null;StringBuilder sb = new StringBuilder();while ((line = br.readLine()) != null) {sb.append(line).append("\n");}return sb.toString();} catch (Exception e) {e.printStackTrace();}return "error";}}

这个业务类的操作非常简单,只包含了一个 url 属性和一个 getHtml 方法,用于获取一个网页的HTML数据,读者看看就懂了。

创建AutoConfiguration

@Configuration
@EnableConfigurationProperties(HttpProperties.class)
public class HttpAutoConfiguration {@Resourceprivate HttpProperties properties; // 使用配置// 在Spring上下文中创建一个对象@Bean@ConditionalOnMissingBeanpublic HttpClient init() {HttpClient client = new HttpClient();String url = properties.getUrl();client.setUrl(url);return client;}}

在上面的AutoConfiguration中我们实现了自己要求:在Spring的上下文中创建了一个HttpClient类的bean,并且我们把properties中的一个参数赋给了该bean。
关于@ConditionalOnMissingBean 这个注解,它的意思是在该bean不存在的情况下此方法才会执行,这个相当于开关的角色,更多关于开关系列的注解可以参考这里。

最后,我们在 resources 文件夹下新建目录 META-INF,在目录中新建 spring.factories 文件,并且在 spring.factories 中配置AutoConfiguration:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nosuchfield.httpstarter.HttpAutoConfiguration

到此,我们的starter已经创建完毕了,使用Maven打包该项目。之后创建一个SpringBoot项目,在项目中添加我们之前打包的starter作为依赖,然后使用SringBoot来运行我们的starter,代码如下:

@Component
public class RunIt {@Resourceprivate HttpClient httpClient;public void hello() {System.out.println(httpClient.getHtml());}}

正常情况下此方法的执行会打印出url http://www.baidu.com/ 的HTML内容,之后我们在application.properties中加入配置:

http.url=https://www.zhihu.com/

再次运行程序,此时打印的结果应该是知乎首页的HTML了,证明properties中的数据确实被覆盖了。

转自:https://www.nosuchfield.com/2017/10/15/Spring-Boot-Starters/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FFT字符串匹配(解决通配符问题)

FFT字符串匹配 定义字符串下标从000&#xff0c;开始&#xff0c;有文本串AAA长度为nnn&#xff0c;模式串BBB长度为mmm&#xff0c;我们可以考虑一个函数f(x,y)A(x)−B(y)f(x, y) A(x) - B(y)f(x,y)A(x)−B(y)。 我们设F(x)(x≥m−1)∑i0m−1f(x−m1i,i)F(x)(x \ge m - 1) …

记一次中小公司的研发问题

作者&#xff1a;zollty&#xff0c;资深程序员和架构师&#xff0c;私底下是个爱折腾的技术极客&#xff0c;架构师社区合伙人&#xff01;一、一些不好的现状&#xff0c;及对应的改进方法1、前后端代码绑定在一起&#xff0c;很难维护&#xff0c;前端UI做得太差&#xff0c…

命令行操作mysql

1、通过命令行连接mysql&#xff1a; 1、输入命令&#xff1a;mysql -h localhost&#xff08;服务IP地址&#xff09; -u root&#xff08;用户名&#xff09; -P 3306&#xff08;服务端口&#xff09;-p 2、输入密码 2、显示数据库、表&#xff1a; show databases; //…

2021CCPC华为云挑战赛:HDU 7091 重叠的子串(SAM + 线段树合并)

重叠的子串 给定一个长度为n(1≤∣s∣≤105)n(1 \le \mid s \mid \le 10 ^ 5)n(1≤∣s∣≤105)的只由小写字母构成的字符串sss&#xff0c;有m,(1≤m≤106)m, (1 \le m \le 10 ^ 6)m,(1≤m≤106)个询问&#xff1a; 每次询问给定l,rl, rl,r&#xff0c;问sss是否存在一个字串…

ASP.NET Core 3.0 使用gRPC

一.简介gRPC 是一个由Google开源的&#xff0c;跨语言的&#xff0c;高性能的远程过程调用&#xff08;RPC&#xff09;框架。gRPC使客户端和服务端应用程序可以透明地进行通信&#xff0c;并简化了连接系统的构建。它使用HTTP/2作为通信协议&#xff0c;使用 Protocol Buffers…

mysql 1030 error:Got error 28 from storage engine

最近生产应用连不上生产环境数据库&#xff0c;使用命令行连上去之后&#xff0c;执行命令出现1030 error。 故Google之&#xff0c;解决方案如下。 mysql 1030 error 是因为没有足够的空间&#xff0c;清理磁盘空间即可恢复正常使用。 然后追踪发现&#xff0c;磁盘空间为99G&…

Codeforces Round #739 (Div. 3)(AK实况)

Codeforces Round #739 (Div. 3) A. Dislike of Threes 找到第kkk个既不是333的倍数&#xff0c;个位数上也不是333的数&#xff0c;也已预处理然后O(1)O(1)O(1)输出&#xff0c;也可直接forforfor循环暴力。 #include <bits/stdc.h>using namespace std;int main() {/…

利用Helm简化Kubernetes应用部署(2)

目录定义Charts 使用Helm部署Demo Helm常用操作命令 定义Charts回到之前的“charts”目录&#xff0c;我们依次进行解读并进行简单的修改。Chart.yaml配置示例&#xff1a;apiVersion: v1 appVersion: "1.1" description: A demo Helm chart for Kubernetes name:…

linux查看磁盘空间命令

Linux 查看磁盘空间可以使用 df 和 du 命令。 df df 以磁盘分区为单位查看文件系统&#xff0c;可以获取硬盘被占用了多少空间&#xff0c;目前还剩下多少空间等信息。 例如&#xff0c;我们使用df -h命令来查看磁盘信息&#xff0c; -h 选项为根据大小适当显示&#xff1a; …

Visual Studio 2019 16.3 正式发布,支持 .NET Core 3.0

微软正式发布了 Visual Studio 2019 16.3 版本&#xff0c;主要更新内容如下&#xff1a;.NET Core 3.0Visual Studio 版本 16.3 包括对 .NET Core 3.0 的支持。注意&#xff1a;如果使用的是 .NET Core 3.0&#xff0c;则需要使用 Visual Studio 16.3 或更高版本。.NET Core 桌…

Linux清空文件内容的三种方法

Linux清空文件内容的三种方法 测试文件&#xff1a;test.txt 1、 $ > test.txt 2、 $echo “” > test.txt 3、 $cat /dev/null > test.txt

Problem M. Mediocre String Problem(Z 函数 + PAM)

Problem M. Mediocre String Problem 给定两个串s,ts, ts,t&#xff0c;要求有多少不同的三元组(i,j,k)(i, j, k)(i,j,k)&#xff0c;满足&#xff1a; 1≤i≤j≤∣s∣1 \le i \le j \le \mid s \mid1≤i≤j≤∣s∣。1≤k≤∣t∣1 \le k \le \mid t \mid1≤k≤∣t∣。j−i1≥k…

【干货】规模化敏捷DevOps四大实践之持续探索CE(中英对照版)

本文翻译来自SAFe DevOps社群帅哥网友贾磊&#xff1a;高级质量经理&敏捷教练 曾就职于外企、国企、大型上市企业等&#xff0c;担任过测试工程师、测试经理、项目经理、敏捷教练、质量总监、高级质量经理等岗位。是一名敏捷变革的爱好者和践行者。爱好网球、羽毛球。正文原…

Spring Cloud——Eureka——架构体系

1、概述 Eureka包括两个端&#xff1a; Eureka Server&#xff1a;注册中心服务端&#xff0c;用于维护和管理注册服务列表。Eureka Client&#xff1a;注册中心客户端&#xff0c;向注册中心注册服务的应用都可以叫做Eureka Client&#xff08;包括Eureka Server本身&#x…

C - Maximize GCD(简单数论)

C - Maximize GCD 给定长度为n,(2≤3105)n, (2 \le 3 \times 10 ^ 5)n,(2≤3105)的数组a,(1≤ai≤3105)a, (1 \le a_i \le 3 \times 10 ^ 5)a,(1≤ai​≤3105)&#xff0c;一个数字K,(1≤K≤1018)K, (1 \le K \le 10 ^{18})K,(1≤K≤1018)&#xff0c; 我们可以对数组aaa进行…

推荐.neter常用优秀开源项目系列之二

.net社区有很多优秀的开源项目&#xff0c;我们今天再推荐12个开源项目&#xff1b;1. Domain-Driven-Design-ExampleDDD 示例 挺不错的。github https://github.com/zkavtaskin/Domain-Driven-Design-Example2.SmartStoreNET开源的电商项目github https://github.com/smartsto…

Zookeeper: Zookeeper架构及FastLeaderElection机制

本文转发自技术世界&#xff0c;原文链接 http://www.jasongj.com/zookeeper/fastleaderelection/ 一、Zookeeper是什么 Zookeeper是一个分布式协调服务&#xff0c;可用于服务发现&#xff0c;分布式锁&#xff0c;分布式领导选举&#xff0c;配置管理等。 这一切的基础&am…

I. Rise of Shadows(类欧几里得)

I. Rise of Shadows 一天有HHH个小时&#xff0c;MMM分钟&#xff0c;问&#xff0c;有多少个整数分钟&#xff0c;满足时针与分针的角度≤α\le \alpha≤α&#xff0c;α2πAHM\alpha \frac{2 \pi A}{HM}αHM2πA​。 ∑i0H−1∑j0M−1[∣2π(iMj)HM−2πjM∣≤2πAHM]∑i0…

与时俱进 | 博客现已运行在 .NET Core 3.0 及 Azure 上

点击上方蓝字关注“汪宇杰博客”导语9月23日&#xff0c;微软正式发布了 .NET Core 3.0&#xff0c;这个版本具有大量新功能和改进。我也在第一时间将自己的博客网站更新到了 .NET Core 3.0&#xff0c;并且仍然跑在微软智慧云 Azure 国际版的应用服务上。本文总结了我在博客迁…

Zookeeper:基于Zookeeper的分布式锁与领导选举

本文转发自技术世界&#xff0c;原文链接 http://www.jasongj.com/zookeeper/distributedlock/ 1、Zookeeper特点 1.1 Zookeeper节点类型 如上文《Zookeeper架构及FastLeaderElection机制》所述&#xff0c;Zookeeper 提供了一个类似于 Linux 文件系统的树形结构。该树形结构…