【原理篇】四、自定义starter
文章目录
- 1、案例分析
- 2、业务功能的实现
- 3、中途调试
- 4、开启定时任务打印报表
- 5、引入属性配置类,写活业务参数配置
- 6、拦截器
- 7、开启yml提示功能
做一个记录系统访客独立IP访问次数的功能,并把它自定义成一个starter,实现:在现有项目中引入这个starter后,则这个项目就有了访客IP统计功能,且通过配置可以去改这个功能。
1、案例分析
功能:记录系统访客独立IP访问次数
问题一:数据记录位置,数据为key-value形式,可考虑:
- Map
- Redis
问题二:功能触发位置的:每次web请求,用拦截器,实现步骤:
- ① 步骤一:降低难度,主动调用,仅统计单一操作访问次数(例如查询)
- ② 步骤二:开发拦截器
问题三:给哪些业务参数(用户的可配置项)
- ① 输出频度,默认10秒
- ② 数据特征:累计数据 / 阶段数据,默认累计数据
- ③ 输出格式:详细模式 / 极简模式
下面新建一个新模块来做这个starter,起名ip_spring_boot_starter(注意命名规范,非Spring官方做的,名称在前,starter单词在后),删掉不用的东西,比如单测坐标、打包插件等。
2、业务功能的实现
主要功能的大体实现:
public class IpCountService {//计数集合private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();//当前的HttpRequest对象的注入工作由使用这个starter的工程去自动装配@Autowiredprivate HttpServletRequest request;public void count(){String ipAddress = request.getRemoteAddr();if(ipCountMap.containsKey(ipAddress)){ipCountMap.put(ipAddress,ipCountMap.get(ipAddress) + 1);}else{ipCountMap.put(ipAddress,1);}}
}
写自动配置类:
public class IpAutoConfiguration {@Beanpublic IpCountService ipCountService(){return new IpCountService();}
}
也可以用@Import
@Import(IpCountService.class)
public class IpAutoConfiguration {}
再写spring.factories
文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.llg.ip.autoconfigure.IpAutoConfiguration
此时开发者引入starter后,服务启动,加载我这个spring.factories文件,进而到自动配置类IpAutoConfiguration,而我自动配置类中@Import或者@Bean了干活儿的业务类。
3、中途调试
starter的大体结构出来了,clean后install到这个starter到本地Maven仓库。
在另一个模块中引入下这个starter:
<dependency><groupId>cn.llg</groupId><artifactId>ip_spring_boot_starter</artifactId><version>0.01-SNAPSHOT</version>
</dependency>
@RestController
public cLass CodeController{@Resourceprivate IpCountService ipCountService;@GetMapping("/test")public String getStr(){//暂时代码调用,实际开发要么对自己的用AOP,对别人的用拦截器ipCountService.count();return "success";}
4、开启定时任务打印报表
需要的效果是每隔固定时间就打印一个表格,使用定时任务去操作上一步count方法得到的ipCountMap集合就行。先开启定时任务功能:
@EnableScheduling
@Import(IpCountService.class)
public class IpAutoConfiguration {}
在IpCountService类中继续写定时任务:
@Slf4j
public class IpCountService {//计数集合private Map<String,Integer> ipCountMap = new HashMap<String,Integer>();//...@Scheduled(cron = "0/10 * * * * ?")public void print(){log.info(" IP访问监控");log.info("+-----ip-address-----+--num--+");for(Map.Entry<String,Integer> info :ipCountMap.entrySet()){String key = info.getKey();Integer count = info.getValue();//String.format修改下对齐缩进,搞一个好看的排版String lineInfo = String.format("|%18s |%6d |",key,count);log.info(lineInfo);}log.info("+--------------------+-------+");}
}
5、引入属性配置类,写活业务参数配置
@ConfigurationProperties(prefix = "tools.ip")
public class IpProperties {/** 日志显示周期 */private long cycle = 10L;/** 是否周期内重置数据 */private Boolean cycleReset = false;/** 日志输出模式 detail:明细模式 simple:极简模式 */private String model = LogModel.DETAIL.value;public enum LogModel {DETAIL("detail"),SIMPLE("simple");private String value;private LogModel(String value) { this.value = value; }public String getValue() { return value; }}
}
设置加载Properties类为Bean:
@EnableConfigurationProperties(IpProperties.class)
@EnableScheduling
@Import(IpCountService.class)
public class IpAutoConfiguration {}
根据配置来灵活实现报表打印:
public class IpCountService {@Autowiredprivate IpProperties ipProperties;@Scheduled(cron = "0/10 * * * * ?") //注意这里,显示周期还没处理,仍然是写死的public void print(){//模式切换if(ipProperties.getMode().equals(IpProperties.LogModel.DETAIL.getValue())){//明细模式}else if(ipProperties.getMode().equals(IpProperties.LogModel.SIMPLE.getValue())){//极简模式}//周期内重置数据(若重置,则先打印,再清空)if(ipProperties.getCycleReset()){ipCountMap.clear();}}
}
明细报表的打印和简略模式报表的打印代码如下:
//明细模式
log.info(" IP访问监控");
log.info("+-----ip-address-----+--num--+");
for(Map.Entry<String,Integer> info :ipCountMap.entrySet()){String lineInfo = String.format("|%18s |%6d |", info.getKey(), info.getValue());log.info(lineInfo);}
log.info("+--------------------+-------+");
//极简模式
log.info(" IP访问监控");
log.info("+-----ip-address-----+");
for(Map.Entry<String,Integer> info :ipCountMap.entrySet()){String lineInfo = String.format("|%18s |", info.getKey());log.info(lineInfo);}
log.info("+--------------------+");
此时,开发者引入starter后,在对应的服务配置文件中写配置即可:
tools:ip:cycle-reset: falsemode: detail
此时,打印周期参数写在cron表达式里,想写活,第一个想到的写法应该是dollar大括号${}
@Scheduled(cron = "/${tools.ip.cycle:5} * * * * ?")
但这时候,相当于属性类里定义的cycle这个属性就没发挥作用,我自己去yaml取值了。因此,使用#{beanName.属性名}
来取:
//注意这个Bean的命名,getBeans找找也行
@Scheduled(cron = "0/#{tools.ip-cn.llg.properties.IpProperties.cycle} * * * * ?")
还有坑,#{beanName.属性名}
前面的beanName会被当作tools,太烦,直接手动控制Bean的名称:
放弃配置属性创建bean方式,改为手工控制:
继续用#{beanName.属性名}
@Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?")
public void print(){//...
}
6、拦截器
前面直接在原来的业务代码里一个个加的方式肯定不行,这里继续在starter里自定义个拦截器:
public class IpInterceptor implements HandlerInterceptor {@Autowiredprivate IpCountService ipCountService;@Overridepublic boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//拦截,执行业务代码前先计数ipCountService.count();return true;}
}
定义配置类,把拦截器加入拦截器链中:
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {@Beanpublic IpInterceptor ipInterceptor(){return new IpInterceptor(); //必须要保证这里创建出来的拦截器对象是唯一对象,因此加@Configuration,其默认属性值proxyBeanMethod=true即可解决这个问题。}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//新增拦截器与拦截对象registry.addInterceptor(ipInterceptor()).addPathPatterns("/**");}
}
到此,starter功能开发完成。
7、开启yml提示功能
和官方starter相比,这个自定义starter被引入后,书写yml配置时不会有提示,继续修改starter,补一个提示功能。starter中引入配置处理器坐标:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
</dependency>
此时,重新clean后install这个starter,可以看到target的META-INF目录下多了个spring-configuration-metadata.json
文件,这就是写配置给提示的关键。将这个文件复制到resource/META-INF下:
然后注释掉starter的pom里的配置处理器坐标,再重新clean后install,否则target和resource下都有spring-configuration-metadata.json
文件,就会有两遍提示:
注释后重新clear+install,在引入starter的项目里可看到提示了:
最后,对于配置项的可选值,还缺少一个提示,修改spring-configuration-metadata.json
文件的hits
"hints": [{"name": "tools.ip.model","values": [{"value": "detail","description": "明细模式."},{"value": "simple","description": "极简模式."}]}
]
重新clean后install:
starter制作完成,开发者只需引入坐标,其对应的模块就有了统计功能。当然,还可以继续优化,比如拦截的资源,也可改成活的,让用户自己配置。
相关文章:

【原理篇】四、自定义starter
文章目录 1、案例分析2、业务功能的实现3、中途调试4、开启定时任务打印报表5、引入属性配置类,写活业务参数配置6、拦截器7、开启yml提示功能 做一个记录系统访客独立IP访问次数的功能,并把它自定义成一个starter,实现:在现有项目…...

redisTemplate不支持zpopmax,解决方案使用reverseRangeWithScore
在redis客户端可以使用zpopmax redisTemplate不支持zpopmax 解决方案 使用reverseRangeWithScore 接下来我们进行测试 我们要返回最大的value,应该是c import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.a…...

基于深度模型的日志异常检测
本文转载自:深度模型的日志异常检测,还有谁不会?PPT下载地址:https://bbs.huaweicloud.com/forum/thread-100052-1-1.html视频讲解地址:https://bbs.huaweicloud.com/live/DevRun_live/202101222000.html 文章目录 1. …...

最大连续子数组
最大连续子数组(Maximum Subarray)问题是一个经典的算法问题,其目标是在给定的整数数组中找到一个连续的子数组,使得该子数组的元素之和最大。这个问题有多种解决方法,其中包括暴力解法、分治法和动态规划等。 下面是…...

【FastCAE源码阅读5】使用VTK实现鼠标拾取对象并高亮
鼠标拾取对象是很多软件的基本功能。FastCAE的拾取比较简单,是通过VTK实现的。 对几何而言,拾取类型切换在工具栏上,单击后再来单击视图区对象进行拾取,拾取后的对象会高亮显示。效果如下图: 一、拾取对象 拾取对象…...

【全志H616 使用标准库 完成自制串口库(分文件实现) orangepi zero2(开源)】.md updata: 23/11/07
文章目录 H616 把玩注意:Linux内核版本5.16 及以上,需手动配置i2c-3 uart5驱动配置示例 分文件编译时需将每个文件一同编译 (空格隔开)例: ggc a.c b.c b.h -lpthread -lxxx..; 常用命令查看驱动文件查看内核检测信息/…...

小白学爬虫:手机app分享商品短连接获取淘宝商品链接接口|淘宝淘口令接口|淘宝真实商品链接接口|淘宝商品详情接口
通过手机APP分享的商品短链接,我们可以调用相应的接口来获取淘口令真实URL,进而获取到PC端的商品链接及商品ID。具体步骤如下: 1、通过手机APP分享至PC端的短链接,调用“item_password”接口。 2、该接口将返回淘口令真实URL。 3…...

python 应用之 request 请求调用
场景: 验证一个第三方接口 目录 一、应用实例 1、预准备工作 1)、引用包 2)、生成随机串 3)、获得当前时间戳 4)、HASH 5)、header处理 6)、请求处理 2、requests请求 1)…...

