背景
公司APP经过多次迭代和长时间运行后,代码逐渐变得臃肿,为了便于代码瘦身和无用功能模块儿的剔除需要记录每个路由的访问次数
方案
- 项目启动成功后获取路由并存入redis中
- 通过拦截器获取路由地址进行计数
相关代码
启动后相关代码执行一次
方法一:spring的ApplicationListener< ContextRefreshedEvent>接口(不推荐)
实现ApplicationListener接口,并实现 onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent)方法
@Compent
public class RouterInit implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
if (contextRefreshedEvent.getApplicationContext().getParent() == null) {//保证只执行一次
//需要执行的方法
}
}
}
方法2:springboot的ApplicationRunner接口
ApplicationRunner和CommandLineRunner两个接口是springBoot提供用来在spring容器加载完成后执行指定方法。两个接口区别主要是入参不同。
实现ApplicationRunner接口
@Component
public class RouterInit implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("执行方法");
}
方法3:springboot的CommandLineRunner接口
实现CommandLineRunner接口
@Component
public class RouterInit implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("执行方法");
}
}
SpringApplication 的run方法会执行afterRefresh方法。
afterRefresh方法会执行callRunners方法。
callRunners方法会调用所有实现ApplicationRunner和CommondLineRunner接口的方法。
获取路由逻辑
@Slf4j
@Component
public class RouterRunner implements ApplicationRunner {
@Autowired
WebApplicationContext applicationContext;
@Autowired
RedisUtil redisUtil;
@Override
public void run(ApplicationArguments args) {
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
// 获取url与类和方法的对应信息
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
Map<String, String> map1 = new HashMap<String, String>();
RequestMappingInfo info = m.getKey();
HandlerMethod method = m.getValue();
PatternsRequestCondition p = info.getPatternsCondition();
for (String url : p.getPatterns()) {
map1.put("url", url);
Boolean hasKey = redisUtil.hHasKey(RedisConstant.ROUTER_STATISTICS_KEY, url);
if(!hasKey){
redisUtil.hset(RedisConstant.ROUTER_STATISTICS_KEY, url,0);
}
}
// 类名
map1.put("className", method.getMethod().getDeclaringClass().getName());
// 方法名
map1.put("method", method.getMethod().getName());
RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
for (RequestMethod requestMethod : methodsCondition.getMethods()) {
map1.put("type", requestMethod.toString());
}
list.add(map1);
}
System.err.println(JSON.toJSONString(list));
}
}
拦截逻辑
/**
* 请求拦截
*/
@Slf4j
@Component
public class SessionInterceptor implements HandlerInterceptor {
@Autowired
private RedisUtil redisUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) {
String requestURI = request.getRequestURI();
String path = requestURI.substring(request.getContextPath().length())
.replaceAll("[/]+$", "");
//记录访问请求次数
try {
redisUtil.hincr(RedisConstant.ROUTER_STATISTICS_KEY, path, 1);
} catch (Exception e) {
log.error("路由计数失败", e);
}
}
}