【原理篇】四、自定义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的工程去自动装配
	@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制作完成,开发者只需引入坐标,其对应的模块就有了统计功能。当然,还可以继续优化,比如拦截的资源,也可改成活的,让用户自己配置。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu

c语言总是有小问题,是练的少吗?

c语言总是有小问题&#xff0c;是练的少吗&#xff1f; 题主说我做c语言的题目时候&#xff0c;是有思路的并且可以按照想法写下来&#xff0c;大体上看没有问题&#xff0c;但是到运行的时候总是不过关。就需要很长的时间找出那个细微的错误&#xff0c;这种细微的错误怎么才能…

【C++】——类与对象(二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

基于element-plus定义表格行内编辑配置化

文章目录 前言一、新增table组件二、使用步骤 前言 在 基于element-plus定义表单配置化 基础上&#xff0c;封装个Element-plus的table表格 由于表格不同于form组件&#xff0c;需自定义校验器&#xff0c;以下组件配置了单个校验&#xff0c;及提交统一校验方法&#xff0c;且…

python核心编程速记【笔记迁移】

笔记速记 1.python非常注重缩进&#xff0c;这是它的显著特征之一。 2.import相当于头文件声明模块。 3.利用type函数 type(a)可以查看当前变量类型。 isinstance可以比较两个数据类型并返回一个布尔值。 4.这里面的可直接使用and和or作为一个函数 5.python的算法比较贴合…

新生儿疝气:原因、科普和注意事项

引言&#xff1a; 新生儿疝气是一种在婴儿中相对较常见的状况&#xff0c;很多新父母可能对这一现象感到困惑和焦虑。疝气发生时&#xff0c;内腹腔的一部分可能穿过腹壁的弱点&#xff0c;导致腹部出现凸起。本文将科普新生儿疝气的原因&#xff0c;提供相关信息&#xff0c;…

Jenkins CICD过程常见异常

1 Status [126] Exception when publishing, exception message [Exec exit status not zero. Status [126] 1.1 报错日志 SSH: EXEC: STDOUT/STDERR from command [/app/***/publish.sh] ... bash: /app/***/publish.sh: Permission denied SSH: EXEC: completed after 200…

PHP 使用递归方式 将其二维数组整合为层级树 其中层级id 为一个uuid的格式 造成的诡异问题 已解决

不啰嗦 直接上源代码 <?php function findChildren($list, $p_id){$r array();foreach ($list as $k > $item) {if ($item[fid] $p_id) {unset($list[$k]);$length count($r);$r[$length] $item;if ($t findChildren($list, $item[id])) {$r[$length][children] …

基于SSM的学生档案管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

android开发布局知识

插件开发的视频笔记&#xff1a;

跨境电商一定要做跨境独立站的6个原因!一键对接电商API接口瞬间拥有全球各大平台几十亿商品

跨境电商为什么要做独立站&#xff1f;它的优势又有哪一些&#xff1f; 如果说我们的企业是做BtoB的跨境电商&#xff0c;那今天这个内容一定要看仔细。 拥有了独立站你就是最高权力者&#xff0c;一切尽在自己掌控中&#xff01; 第一呢&#xff0c;独立站它就是我们自己做的…

IP行业API助力于网络分析和数据挖掘

引言 在当今数字化时代&#xff0c;数据成为了企业、科研机构和政府决策者的重要资源&#xff0c;而IP行业API则成为了数据分析及挖掘的工具之一。IP行业API是一种能够查询IP地址所属的行业分类信息的应用程序接口&#xff0c;它能够提供在网络分析、用户行为分析及大数据挖掘…

【PG】PostgreSQL13主从流复制部署(详细可用)

目录 版本 部署主从注意点 1 主库上创建复制用户 2 主库上修改pg_hba.conf文件 3 修改文件后重新加载配置使其生效 4 主库上修改配置文件 5 重启主库pg使参数生效 6 部署从库 7 备份主库数据至从库 停止从库 备份从库的数据库目录 新建数据库数据目录data 创建和…

客户服务质量提升的三种思路

客户服务质量是企业在市场竞争中立于不败之地的重要因素之一&#xff0c;优秀的客户服务不仅可以提高客户满意度&#xff0c;还可以提高客户黏度和回头率。随着经济的发展&#xff0c;客户服务行业也在不断发展壮大。在这个竞争激烈的行业中&#xff0c;企业如何提高客户服务质…

送水服务预约小程序内容该如何做

无论小区还是办公楼等场景&#xff0c;送水服务往往有较高需求&#xff0c;同时该服务属于长期稳定性的&#xff0c;因此对品牌来说&#xff0c;如何打造品牌获取更多用户及转化非常重要&#xff0c;然而在实际订水过程中&#xff0c;又会面临着一些难题&#xff1a; 1、品牌传…

基于SSM的学院就业信息网设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【FPGA】正确处理设计优先级--或许能帮你节省50%的资源

概述 假如现在有一种方法–可以在不怎么需要修改已有设计的情况下&#xff0c;就可以帮您节省50%的设计资源&#xff0c;那你会试试看吗&#xff1f; 当前市场环境下&#xff0c;更低廉的成本却可获得同等性能无疑是极具诱惑的。本文将介绍一种FPGA设计技术&#xff0c;该技术…

docker创建并访问本地前端

docker创建并访问本地前端&#xff0c;直接上命令&#xff1a; 安装nginx镜像&#xff1a; docker pull nginx 查看已安装的nginx&#xff1a; docker images 创建DockerFile文件&#xff0c;直接在当前文件夹种创建 touch Dockerfile 在Dockerfile写入内容&#xff1a; F…

ISP算法——UVNR

ISP算法——UVNR 概念简介 UVNR也就是经过CSC只有在YUV域对UV两个色域进行降噪&#xff0c;在有些方案里也叫CNR&#xff08;chroma noise reduction&#xff09;。主要就是在YUV域针对彩燥进行特殊处理的一系列算法。 关于噪声产生的原因在前面关于降噪的文章和视频中已经做…

Redis系列-Redis性能优化与安全【9】

目录 Redis系列-Redis性能优化与安全【9】Redis性能优化策略Redis安全设置与防护措施Redis监控与诊断工具介绍 七、Redis应用案例与实战八、Redis未来发展与趋势 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; Redis系列-Redis性能优化与安…