【原理篇】四、自定义starter

news2024/12/23 15:13:00

文章目录

  • 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的工程去自动装配
	@Autowired
	private 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 {

	@Bean
	public 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{

	@Resource
	private 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 {

	@Autowired
	private 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: false
    mode: 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 {

	@Autowired
	private IpCountService ipCountService;

	@Override
	public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//拦截,执行业务代码前先计数
		ipCountService.count();
		return true;
	}
}

定义配置类,把拦截器加入拦截器链中:

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

	@Bean
	public IpInterceptor ipInterceptor(){
		return new IpInterceptor();  //必须要保证这里创建出来的拦截器对象是唯一对象,因此加@Configuration,其默认属性值proxyBeanMethod=true即可解决这个问题。
	}
	
	@Override
	public 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制作完成,开发者只需引入坐标,其对应的模块就有了统计功能。当然,还可以继续优化,比如拦截的资源,也可改成活的,让用户自己配置。

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

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

相关文章

systemctl enable docker.service报错“Failed to execute operation: Bad message“

将docker加入到开机自启&#xff0c;报错&#xff1a; 解决&#xff1a; 重新粘贴复制&#xff1a; [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target…

【基带开发】AD9361通信基础:复数乘法 除法

复数 是实数和虚数的组合 例子&#xff1a;3.6 4i, −0.02 1.2i, 25 − 0.3i, 0 2i 乘法 除法

apple mobile device ethernet

莫名其妙使用了一次apple mobile device ethernet&#xff0c;原本正常的网络突然之间抽筋了&#xff0c;在网卡界面看到有两个&#xff0c;以太网3原本启用状态&#xff0c;禁用恢复。 通过搜索apple mobile device ethernet&#xff0c;在网上看到该答案&#xff0c;原来是接…

DDD技术方案落地实践

1. 引言 从接触领域驱动设计的初学阶段&#xff0c;到实现一个旧系统改造到DDD模型&#xff0c;再到按DDD规范落地的3个的项目。对于领域驱动模型设计研发&#xff0c;从开始的各种疑惑到吸收各种先进的理念&#xff0c;目前在技术实施这一块已经基本比较成熟。在既往经验中总结…

PPT NO.3 如何设置日期自动更新

打开PPT,停留在你想放入时间的那页上&#xff1a; ​ 点击“插入”-->"时间和日期"&#xff1a; 然后会弹出一个窗口&#xff0c; 选择“日期和时间”&#xff0c;自动更新里面可以选择自己喜欢的日期格式&#xff0c;左下角“应用”就是当前的这张ppt&#xff0…

Workbench环境中常见问题

问题描述&#xff1a;1 解决方案&#xff1a;2 问题描述&#xff1a;在WB中启动Fluent&#xff0c;报错&#xff0c;提示 “The requested operatjon requires elevation” 解决方案&#xff1a;这个问题是因为WB主程序没有管理员权限导致。使用管理员权限启动WB后&#xff0c…

机器学习练习1

线性回归 数据集模型,第一列代表人口,第二列代表利润 此处的线性回归 与 常规的 y wx b 不同的是 将b换成了w的一部分 故需要在数据集x前面加个1, 求出b . 可以简化计算,只需要一个乘法就可以

音频恢复怎么做?这3招很管用!

“我是一名电台主播&#xff0c;所以经常需要用电脑录制并保存一些音频&#xff0c;今天想对某期节目进行加工时&#xff0c;突然有一部分音频丢失了&#xff0c;有什么方法能找回这些音频吗&#xff1f;” 在日常工作中&#xff0c;或许我们也会将很多重要的音频文件保存在电脑…

论文速览 | TRS 2023: 使用合成微多普勒频谱进行城市鸟类和无人机分类

注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 论文速览 | TRS 2023: Urban Bird-Drone Classification with Synthetic Micro-Doppler Spectrograms 原始论文:D. White, M. Jahangir, C. J. Baker and M. Antoniou, “Urban Bird-Drone…

【CSP认证考试】202309-1:坐标变换(其一)100分解题思路+代码

解题思路 暴力解决&#xff0c;不考虑时空开销就一直用for循环也可以做出来。按照题目意思输入两个数组&#xff0c;然后将第一个输入的数组的x部分累加起来记作x&#xff0c;再将y部分累加起来记作y。再将第二个数组的x部分都加上x&#xff0c;y部分加上y。最后输出第二个数组…

Git的入门详细教程

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《git》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…

网络原理---拿捏TCP机制原理

文章目录 确认应答机制超时重传机制连接管理机制三次握手&#xff08;建立连接&#xff09;三次握手的流程三次握手的状态转换 四次挥手&#xff08;断开连接&#xff09;四次挥手的流程四次挥手的状态转换 滑动窗口机制流量控制机制拥塞控制机制延迟应答机制捎带应答机制粘包问…

网络工程师回顾学习(第一部分)

根据书本目录&#xff0c;写下需要记忆的地方&#xff1a; 参考之前的笔记&#xff1a; 网络工程师回答问题_one day321的博客-CSDN博客 重构第一部分需要记忆的&#xff1a; 第一章&#xff1a;计算机网络概论 计算机网络的定义和分类&#xff1a;计算机网络是指将地理位…

【Linux】:git基本操作_添加文件_两种场景_查看.git文件 || git修改文件 || 版本回退

&#x1f3af;添加⽂件–场景⼀ &#x1f3af;在包含.git的⽬录下新建⼀个ReadMe⽂件&#xff0c;我们可以使⽤ git add 命令可以将⽂件添加到暂存区&#xff1a; • 添加⼀个或多个⽂件到暂存区&#xff1a; git add [file1] [file2] … • 添加指定⽬录到暂存区&#xff0c;…

【项目总结】基于SpringBoot+Ansj分词+正倒排索引的Java文档搜索引擎项目总结

文章目录 项目介绍&#xff08;开发背景&#xff09;主要用到的技术点前端后端Ansj分词实现索引模块实现Parser类实现Index类完善Parser类优化制作索引速度 实现搜索模块实现DocSearcher类处理暂停词 项目编写过程中遇到的困难点上传部署总结 项目介绍&#xff08;开发背景&…

【紫光同创国产FPGA教程】【PGC1/2KG第六章】密码锁实验例程

本原创教程由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处 适用于板卡型号&#xff1a; 紫光同创PGC1/2KG开发平台&#xff08;盘古1K/2K&#xff09; 一&#xff1a;盘古1K/2K开发板&#xff08;紫光同创PGC…

排序算法的空间复杂度和时间复杂度

一、排序算法的时间复杂度和空间复杂度 排序算法 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性 冒泡排序 O(n) O(n) O(n) O(1) 稳定 直接选择排序 O(n) O(n) O(n) O(1) 不稳定 直接插入排序 O(n) O(n) O(n) O(1) 稳定 快速排序 O(n…

node插件MongoDB(一)——MongoDB的下载和安装

文章目录 前言一、MongoDB的下载和安装1. 下载(1) 打开官网(2) 选择版本(3) 选择电脑系统和安装格式后点击下载(4) 将文件解压放到C:\Program Files文件目录下(5) 在c盘下创建文件夹(6) 启动服务端程序(7) 服务端程序启动成功效果(8) 在浏览器中输入127.0.0.1:27017查看效果&am…

linux下IO模及其特点及select

ftp实现 模拟FTP核心原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0c…

nacos应用——占用内存过多问题解决(JVM调优初步)

问题描述 最近搞了一台1年的阿里云服务器&#xff0c;安装了一下常用的MySQL&#xff0c;Redis&#xff0c;rabbitmq&#xff0c;minio&#xff0c;然后有安装了一下nacos&#xff0c;结果一启动nacos内存占用就很高&#xff0c;就比较限制我继续安装其他镜像或者启动别的服务…