手把手教你手写一个最简单的 Spring Boot Starter
欢迎关注微信公众号:「Java之言」技术文章持续更新,请持续关注......
- 第一时间学习最新技术文章
- 领取最新技术学习资料视频
- 最新互联网资讯和面试经验
何为 Starter ?
想必大家都使用过 SpringBoot,在 SpringBoot 项目中,使用最多的无非就是各种各样的 Starter 了。那何为 Starter 呢?你可以理解为一个可拔插式的插件(组件)。或者理解为场景启动器。
通过 Starter,能够简化以前繁杂的配置,无需过多的配置和依赖,它会帮你合并依赖,并且将其统一集成到一个 Starter 中,我们只需在 Maven 或 Gradle 中引入 Starter 依赖即可。SpringBoot 会自动扫描需要加载的信息并启动相应的默认配置。例如,如果你想使用 jdbc 插件,你只需引入 spring-boot-starter-jdbc 即可;如果你想使用 mongodb,你只需引入 spring-boot-starter-data-mongodb 依赖即可。
SpringBoot 官方提供了大量日常企业应用研发各种场景的 spring-boot-starter 依赖模块。这些依赖模块都遵循着约定成俗的默认配置,并允许我们根据自身情况调整这些配置。
总而言之,Starter 提供了以下功能:
- 整合了模块需要的所有依赖,统一集合到 Starter 中。
- 提供了默认配置,并允许我们调整这些默认配置。
- 提供了自动配置类对模块内的 Bean 进行自动装配,注入 Spring 容器中。
Starter 命名规则
Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name},例如 spring-boot-starter-data-mongodb。Spring 官方建议,非官方 Starter 命名应遵循 {name}-spring-boot-starter 的格式,例如,myjson-spring-boot-starter。
自定义一个 Starter
了解了 Starter 的含义以及应用场景后,我们可以尝试手写一个 Starter,加深对它的了解以及能在实际工作中,开发出自己的 Starter,提高我们的开发效率。
可能有人会问 Starter 能干嘛呢?其实在我们的日常开发工作中,总有一些独立于业务系统之外的配置模块,它是可以在不同项目中进行复用的。如果在每个项目中都编写重复的模块代码,不仅浪费时间和人力,而且还和项目耦合。所以我们将这些可独立于业务代码之外的功能配置模块封装成一个 Starter,在需要用到此功能模块的项目中,只需要在其 pom.xml 文件中引用依赖即可,SpringBoot 帮我们完成自动装配,而且我们还可以在配置文件中调整 Starter 中默认的配置信息。
假设我们现在需要实现这样一个功能:
- 根据用户提供的 Java 对象,将其转换为 JSON 形式,并且在 JSON 字符串中添加指定的前辍和后辍。
- 用户可以动态改变前辍和后辍,即可在 yml 或 properties 配置文件中自定义。
举个栗子,假如用户输入下面这个类的对象 person:
public class Person {
private String name;
private int age;
private String address;
public Person(String name, int age, String address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
// 省略get和set方法
}
Person person = new Person("Mr.nobody", 18, "拉斯维加斯");
并假设用户在 application.yml 配置文件中配置的前辍为 @,后辍为 %,则最终生成的字符串为:
@{"address":"拉斯维加斯","age":18,"name":"Mr.nobody"}%
首先新建一个 Maven 工程(当然也可以其他类型例如 Gradle 工程),在 pom.xml 文件中引入如下依赖。fastjson 依赖是我们业务用到将 Java 对象转换为 JSON 字符串;spring-boot-configuration-processor 依赖是可选的,加入此依赖主要是打包时,自动生成配置元信息文件 META-INF/spring-configuration-metadata.json,并放入到 jar 中。方便使用者了解到一些配置元信息。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nobody</groupId>
<artifactId>myjson-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>myjson-spring-boot-starter</name>
<description>Demo project for Spring Boot Starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.8.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
</dependencies>
</project>
业务处理类,实现 Java 对象转换为带有指定前后缀的 JSON 字符串。
package com.nobody.myjson.service;
import com.alibaba.fastjson.JSON;
/**
* @Description 业务处理类
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
public class MyJsonService {
// 前缀
private String prefixName;
// 后缀
private String suffixName;
/**
* 将Java对象转为带有指定前后缀的JSON字符串
*
* @param o 需要转换的Java对象
* @return 转换后的字符串
*/
public String objectToMyJson(Object o) {
return prefixName + JSON.toJSONString(o) + suffixName;
}
public String getPrefixName() {
return prefixName;
}
public void setPrefixName(String prefixName) {
this.prefixName = prefixName;
}
public String getSuffixName() {
return suffixName;
}
public void setSuffixName(String suffixName) {
this.suffixName = suffixName;
}
}
配置类,定义需要的配置信息和默认配置项,并指明关联配置文件的配置项前缀。它可以把相同前缀的配置信息通过配置项名称映射成实体类的属性中。
package com.nobody.myjson.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Description 配置类(类名一般为模块名+Properties) nobody.json为Starter使用者通过yml配置文件动态修改属性值的变量名前缀
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
@ConfigurationProperties(prefix = "nobody.json")
public class MyJsonProperties {
// Starter使用者没在配置文件中配置prefixName属性的值时的默认值
public static final String DEFAULT_PREFIX_NAME = "@";
// Starter使用者没在配置文件中配置suffixName属性的值时的默认值
public static final String DEFAULT_SUFFIX_NAME = "@";
private String prefixName = DEFAULT_PREFIX_NAME;
private String suffixName = DEFAULT_SUFFIX_NAME;
public String getPrefixName() {
return prefixName;
}
public void setPrefixName(String prefixName) {
this.prefixName = prefixName;
}
public String getSuffixName() {
return suffixName;
}
public void setSuffixName(String suffixName) {
this.suffixName = suffixName;
}
}
自动装配类,使用 @Configuration 和 @Bean 来进行自动装配,注入 Spring 容器中。
package com.nobody.myjson.config;
import com.nobody.myjson.service.MyJsonService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description 自动装配类
* @Author Mr.nobody
* @Date 2021/2/27
* @Version 1.0
*/
@Configuration // 标识此类是配置类
@ConditionalOnClass(MyJsonService.class) // 表示只有指定的class在classpath上时才能被注册
@EnableConfigurationProperties(MyJsonProperties.class) // 激活@ConfigurationProperties
public class MyJsonConfiguration {
private MyJsonProperties myJsonProperties;
// 自动注入配置类
public MyJsonConfiguration(MyJsonProperties myJsonProperties) {
this.myJsonProperties = myJsonProperties;
}
// 创建MyJsonService对象,注入到Spring容器中
@Bean
@ConditionalOnMissingBean(MyJsonService.class)
public MyJsonService myJsonService() {
MyJsonService myJsonService = new MyJsonService();
myJsonService.setPrefixName(myJsonProperties.getPrefixName());
myJsonService.setSuffixName(myJsonProperties.getSuffixName());
return myJsonService;
}
}
在 src/main/resources/META-INF目录下新建 spring.factories 文件,输入以下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nobody.myjson.config.MyJsonConfiguration
SpringBoot 项目启动时,类加载器会从 META-INF / spring.factories 加载给定类型的工厂实现的完全限定类名。也就是说类加载器得到工程中所有 jar 包中的 META-INF/spring.factories 文件资源,从而得到了一些包括自动配置相关的类的集合,然后将它们实例化,放入 Spring 容器中。
最终项目结构如下:

