# springbootipstarter **Repository Path**: weiyikai/springbootipstarter ## Basic Information - **Project Name**: springbootipstarter - **Description**: 自定义starter实现IP监控访问 - **Primary Language**: Unknown - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 2 - **Created**: 2023-01-09 - **Last Updated**: 2023-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## springboot组件: 自定义starter,分别使用map集合和redis的key-value特性实现ip访问监控。 ## 需求分析: ### 数据存储: 实现此功能,需要将ip监控的数据存放在哪里?我们需要存储用户的ip和对应ip访问的次数,所以很明显使用键值对的方式来实现功能。 拥有键值对特性的有map集合和redis,我们分别使用这两者来实现功能! ### 实现方式: 既然要实现IP的访问监控,我们思考,每当web请求传送到我们后台服务器的时候,需要有一个拦截的功能将请求先拦截一下,记录完IP访问之后再放行。很容易想到我们可以使用过滤器和拦截器来实现,方便起见,这里我们使用拦截器!还有后台监控,需要实时监控,也就是说需要不停地显示监控图,这里我们使用spring内置的@EnableScheduling注解来实现定时功能监控! ### 数据多模式展示: 既然我们自定义了starter,那么也可以依赖springboot的底层特性,通过修改配置文件,来实现数据的多模式展示,在此次功能中,我定义了三种模式来展示数据,它们分别是: ● 输出频度,默认5秒 ● 数据特征:累计数据 / 阶段数据,默认累计数据 ● 输出格式:详细模式 / 极简模式 这里我们根据starter的特性,定义一个IpProperties类作为属性类,来实现配置文件的绑定。 IpProperties: ``` package site.weiyikai.springbootipstarter.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * @author 程序员小魏 */ @Data @ConfigurationProperties(prefix = "tool.ip") @Component("ipProperties") 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; } } } ``` application.yml ``` tool: ip: cycle: 5 cycle-reset: false model: detail ``` ## 具体功能实现: ### IP计数功能开发: 这个功能是整个组件的核心,所以我们将它写在Service类中,表示核心业务实现。 我们需要定义一个打印方法(print)来实现监控图的定时打印,以及定义一个计数方法(count)来记录IP访问。 ### Map集合实现: ``` package site.weiyikai.springbootipstarter.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import site.weiyikai.springbootipstarter.properties.IpProperties; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * @author 程序员小魏 */ public class IpCountService { @Autowired private IpProperties ipProperties; private Map ipCountMap = new HashMap(); @Autowired //当前的request对象的注入工作由使用当前starter的工程提供自动装配 private HttpServletRequest httpServletRequest; public void count() { String ip = httpServletRequest.getRemoteAddr(); //2.根据IP地址从Map取值,并递增 Integer count = ipCountMap.get(ip); if (count == null) { ipCountMap.put(ip, 1); } else { ipCountMap.put(ip, count + 1); } if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); for (Map.Entry entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(String.format("|%18s |%5d |", key, value)); System.out.println("+--------------------+-------+"); } } else { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+"); for (Map.Entry entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(String.format("|%18s |", key)); } System.out.println("+--------------------+"); }; } @Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?") public void print() { if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); for (Map.Entry entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(String.format("|%18s |%5d |", key, value)); } System.out.println("+--------------------+-------+"); } else { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+"); for (Map.Entry entry : ipCountMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(String.format("|%18s |", key)); } if (ipProperties.getCycleReset()) { ipCountMap.clear(); } } } } ``` ### redis实现: ``` package site.weiyikai.springbootipstarter.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import site.weiyikai.springbootipstarter.properties.IpProperties; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * @author 程序员小魏 */ public class IpCountService { @Autowired private IpProperties ipProperties; @Autowired //当前的request对象的注入工作由使用当前starter的工程提供自动装配 private HttpServletRequest httpServletRequest; @Autowired private StringRedisTemplate stringRedisTemplate; public void count() { String ip = httpServletRequest.getRemoteAddr(); String num = stringRedisTemplate.opsForValue().get(ip); if(num == null){ stringRedisTemplate.opsForValue().set(ip,"1"); }else{ stringRedisTemplate.opsForValue().set(ip,Integer.parseInt(num) + 1 + ""); } if (count == null) { ipCountMap.put(ip, 1); } else { ipCountMap.put(ip, count + 1); } if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); Set keys = stringRedisTemplate.keys("*"); if(keys != null && keys.size() > 0) { for (String key : keys) { Integer value = Integer.parseInt(stringRedisTemplate.opsForValue().get(key)); System.out.println(String.format("|%18s |%5d |",key,value)); } }else{ throw new RuntimeException("服务器出现异常!"); } System.out.println("+--------------------+-------+"); } } else { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+"); Set keys = stringRedisTemplate.keys("*"); if(keys != null && keys.size() > 0) { for (String key : keys) { System.out.println(String.format("|%18s |",key)); } }else{ throw new RuntimeException("服务器出现异常!"); } System.out.println("+--------------------+"); } ; } @Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?") public void print() { if (ipProperties.getModel().equals(IpProperties.LogModel.DETAIL.getValue())) { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+--num--+"); Set keys = stringRedisTemplate.keys("*"); if(keys != null && keys.size() > 0) { for (String key : keys) { Integer value = Integer.parseInt(stringRedisTemplate.opsForValue().get(key)); System.out.println(String.format("|%18s |%5d |",key,value)); } }else{ throw new RuntimeException("服务器出现异常!"); } System.out.println("+--------------------+-------+"); } else { System.out.println(" IP访问监控"); System.out.println("+-----ip-address-----+"); Set keys = stringRedisTemplate.keys("*"); if(keys != null && keys.size() > 0) { for (String key : keys) { System.out.println(String.format("|%18s |",key)); } }else{ throw new RuntimeException("服务器出现异常!"); } System.out.println("+--------------------+"); }; if (ipProperties.getCycleReset()) { Set keys = stringRedisTemplate.keys("*"); Long delete = stringRedisTemplate.delete(keys); } } } } ``` ### 配置类: 由于在service类中的@Scheduled(cron = "0/#{ipProperties.cycle} * * * * ?")注解,我们使用了ipProperties,如果此时仍然使用@EnableConfigurationProperties注解来注入IpProperties类的bean,那么此时bean真正的名称并非ipProperties。所以我们这里不再使用此注解,而是在IpProperties中使用@Component注解。然后再导入到配置类中去。 ``` /** * @author 程序员小魏 * @create 2022-12-11 15:39 */ @Import({IpProperties.class}) @EnableScheduling public class IpAutoConfiguration { @Bean public IpCountService ipCountService(){ return new IpCountService(); } } ``` 最后在META-INF目录中创建spring.factories文件,通过此文件将配置类加载进springboot的容器中。 spring.factories: ``` # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=site.weiyikai.springbootipstarter.config.IpAutoConfiguration ``` ### 拦截器: ``` 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; } } ```