- 联系方式:1761430646@qq.com
- 菜狗摸索,有误勿喷,烦请联系
1. 自定义starter
1.1 前言
- 这个自定义
starter
过程是跟着 B 站@黑马程序员的视频做出来的 - 链接:黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)_哔哩哔哩_bilibili
1.2 要求
-
做一个系统访客独立 IP 访问次数的自定义
starter
-
并在后台每10 秒输出一次监控信息
-
业务分析
- 数据记录中介:Map / Redis
- 功能触发位置:每次请求
- 配置项:
- 输出频度,默认 10s 一次
- 数据特征:累计 / 阶段,默认为累计
- 输出格式:详细 / 极简
- 其他:
- yml 的配置提示功能
1.3 功能实现
1.3.1 项目搭建
-
由于这个功能比较简单,只牵扯到 web 场景,所以正常搭建 SpringBoot 项目后导入一个
web
依赖即可 -
切记把 SpringBoot 自带的 maven 构建依赖给删掉,要不然会出大错
1.3.2 访问 IP 计数功能实现
-
二话不说,先创建对应的业务接口以及抽象方法,并添加实现类
-
由于 IP 的获取我们需要通过
HtttpServerletRequest
对象获取,现在没有,就得通过注解@Autowried
注入一个,并且 IP 统计的存储是计划使用Map
(可以使用Redis
等其他实现,这里重点是stater
的自定义过程,就使用Map
来简化了),得手动创建一个- 这里的 Map 可能有线程安全问题,最好是通过注入的形式来获取,这里为了简化才直接
new
出来的
- 这里的 Map 可能有线程安全问题,最好是通过注入的形式来获取,这里为了简化才直接
-
访问 Ip 统计业务实现,这个比较无脑,直接拿并存就好
-
到这里其实 IP 计数业务接口已经写好,后面是自定义
starter
的关键了 -
创建一个
IpCountAutoConfiguration
配置类,并加以配置 -
在
resources
目录下创建META-INF
目录,然后创建spring.factories
文件,并把我们刚刚的自动配置类的全路径类名作为key
为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的value
-
这里是自定义
starter
的关键,切记切记 -
现在其实一个最基本的实现 IP 统计的自定义
starter
已经做出来,通过Maven
工具的先clean
后install
操作,我们即可在本地的 Web 项目开发中导入此starter
,直接在Controller
层注入IpCountService
bean,调用方法便可自动增加了访问 IP 统计的功能
1.3.3 展示 IP 访问数据功能实现
-
照仿上一小节的功能实现,我们先创建对应的业务接口以及其实现类,但是这里为了简化(–偷个懒)并且由于存储 IP 的容器由于不是注入的,如果是新建业务接口类的话是拿不到,所以这里直接在上一节的已有接口中增加展示 IP 访问数据功能的实现
-
接着就是 IP 数据展示的功能实现了,其实也挺无脑的,直接拿出来并打印在控制台即可,最重要的是格式的控制
- 好了,IP 数据展示功能接口已经做完了
1.3.4 配置拦截路径
-
其实这个配置拦截路径可以不用实现,但是为了方便当别人使用到我们自定义的
starter
时,无需手动注入IpCountService
bean,再通过调用count()
方法再来实现 IP 统计功能的实现,而是为了导入依赖后啥都不用干就有此功能的快捷,在这里我们还是给它下配置拦截路径(使用者快乐就好) -
下面分析下怎么做
-
对于以前我们使用
Serverlet
开发的项目,如果进行 IP 统计的话,通常都是在filter
中利用AOP
的思想做 -
在这里的自定义
starter
项目中,我们也可以通过实现filter
来做,当然也可以通过interceptor
来做,本次我们就通过interceptor
来搞 -
新建一个专门存放
interceptor
的包,并在其中新建关于 IP 的interceptor
类,实现HandlerInterceptor
接口,重写preHandle
方法 -
注入
IpCountService
bean,调用其count()
方法实现 IP 统计功能 -
创建一个实现
WebMvcConfigurer
接口的SpringMvc
配置类,并在其中配置刚刚的拦截器加载到容器中,且设置拦截路径–这一小节最重要的一点,属于SpringBoot如何配置Interceptor
的内容)- 注意使用
@Configuration
标注此类
- 注意使用
-
切记最后要在自动配置类中通过
@Import
导入刚刚的SpringMvc
配置类,要不然这个功能以及刚刚做的一大堆功夫就相当于YY了
1.3.5 定时数据日志功能实现
-
对 IP 进行统计后的数据得进行定时展示,有利于使用者监控,在这里我们就是用简单的控制台输出即可
-
实现很简单
-
首先在自动配置类中开启
SpringBoot
的定时展示功能 -
在数据展示的功能接口上通过
cron
表达式配置数据显示周期,在这里即为在show()
方法上配置(先默认设置每 5 秒进行一次数据展示) -
完事
1.3.6 配置值动态获取实现
-
对于上述功能的实现,我们发现我们都是通过默认值来控制其数据显示周期,数据展示格式,永久不周期内重置数据,拦截路径固定死等
-
这样就太死板了,所以我们得做一个属性配置类,为使用者提供一个接口来动态调整
-
抽取所有可调属性,重新构造成一个新类
具体内容如下
@Component("ipCountProperties") @ConfigurationProperties(prefix = "tools.ip") public class IpCountProperties { /** * 日志显示周期 */ private long cycle = 10L; /** * 是否周期内重置日志 */ private boolean cycleReset = false; /** * 日志输出模式 detail:详细模式 simple:极简模式 */ private String model = LogModel.DETAIL.value; /** * 拦截路径 */ private String interceptorPath = "/**"; public enum LogModel { /** * 详细日志模式 */ DETAIL("detail"), /** * 简单日志模式 */ SIMPLE("simple"); private String value; private LogModel(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public long getCycle() { return cycle; } public void setCycle(long cycle) { this.cycle = cycle; } public boolean isCycleReset() { return cycleReset; } public void setCycleReset(boolean cycleReset) { this.cycleReset = cycleReset; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } }
- 注意:在这里通过
@ConfigurationProperties
配置其属性值前缀,通过@Component
来加载此对象到 Spring 容器中,至于为什么不是通过@EnableConfigurationProperties
来加载,这里面会有个坑,后面再讲
- 注意:在这里通过
-
接下来就是在所有可以用到这些属性值的地方注入此
bean
,然后动态设置值
-
切记,还得在自动配置类手动导入这个
IpCountProperties
类,因为我们默认没有开启扫描当前包功能,不主动导入就相当于做了个寂寞 -
完事
1.4 配置 yml 时提示功能实现
-
现在通过配置文件进行动态调值时,会发现配置时不会像别人那样自动弹出提示,虽然说我们也知道有哪些值可以配置,它们代表什么含义,但是使用者可不知道
-
当然也可以建立相对应的
starter
配置文档,但是在配置属性时有一个提示功能无疑会让使用者感觉舒服很多 -
下面就进行提示功能的实现
-
它是通过一个配置文件来实现的,在抽取属性成为一个类时,Idea 也在冒出一个提示
-
虽然不知道为什么会有这个提示出现,但是点击
Open Ducementation
,会发现进入到SpringBoot
的官方文档里,那提示我们要加载一组依赖 -
而本次的提示功能实现也是需要这组依赖的,所以先手动导入其对应依赖
-
然后
Maven
工具的先clean
后install
命令,会发现编译后的META-INF
目录会多生成一个名为spring-configuration-metadata.json
的文件(如果不加入上述依赖,通过Maven
工具运行同样的命令,是不会有这个文件生成的,你们可以试试) -
然后我们拷贝一份
spring-configuration-metadata.json
文件到我们的META-INF
目录下,并把源文件给删掉,以及上面导入的这组依赖给取消掉 -
打开这个
spring-configuration-metadata.json
文件,我们会发现这是由我们之前配置的IpCountProperites
类的属性注释信息构成的 -
现在在
yml
文件里配置属性,就会有提示功能了 -
对于
model
属性,也即输出模式的选定,其值是通过枚举类是枚举的,在别人开发的starter
中,对于枚举的属性值的配置,会有一个专门的提示功能,但是现在我们没有 -
下面来做这个枚举的提示功能,其实也很简单,配置文件的提示功能归根到底是
spring-configuration-metadata.json
文件中的内容生成的,我们只需进到这个文件,找到hints
区域进行值的配置即可 -
配置完后就可以了,效果马上出来了
-
完事
- 通过
Maven
工具的先clean
后install
操作,将此模块导入我们的Maven
仓库
1.5 实践
-
找到一个 Web 项目
-
加入我们刚刚自定义
starter
的依赖 -
启动测试,会发现我们做的 IP 统计功能就直接有了
-
在配置文件中也可以看到对应的配置提示信息
1.6 老鼠坑
-
在上面的工作中,我们把
xxxProperties
的实例对象加载到bean
容器是通过@Component(名称)
注解以及在自动配置类中通过@Import
导入此xxxProperties
类生成的 -
那时就留了个疑问,为什么不是使用
@EnableConfigurationProperties
搭配呢?官方也是这样开发的呀 -
这主要是考虑到我们的业务特点,因为对于定时周期的取值,我们是通过
#{bean名称.属性}
取出来的 -
但是对于使用
@EnableConfigurationProperties
注册 bean 的名称,是由一个专门的 bean名称生成器来生成的,也就是不受我们的管控,通过这个注解注册的 bean 的名称格式可以通过下面一个例子得出 -
总结一下,其生成的名称格式为
属性匹配前缀-全路径类名
,这也是最重要的一点,因为#{bean名称.属性}
是由.
作为分割的,那么对于上述例子,SpringBoot 就会认为person
才是这个bean
的名称,后面的是它的属性值(可能是个对象套娃,所以会有多个.
) -
所以所以,如果通过
@EnableConfigurationProperties
注册此xxxProperties
bean的话,我们是拿不到值的,所以只能通过@Component
去固定这个 bean 的名称方便我们取值 -
这也告诉我们业务需求第一,不一定要遵守开发习惯