在开发工具 IDEA 通过 Maven 的 install 命令进行构建打包。或者在项目的目录下,打开命令行窗口,使用mvn install命令进行构建打包。打包后,会在工程的 target 目录下生成一个 jar 包,并且在 maven 本地仓库也会生成相应的 jar 包。




使用自定义的 Starter
经过上面几个步骤,我们自定义的 Starter 就开发好了,以下是在其他工程进行引入使用。在需要引用此 Starter 的工程的 pom.xml 文件中引入此依赖。
<dependency>
<groupId>com.nobody</groupId>
<artifactId>myjson-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
刷新依赖,就能在项目的依赖库中看到此依赖了。

展开,还能查看此 Starter 可以配置的属性项有哪些,如下:

然后在需要用到的类中进行注入使用即可。
package com.nobody.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.nobody.domain.Person;
import com.nobody.service.MyJsonService;
@RestController
@RequestMapping("demo")
public class DemoController {
// 注入我们Starter中的服务类
@Autowired
private MyJsonService myJsonService;
@GetMapping()
public String test() {
Person p = new Person("Mr.nobody", 18, "拉斯维加斯");
// 调用服务方法
return myJsonService.objectToMyJson(p);
}
}
启动项目,在浏览器中访问此接口,得到如下结果:

如果我们在 application.yml 文件中添加以下配置信息,然后再访问接口的结果如下,也验证了我们可以自定义 Starter 中默认的配置项。
nobody:
json:
prefixName: HH
suffixName: KK

