前言:
通过这篇文章 你能了解SpringBoot Starter的概念和用处 并且通过实战 自定义一个SpringBoot Starter 来实现数据脱敏的功能
加油 搬砖人~ 今天不学习,明天变垃圾。
一、什么是SpringBoot Starter?用SpringBoot Starter能带来什么好处?
学习一项技术的前提是搞清自己的需求和这项技术能为我们解决什么样的实际问题。
那我们的需求是了解SpringBoot Starter的特性,利用Starter 的特性 学会自定义Starter ,最后在自己的项目中使用自定义的Starter来简化配置 快速使用第三方组件或者自己提供的一些功能。
1、SpringBoot Starter概念的解释:
SpringBoot Starter是 Spring Boot框架中的一个关键概念,用于简化Spring应用程序的依赖管理和配置。
Starter通常包含了要使用特定功能所需的所有依赖项、配置和自动配置。
通俗点说Starter就是SpringBoot定义的一种规范,在Spring中我们想使用某个中间件无外乎下面三步
①、配置好需要的bean
②、将配置好的bean注册到Spring的IoC容器 (注意:IoC的全称是Inversion of Control,而不是Inverse of Control,IoC的写法采用了小写的"o",以与"of"之间的大写字母进行区分)
③、在代码中 使用@Resource 或者@Autowired从容器中注入需要的bean 开始使用
SpringBoot Starter 可以帮助我们简化①、②步的操作
也就是说使用SpringBoot Starter 我们可以将精力更多的放在功能的使用上 而不用过多的去关注bean的配置
2、用SpringBoot Starter有什么好处:
能够简化中间件的配置、将我们需要的bean自动配置好注入容器 方便开发者直接拿来使用
在企业中使用Starter封装中间件 能减少重复配置 拿来即用 提高开发效率
二、熟悉Spring和配置相关的注解
要想自定义Starter 就要先理解SpringBoot和配置相关的注解
①、 @ConfigurationProperties
@ConfigurationProperties注解用于将外部配置文件(如application.properties或application.yml)中的属性值绑定到Java类的属性上
还支持配置前缀(prefix = “xxx”)
例如:
下面是个properties文件
desensitization.enabled=true
上面的配置能够直接配置到下面的java类中
@Data
@ConfigurationProperties(prefix = "desensitization")
public class DesensitizationProperties {
private Boolean enabled;
}
②、@EnableConfigurationProperties(xxx.class)
这个注解是用来配合@ConfigurationProperties注解一起使用的 括号内可以设置你的配置类
例如 @EnableConfigurationProperties(DesensitizationProperties.class)
把@EnableConfigurationProperties注解放在某个配置类上或者启动类上 即可生效、能够将DesensitizationProperties类注册到Spring容器内
③、@Import
导入配置类:使用@Import注解可以将其他配置类导入到当前配置类中
导入组件:使用@Import注解导入一个普通的Java类时,该类将会被注册为Spring容器管理的Bean
④、@EnableAutoConfiguration
自动配置Spring环境:自动配置Spring Boot应用程序的各种配置项,如数据源、数据访问框架、Web框架等。通过这种方式,可以简化配置和启动过程,快速搭建和运行Spring Boot应用程序。
激活自动配置类:@EnableAutoConfiguration会根据项目的classpath中存在的类、依赖和配置文件等信息,自动搜索并加载满足条件的自动配置类,将其添加到Spring应用程序上下文中
如下图所示 SpringBoot有这么多默认的配置类
接下来我们自定义Starter 就会用到
@ConfigurationProperties
@EnableConfigurationProperties
三、感受Starter带给我们的方便 自己实现一个Starter 实现数据脱敏功能
简单说一下 数据脱敏
例如: 调用获取人员信息接口 人员信息有手机号
数据库存的是 18888888888 接口返回给前端的是 188****8888 隐藏部分敏感数据 就叫数据脱敏
在实际项目中有时候会用到这个功能
那我们就实现一个简单的数据脱敏功能 并且封装成SpringBoot的starter 然后在项目中去使用它
①、先实现数据脱敏功能(这个不重要 只说下大概思路)
使用自定义的Jackson序列化器结合Hutools工具 实现数据脱敏
大致的步骤是:
- 1、创建一个自定义的Jackson序列化器,继承com.fasterxml.jackson.databind.JsonSerializer抽象类。
重写serialize方法,实现ContextualSerializer接口 重写createContextual方法 - 2、制定一个注解用于标记需要脱敏的字段
- 3、当使用@RestController或者@ResponseBody注解将Controller层返回对象转换为JSON时,Jackson会自动使用自定义的序列化器对标注了自定义注解的字段进行脱敏处理
先看下效果
下面是段测试代码
@RestController
@RequestMapping(value = "/test")
public class TestController {
@RequestMapping(value = "/a", method = RequestMethod.GET)
public User test() {
User user = new User();
String phone = "18888888888";
user.setPhone(phone);
return user;
}
}
@Data
class User {
@Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
private String phone;
}
浏览器访问返回结果
②、给自己的Starter取个牛逼的名字
Spring Boot官方发布的Starter名称规范则是:spring-boot-starter-xxx
官网推荐我们自己的starter命名规则为:xxx-spring-boot-starter
为了见名知义 就取个直观点的名字 就叫 data-desensitization-spring-boot-starter (够长吧^_^ 量了下 起码有5厘米!)
③、创建data-desensitization-spring-boot-starter 项目 (继续往下看 有源码可以去下载)
1、创建一个maven项目并在resources目录下 新建 META-INF目录 再在META-INF目录 下新建 spring.factories文件
(spring.factories的目的是 在Spring Boot应用程序启动时,Spring框架会扫描spring.factories文件,解析其中的配置,自动加载和应用这些自动配置类 )
2、然后再新建 spring-configuration-metadata.json文件 (这个文件是给配置文件添加注释用的,可以自己新建 也可以引入spring-boot-configuration-processor这个maven依赖来通过在配置类的属性上添加javadoc注释 然后打包的时候就会自动根据javadoc注释生成 spring-configuration-metadata.json文件 )
最后效果就是你在properties或者yml里面写配置信息的时候会有提示 像下面这样:
3、引入基础的 Spring Boot Starter依赖
<!-- 添加Spring Boot Starter依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
4、创建自动配置类
5、在spring.factories文件里 配置自己的配置类的全路径类名
使用\ 是为了方便阅读 如果有多个 就用 ,\
6、maven install 打包 就能在其他项目中引用了
下面是打包好的Starter maven坐标
<dependency>
<groupId>org.example</groupId>
<artifactId>data-desensitization-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
自己实现一个starter 实现数据脱敏功能 源码 点击下载
推荐自己去看看源码 很容易看懂 看过源码之后 可以自己参考源码再敲一遍
然后自己新建一个springboot项目 去验证下自己写的starter好不好用
测试项目的源码 点击下载
需要先下载 数据脱敏的 starter install 到本地仓库之后 再去启动测试项目
四、讲个小故事 自己动手把一个第三方工具封装成starter使用
提供个思路 例如 自己再写个 线程池 Starter 自己实现一个线程池 注入到容器 提供给别的项目用 再写一些线程池的参数配置 支持外部配置线程池子关键参数等功能
这里再结合现实的一个工作场景来写个使用Starter的小故事加深大家的印象
故事开始
你是小黑今年刚毕业 在投了888份简历之后,最终拿到了二哈公司的offer。 工资9000块一个月。
二哈公司有个 微服务项目的 定时任务模块 功能比较单一 每次新增定时任务都需要改动大量代码 而且任务失败没有通知机制 已经无法满足日益复杂的定时任务需求。
这天你的开发组长秀逗找到你。
秀逗: 小黑 我们公司 狗头项目的定时任务模块现在需要升级 硬性要求是 必须要有任务失败的通知机制 并且定时任务模块要和业务代码完成解耦。给你2天时间把这个活干了,我最近比较忙 要不然这个活就我自己干了!
小黑: 心里默念(2天?逗我呢 我光去实现一个通知机制就得1天了吧 还得去考虑解耦 还有30多个微服务要配置 2天确定不是逗我?) 额~~
算了 刚入职 就说2天干不了 别被公司优化了 , 额 ~ 好的秀逗组长 我这就去干~ 。
此时你内心开始思考,就给2天 而且狗头项目有30多个微服务 自己去实现肯定是不现实了
于是打开百度 查看下有哪些定时任务的轮子
于是你查到了 LTS 、SchedulerX、xxl-job、PowerJob、QuartZ
然后去对比下 又发现SchedulerX要收费 , LTS没有通知告警机制
经过对比 你发现xxl-job貌似不错
好的 接下来你又去部署了xxl-job服务端, 在项目中依赖了 xxl-job的客户端依赖 去配置了xxl-job的配置项 ,小试了一把 呦 这个不错 挺好用
就是你了 xxl-job 你就是我要找的那个靓仔 , 一天过去了~
开始改造30多个微服务 让他们都成为xxl-job的执行器 这个时候你发现 竟然要配置30多份配置文件 要疯了
这个时候时间来到了第二天中午 ~
秀逗: 小黑昨天给你的任务搞得怎么样了?
小黑:额 搞 搞 搞 的差不多了 ~ ~
秀逗: 不错 下午搞好了 给我演示看下。
小黑: ~ ~ ~ 好。
此时你心中: WTF 30多个 微服务配置想搞死我 还有几个小时就下午了…
正在煎熬的时候你突然想到了 曾经看到过一篇文章 名字叫做 学习自定义SpringBoot Starter组件 (超详细的图文教程,从理论到实战)
你茅塞顿开 ,想到可以使用starter 把配置信息封装一下 每个微服务只要依赖封装的Starter 就可以不用去重复配置了
太棒了 现在就干
①、
新建微服务 goutou-job-spring-boot-starter
pom文件
<?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>
<groupId>com.goutou</groupId>
<artifactId>goutou-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<!--xxl-job 依赖-->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
<!--引入这个依赖,对spring.factories 文件添加 注释-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.2.RELEASE</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>
注意 <optional>true</optional>
的含义是 引用这个依赖 的项目 不会把 带 <optional>true</optional>
的依赖引入到项目中
编写配置类 注册XxlJobSpringExecutor 到Spring容器 xxl-job运行时需要这个bean
package com.goutou.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(XxlJobAutoConfiguration.class)
@Slf4j
public class XxlJobAutoConfiguration {
@Bean
public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties properties) {
log.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(properties.getAdminAddresses());
xxlJobSpringExecutor.setAppname(properties.getAppname());
xxlJobSpringExecutor.setAddress(properties.getAddress());
xxlJobSpringExecutor.setIp(properties.getIp());
xxlJobSpringExecutor.setPort(properties.getPort());
xxlJobSpringExecutor.setAccessToken(properties.getAccessToken());
xxlJobSpringExecutor.setLogPath(properties.getLogPath());
xxlJobSpringExecutor.setLogRetentionDays(properties.getLogRetentionDays());
return xxlJobSpringExecutor;
}
}
编写配置文件
package com.goutou.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "goutou.job")
@Data
public class XxlJobProperties {
/**
* xxl-job 后台地址
*/
private String adminAddresses;
/**
* 可选
*/
private String accessToken;
/**
* 执行器名称 建议写 当前应用名称前缀-xxl-job
*/
private String appname;
/**
* 可选
*/
private String address;
/**
* 可选
*/
private String ip;
/**
* 可选
*/
private int port;
/**
* 日志地址
*/
private String logPath;
/**
* 日志保存时长 单位 天
*/
private int logRetentionDays;
}
编写spring.factories 文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.goutou.config.XxlJobAutoConfiguration
你打包到本地仓库 先测试 没问题之后 上报狗头公司经过批准后 可以deploy 到公司的maven仓库
整个项目的代码结构如下:
你最终在30多个微服务 都引入了
<dependency>
<groupId>com.goutou</groupId>
<artifactId>goutou-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
并在 对应微服务的yml添加了 少量配置 就按时完成了定时任务模块的改造
秀逗组长在下午的评审中对你的工作效率赞赏有加,于是便找你谈话~
秀逗:小黑 ,我就知道你小子可以,效率挺高,不愧是我的人~ 狗头项目目前 没有对分布式事务进行处理 只有部分复杂业务通过回调机制实现简单的数据回滚 你给狗头项目写一个分布式事务组件 用来支持狗头项目的分布式事务 提高狗头项目的数据一致性 今天周三了 周五下午完成 给我汇报吧 主要是我太忙了 要不然我就自己写了!
小黑: 额(额 额~ ~ ~ 未完待续 ~ ~ ~)
所有的学习都是站在巨人的肩膀上:
参考文章:
https://javaguide.cn/system-design/schedule-task.html#powerjob
https://zhuanlan.zhihu.com/p/515380149
https://www.cnblogs.com/xiaoymin/p/14131982.html