BeanUtils.copyProperties浅拷贝的坑你得知道?
今天想写一篇文章,主要关于深拷贝和浅拷贝相关的,主要是最近写代码的时候遇到一个BUG,刚好涉及到浅拷贝导致的问题。 问题背景 现在有一个需要是需要修改门店信息,门店也区分父门店和子门店,父门店被编辑更新是需要通过…...

ubuntu安装rabbitMQ 并 开启记录消息的日志
apt-get update apt-get install rabbitmq-server rabbitmqctl add_user root password // 设置用户名密码 rabbitmqctl set_user_tags root administrator // 设置为管理员身份 rabbitmqctl set_permissions -p / root ".*" ".*" ".*" //为…...

思维模型 首因效应
本系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。先入为主,一见钟情。 1 首因效应的应用 1.1 面试中的首因效应 小李是一名应届毕业生,他准备参加一家知名互联网公司的面试。在面试前,他做了充分的准备…...

Redis极速上手开发手册【Redis全面复习】
文章目录 什么是RedisRedis的特点Redis的应用场景Redis安装部署Redis基础命令Redis多数据库特性Redis数据类型Redis数据类型之stringRedis数据类型之hashRedis数据类型之listRedis数据类型之setRedis数据类型之sorted set案例:存储高一班的学员信息 Redis封装工具类…...

