本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2016/02/21/include-data-rest-in-mappings-endpoint/
Spring Boot提供了一套方便使用的endpoint,暴露了常用的一些应用信息,比如路由情况,堆栈情况等等,灵活的使用不仅可以加快调试,也可以方便运维。
RequestMappingEndpoint类提供了地址为mappings的endpoint,提供了是当前的路由情况,什么样的路径解析到什么样的执行器中。大概的输出如下

当然,任何你自己定义的Controller等等,都会被自动包含到这个mappings中。但是如果你使用Spring Data Rest,你就会发现它的相关信息并没有被包含到这个里面。
Mappings Endpoint原理
要解决这个问题,就需要先来看看RequestMappingEndpoint是如何工作的。
首先它并没有缓存,这意味着每次访问的时候它都会调用invoke方法来获取信息
public Map<String, Object> invoke() {
....
}
而Spring中的路由的映射大致讲只有两种,一种是UrlHandlerMapping,一种是MethodMapping。我们最常用的Controller配合RequestMapping的属于后者。
而处理这两种情况都是从applicationContext中获取所有实例,然后提取信息。唯一的不同是两种映射的对应信息保持方式不同而已。
Map <String,AbstractUrlHandlerMapping > mappings = applicationContext.getBeansOfType(AbstractUrlHandlerMapping.class);
for (Entry < String, AbstractUrlHandlerMapping > mapping: mappings.entrySet()) {
Map < String, Object > handlers = getHandlerMap(mapping.getValue());
for (Entry < String, Object > handler: handlers.entrySet()) {
result.put(handler.getKey(), Collections.singletonMap("bean", mapping.getKey()));
}
}
那么以这种逻辑来看,Spring Data Rest的映射也必然在这其中才对,为什么没有显示出来?
RepositoryRestHandlerMapping和其初始化
Spring Data Rest的映射是由RepositoryRestHandlerMapping处理的AbstractHandlerMethodMapping。
也就是说在RequestMappingEndpoint从上下文中获取所有MethodMapping的时候就应该获取到它。
那么来看看它的初始化过程,具体代码在RepositoryRestMvcConfiguration中
@Bean
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
可以看到对上下文而言,暴露的只有DelegatingHandlerMapping本身,并没有暴露RepositoryRestHandlerMapping,而DelegatingHandlerMapping实现的是HandlerMapping。
也就是说只要暴露RepositoryRestHandlerMapping,那么mappings endpoint就能工作了。
遗憾的是RepositoryRestMvcConfiguration是data-rest-webmvc的一部分,没有修改,所以实在需要这些信息只有自己去获取并暴露出来。
暴露Spring Data Rest路由信息
自定义一个endpoint,对于invoke方法
@Override
public Map<String, Object> invoke() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
addDataRestMethodMappings(this.applicationContext, result);
return result;
}
protected void addDataRestMethodMappings(ApplicationContext applicationContext, Map<String, Object> result) {
if (applicationContext != null) {
RepositoryResourceMappings resourceMappings = applicationContext.getBean(RepositoryResourceMappings.class);
RepositoryRestConfiguration config = applicationContext.getBean(RepositoryRestConfiguration.class);
JpaHelper jpaHelper = applicationContext.getBean(JpaHelper.class);
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings, config);
repositoryMapping.setJpaHelper(jpaHelper);
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
Map<?, HandlerMethod> methods = repositoryMapping.getHandlerMethods();
for (Map.Entry<?, HandlerMethod> method : methods.entrySet()) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("bean", "repositoryMapping");
map.put("method", method.getValue().toString());
result.put(method.getKey().toString(), map);
}
}
}
当然,这段代码只是一个例子,并没有做任何安全检查和空判断。
最后效果如下:

参考资料
https://github.com/spring-projects/spring-boot/issues/4541
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2016/02/21/include-data-rest-in-mappings-endpoint/