此演示项目已上传到Github,如有需要可自行下载,欢迎 Star 。
https://github.com/LucioChn/myjson-spring-boot-starter
手把手教你手写一个最简单的 Spring Boot Starter的更多相关文章
- 【spring】-- 手写一个最简单的IOC框架
1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...
- 手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...
- AQL Subset Compiler:手把手教你如何写一个完整的编译器
项目地址:https://github.com/laiy/Awesome-Complier. 转载请注明出处. 前言 这是学校里编译原理课程的大作业,此Project十分适合编译原理的学习,让基本不听 ...
- 手写一个超简单的Vue
基本结构 这里我根据自己的理解模仿了Vue的单文件写法,通过给Vue.createApp传入参数再挂载元素来实现页面与数据的互动. 其中理解不免有错,希望大佬轻喷. 收集数据 这里将Vue.creat ...
- 摊牌了!我要手写一个“Spring Boot”
目前的话,已经把 Spring MVC 相关常用的注解比如@GetMapping .@PostMapping .@PathVariable 写完了.我也已经将项目开源出来了,地址:https://gi ...
- 手写一个最迷你的Web服务器
今天我们就仿照Tomcat服务器来手写一个最简单最迷你版的web服务器,仅供学习交流. 1. 在你windows系统盘的F盘下,创建一个文件夹webroot,用来存放前端代码. 2. 代码介绍: ( ...
- 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理
摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...
- 教你如何使用Java手写一个基于链表的队列
在上一篇博客[教你如何使用Java手写一个基于数组的队列]中已经介绍了队列,以及Java语言中对队列的实现,对队列不是很了解的可以我上一篇文章.那么,现在就直接进入主题吧. 这篇博客主要讲解的是如何使 ...
- 利用SpringBoot+Logback手写一个简单的链路追踪
目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...
随机推荐
- 【bzoj 2597】[Wc2007]剪刀石头布(图论--网络流 最小费用最大流)
题目:在一些一对一游戏的比赛(如下棋.乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况.有的时候,无聊的人们会津津乐道于统计有多少这样的 ...
- B、小花梨的三角形(解题报告)
通过set进行标记(思想很简单,实现起来有点容易错)set(红黑树:效率高) 思路: 对行列和长度进行枚举: 对三个顶点进行排序 储存顶点后计数输出 #include<iostream> ...
- fzu2204 7
Problem Description n个有标号的球围成一个圈.每个球有两种颜色可以选择黑或白染色.问有多少种方案使得没有出现连续白球7个或连续黑球7个. Input 第一行有多组数据.第一行T表 ...
- python中schedule模块的简单使用 || importlib.import_module动态导入模块
1 import schedule 2 import time 3 4 def start(): #定义一个函数 5 print("****") 6 7 8 if __name__ ...
- 用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很厉害?
前言 micrometer中自带了很多其他框架的指标信息,可以很方便的通过prometheus进行采集和监控,常用的有JVM的信息,Http请求的信息,Tomcat线程的信息等. 对于一些比较活跃的框 ...
- k8s二进制部署 - master节点安装
下载kubernetes服务端 [root@hdss7-21 ~]# cd /opt/src [root@hdss7-21 src]# wget https://dl.k8s.io/v1.15.2/k ...
- 网络之一次http请求的完整过程
关于网络的知识平时可能真正用的比较少,但是有一些点还是需要总结的: 完成一次http请求要大致可以分为7个步骤: 一.TCP三次握手 第一次握手:建立连接.客户端发送连接请求报文段,将SYN位置为1, ...
- 牛客多校第三场J LRU management(双向链表)题解
题意: 给一个长度为\(m\)的队列,现给定以下操作: \(opt=0\),插入一个串,如果不在队里直接插入栈尾,如果超出\(m\)删队首:在队里就拿出来重新放到队尾,返回\(v\)值. \(opt= ...
- keras自定义网络层
在深度学习领域,Keras是一个高度封装的库并被广泛应用,可以通过调用其内置网络模块(各种网络层)实现针对性的模型结构:当所需要的网络层功能不被包含时,则需要通过自定义网络层或模型实现. 如何在ker ...
- Google Developer Days 2019 & GDD
Google Developer Days 2019 2019 Google 开发者大会 GDD Google Developer Days https://events.google.cn/intl ...