目录
案例实现功能:自己写一个starter,实现如下的功能:
1 每次访问网站,统计ip访问次数
2 后台每10s输出一次监控信息
把坐标放上去,功能就好用,把坐标拿掉,功能就不好用.
一 starter搭框架,引入自己写的starter
1.1 自己写starter步骤
1 创建一个springboot的模块
2 service
package org.zoo.service;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class IpcountService {
private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();
@Autowired
//当前request对象的注入工作,由使用当前starter的工程自动装配
private HttpServletRequest httpServletRequest;
public void count() {
//1 记录ip
String ip = httpServletRequest.getRemoteAddr();
System.out.println("-------------ip:"+ip+"--------------");
//2 增加访问次数
Integer count = ipCountMap.get(ip);
if (count == null) {
//ip第一次访问
ipCountMap.put(ip, 1);
} else {
//曾经访问过
ipCountMap.put(ip, ipCountMap.get(ip) + 1);
}
}
}
3 创建自动装配类
package org.zoo.autoconfig;
import org.springframework.context.annotation.Import;
import org.zoo.service.IpcountService;
@Import(IpcountService.class)
public class IpAutoConfiguration {
}
4 resources文件夹--新建文件夹META-INF--spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.zoo.autoconfig.IpAutoConfiguration
5 maven -- clean --install
1.2 使用自己写的starter
1 找一个项目,导入自己写的starter坐标
<dependency>
<groupId>org.zoo</groupId>
<artifactId>ip_springboot_starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2 调用
package com.qing.controller;
import com.qing.domain.Book;
import com.qing.service.BookService3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.zoo.service.IpcountService;
import java.util.List;
@RestController
@RequestMapping("/books")
public class BookController3 {
@Autowired
private BookService3 bookService;
@Autowired
private IpcountService ipcountService;
//查1
@GetMapping("/{id}")
public Book getById(@PathVariable Integer id){
ipcountService.count();
return bookService.getById(id);
}
}
测试
调用了自己写的 starter
二 完善功能
自动配置
package org.zoo.autoconfig;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.service.IpcountService;
//开启定时任务
@EnableScheduling
@Import(IpcountService.class)
public class IpAutoConfiguration {
}
service
package org.zoo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class IpcountService {
private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();
@Autowired
//当前request对象的注入工作,由使用当前starter的工程自动装配
private HttpServletRequest httpServletRequest;
//计数
public void count() {
//1 记录ip
String ip = httpServletRequest.getRemoteAddr();
//2 增加访问次数
Integer count = ipCountMap.get(ip);
if (count == null) {
//ip第一次访问
ipCountMap.put(ip, 1);
} else {
//曾经访问过
ipCountMap.put(ip, ipCountMap.get(ip) + 1);
}
}
//展示
//定时任务
//cron = "秒 分 时 日 月 星期"
//cron = "0/10 * * * * ?" :从0秒开始每10s执行一次
@Scheduled(cron = "0/10 * * * * ?")
public void print(){
System.out.println(" ip访问监控 ");
System.out.println("|------ip address--------count--|");
for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
//%s:字符串、%d:整数
//%20s:总共占20位的字符串 (默认右对齐)
//%-20s:总共占20位的字符串 (左对齐)
System.out.println(String.format("|%20s |%6d |",key,value));
}
}
}
写好了,给自定义的starter 做 maven 的 clean install
三 自定义starter灵活的自动配置1
修改配置文件的属性配置 设置功能参数 → 就是修改引用自定义starter坐标的项目 的配置文件的值, 得到不一样的效果
如下图,修改配置文件的值,会影响统计的功能
1 properties
package org.zoo.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix ="tools.ip")
public class IpProperties {
/**
* 日志显示周期
*/
private Long cycle = 5L;
/**
* 是否周期内重置数据
*/
private boolean cycleReset = false;
/**
* 日志输出模式 detail、 simple
*/
private String model = LogModel.DETAIL.value;
public enum LogModel{
DETAIL("detail"),
SIMPLE("simple");
private String value;
LogModel(String value) {
this.value = value;
}
public String getValue() {
return 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;
}
}
2 配置类
package org.zoo.autoconfig;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.properties.IpProperties;
import org.zoo.service.IpcountService;
//加载IpProperties到spring容器
@EnableConfigurationProperties(IpProperties.class)
//开启定时任务
@EnableScheduling
@Import(IpcountService.class)
public class IpAutoConfiguration {
}
3 service
package org.zoo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.zoo.properties.IpProperties;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class IpcountService {
private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();
@Autowired
private IpProperties ipProperties;
//展示
//定时任务
//cron = "秒 分 时 日 月 星期"
//cron = "0/10 * * * * ?" :从0秒开始每10s执行一次
@Scheduled(cron = "0/10 * * * * ?")
public void print(){
//使用配置文件里的配置来动态控制输出的数据 → model
//详细模式
if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){
//详细模式
System.out.println(" ip访问监控--详细模式 ");
System.out.println("|------ip address--------count--|");
for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
//%s:字符串、%d:整数
//%20s:总共占20位的字符串 (默认右对齐)
//%-20s:总共占20位的字符串 (左对齐)
System.out.println(String.format("|%20s |%6d |",key,value));
}
System.out.println("|-------------------------------|");
}else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){
//简单模式只显示ip地址
System.out.println(" ip访问监控--极简模式 ");
System.out.println("|-------ip address-----|");
for (String key : ipCountMap.keySet()) {
System.out.println(String.format("|%20s |",key));
}
System.out.println("|----------------------|");
}
//使用配置文件里的配置来动态控制输出的数据 → cycle-reset
if(ipProperties.isCycleReset()){
//是否清空数据为 true
ipCountMap.clear();
}
}
}
maven-- clean install
测试 → 周期内重置数据
引用自定义starter项目的配置文件
10秒周期内,访问了6次,清空了数据,再过10秒,没有点击数据效果如下图
测试 → 输出格式为简单模式
四 自定义starter灵活的自动配置2
关于cycle的配置
1 自定义该容器的名字
2 手工导入IpProperties
package org.zoo.autoconfig;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.zoo.properties.IpProperties;
import org.zoo.service.IpcountService;
//加载IpProperties到spring容器
//@EnableConfigurationProperties(IpProperties.class)
//开启定时任务
@EnableScheduling
//手工导入IpProperties,因为要用spel表达式:bean.属性
// @EnableConfigurationProperties导入的类名有前缀tools.ip..,但是tools不是bean
@Import({IpcountService.class,IpProperties.class})
public class IpAutoConfiguration {
}
为什么要手动导入IpProperties?
正常@ConfigurationProperties(prefix = "XXX")和@EnableConfigurationProperties 是一对注解
关于@ConfigurationProperties和@EnableConfigurationProperties
但是因为要用到IpProperties的默认日志显示周期cycle,需要用到spel表达式的bean名.属性名这种格式(见后面)
但是因为用@ConfigurationProperties(prefix = "XXX")和@EnableConfigurationProperties这对注解,导致spring容器里加载的该IpProperties bean是如下tools.ip这种前缀的名字,如下图:
tools.ip...这种名字并不符合spel表达式的 bean名.属性名 格式
所以用了@Component("ipProperties")重新自定义了该IpProperties bean 的名字,更换使用了import
注入IpProperties类
3 修改service里定时器的设置
使用spel表达式引入默认的cycle设置
完整的service
package org.zoo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.zoo.properties.IpProperties;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class IpcountService {
private Map<String, Integer> ipCountMap = new HashMap<String, Integer>();
@Autowired
//当前request对象的注入工作,由使用当前starter的工程自动装配
private HttpServletRequest httpServletRequest;
//计数
public void count() {
//1 记录ip
String ip = httpServletRequest.getRemoteAddr();
//2 增加访问次数
Integer count = ipCountMap.get(ip);
if (count == null) {
//ip第一次访问
ipCountMap.put(ip, 1);
} else {
//曾经访问过
ipCountMap.put(ip, ipCountMap.get(ip) + 1);
}
}
@Autowired
private IpProperties ipProperties;
//展示
//定时任务
//cron = "秒 分 时 日 月 星期"
//cron = "0/10 * * * * ?" :从0秒开始每10s执行一次
// @Scheduled(cron = "0/10 * * * * ?")
@Scheduled(cron="0/#{ipProperties.cycle} * * * * ?")
public void print(){
//使用配置文件里的配置来动态控制输出的数据 → model
//详细模式
if(ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())){
//详细模式
System.out.println(" ip访问监控--详细模式 ");
System.out.println("|------ip address--------count--|");
for (Map.Entry<String, Integer> entry : ipCountMap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
//%s:字符串、%d:整数
//%20s:总共占20位的字符串 (默认右对齐)
//%-20s:总共占20位的字符串 (左对齐)
System.out.println(String.format("|%20s |%6d |",key,value));
}
System.out.println("|-------------------------------|");
}else if(ipProperties.getModel().equals(IpProperties.LogModel.SIMPLE.getValue())){
//简单模式只显示ip地址
System.out.println(" ip访问监控--极简模式 ");
System.out.println("|-------ip address-----|");
for (String key : ipCountMap.keySet()) {
System.out.println(String.format("|%20s |",key));
}
System.out.println("|----------------------|");
}
//使用配置文件里的配置来动态控制输出的数据 → cycle-reset
if(ipProperties.isCycleReset()){
//是否清空数据为 true
ipCountMap.clear();
}
}
}
maven -- clean install
测试
1秒打印一次
没有设置cyle的值,按默认的5s是一个周期打印一次
总结
五 使用拦截器调用自定义starter的功能
原来是在使用自定义starter的项目里 调用的服务. 现在要改用在拦截器里调用服务
1 定义拦截器
package org.zoo.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.zoo.service.IpcountService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class IpCountInterceptor implements HandlerInterceptor {
@Autowired
private IpcountService ipcountService;
//运行拦截之前做的事情
//返回值是 拦截器走到这儿还走不走后面的原始调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ipcountService.count();
return true;
}
}
2 让拦截器生效
package org.zoo.interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//用@Configuration:默认创建代理对象 → 默认情况创建的对象是一样的 (创建了一次就不会再创建了,而是取出容器中的对象)
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//如果有多个拦截器就registry.add..写多个
//拦截所有:/**
registry.addInterceptor(ipCountInterceptor()).addPathPatterns("/**");
}
@Bean
public IpCountInterceptor ipCountInterceptor(){
return new IpCountInterceptor();
}
}
备注:拦截器写在自定义的 starter项目里.
为什么拦截器要写在自定义的 starter项目里?
因为如果把拦截器写在 使用starter坐标的项目 里, 当把starter坐标删掉之后, 使用拦截器的地方就会报错
maven -- clean install
测试
总结
定义拦截器
让拦截器生效
六 开启yaml提示功能
想实现如下yaml出提示的功能
target下的resources-META-INF 文件夹目前是这样子
1 自定义starter的pom文件加坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
2 刷新maven ,clean install
此时 target下的resources-META-INF 文件夹 多了一个文件,这个文件就是出提示的配置文件
看一下这个json文件里面
decription就是提示的内容,这个内容是 之前写的 文档注释里的内容
3 把 新生成的json文件,剪切到项目的resources-META-INF 里
4 去掉pom文件坐标,刷新maven ,clean install
否则会出2组相同的注释
功能已经基本实现
我们也可以自己定义更多的提示,实现如下的效果
在json文件里修改 hints
"hints": [
{
"name": "tools.ip.cycle-reset",
"values": [
{
"value": "true",
"description": "周期内清空数据重置"
},
{
"value": "false",
"description": "周期内不重置"
}
]
}
]