[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子
[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子 文章目录 [动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子题目解析解题思路状态表示状态转移方程初始化和填表顺序返回值 代码实现总结 LCR 091. 粉刷房子 题目解析 (1) 一排房子,共有n个 (2) 染…...

【VSS版本控制工具】
VSS版本控制工具 1 安装 VSS2 服务器端配置3 新建用户4 客户端配置Vss2005Vs20055 客户端详细操作 1 安装 VSS 第一步:将VisualSourceSafe2005安装包解压。 第二步:找到setup.exe双击运行。 第三步:在弹出的界面复选框中选中Iaccepttheterms…...

数据持久化技术(Python)的使用
传统数据库连接方式:mysql(PyMySQL)ORM 模型:SQLAlchemy MyBatis、 Hibernate PyMySQL 安装: pip install pymysql简单使用 利用 pymysql.connect 建立数据库连接并执行 SQL 命令(需要提前搭建好数据库…...

第23章(上)_索引原理之索引与约束
文章目录 索引索引分类主键选择索引的代价 约束外键约束约束与索引的区别 索引使用场景不要使用索引的场景总结 索引 索引的概念:索引是一种有序的存储结构。索引按照单个或多个列的值进行排序。 索引的目的:提升搜索效率。 索引分类 按照数据结构分为…...

金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件
文章目录 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件背景说明业务需求格式BOS配置 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件 背景说明 序列号档案是基础资料,资料里…...

OFDM深入学习及MATLAB仿真
文章目录 前言一、OFDM 基本原理及概念1、OFDM 简介2、子载波3、符号4、子载波间隔与符号长度之间的关系 二、涉及的技术1、保护间隔2、交织3、信道编码4、扩频5、导频6、RF(射频)调制7、信道估计 三、变量间的关系四、IEEE 802.11a WLAN PHY 层标准五、…...

软件测试简历原来是写了这些才让面试官已读不回
前言: 马上就到了面试跳槽涨薪好时候了,最近看很多的小伙伴已经开始投简历了,一天投了几十次几百次,面试官已读不会,面试的机会都没有更别说后面的事情的,这是为什么呢? 很大一部分的原因是的…...

ESP32网络开发实例-Web服务器RGB LED调光
Web服务器RGB LED调光 文章目录 Web服务器RGB LED调光1、RGB LED介绍3、软件准备4、硬件准备4、代码实现在本文中,我们将创建一个 RGB LED 控制器网络服务器。 Web 服务器将显示用于设置 RGB LED 颜色的色谱。 颜色将主要分为三种:红色、绿色和蓝色。 用户将从光谱中选择一种…...

C# TCP Server服务端多线程监听RFID读卡器客户端上传的读卡数据
本示例使用设备介绍:液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Sy…...

【electron】【附排查清单】记录一次逆向过程中,fetch无法请求http的疑难杂症(net::ERR_BLOCKED_BY_CLIENT)
▒ 目录 ▒ 🛫 导读需求开发环境 1️⃣ Adblock等插件拦截2️⃣ 【失败】Content-Security-Policy启动服务器json-serverhtml中的meta字段 3️⃣ 【失败】https vs httpwebPreferences & allowRunningInsecureContent disable-features 4️⃣ 【失败】检测fetch…...

【JS】scrollTop+scrollHeight+clientTop+clientHeight+offsetTop+offsetHeight
scrollTop、scrollHeight、clientTop、clientHeight、offsetTop以及offsetHeight 1. scrollTop 与 scrollHeight 1.1 scrollTop scrollTop 是这六个属性中唯一一个可写的属性。 Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。 一个元素的 scrollT…...

Go语言函数用法
文章目录 Go语言函数用法 Go语言函数用法 函数在Go语言中有多种用法,它们是组织和模块化代码、提高代码的可维护性和可重用性的关键部分。以下是函数的一些常见用法: 封装代码:函数允许将一组相关的代码块封装到一个独立的单元中,…...

3.5、Linux:命令行git的使用
个人主页:Lei宝啊 愿所有美好如期而遇 在Linux Centos7.6下安装git yum -y install git 注册一个gitee账号 进去注册就好,记住自己的用户名和密码。 创建一个仓库 点击复制,接着就可以在Linux上使用了 git clone git clone 刚才复制的地…...

基于servlet+jsp+mysql网上书店系统
基于servletjspmysql网上书店系统 一、系统介绍二、功能展示四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型:Java web项目 项目名称:基于servletjspmysql网上书店系统 项目架构:B/S架构 开发语言:Java语言 前端技…...

自用工具类整理
自动生成数据 uuid&雪花id private static Long workerId 1L; private static Long datacenterId 1L; private static Snowflake snowflake IdUtil.createSnowflake(workerId, datacenterId);public static String getId(String idType) {if (idType.equals("uui…...

jenkins2
jenkins插件管理安装:docker-build jenkins安装了docker 配置docke builder 添加 unix:///var/run/docker.sock rootubuntu20:~# usermod -G docker jenkins 修改docker中service文件添加 -H tcp://0.0.0.0:2376 jenkins中系统管理中 tcp://localhost:2376...

YOLOv5独家改进:分层特征融合策略MSBlock | 南开大学提出YOLO-MS |超越YOLOv8与RTMDet,即插即用打破性能瓶颈
💡💡💡本文独家改进:分层特征融合策略MSBlock,不同Kernel-Size卷积在不同尺度提升特征提取能力,最终引入到YOLOv5,做到二次创新 1)MSBlock使用;2)和C3结合使用 推荐指数:5颗星 MSBlock | 亲测在多个数据集能够实现大幅涨点,小目标检测效果也不错 💡💡…...

HTTP 协议详解-上(Fiddler 抓包演示)
文章目录 HTTP 协议HTTP 协议的工作过程HTTP 请求 (Request)认识URL关于 URL encode认识 "方法" (method)GET 方法POST 方法其他方法请求 "报头" (header)请求 "正文" (body) HTTP 响应详解状态码响应 "报头" (header) HTTP 协议 HTT…...