由于对 SpringBoot 的版本升级,从 knife4j-spring-boot-starter 3.0.3 版本迭代到 knife4j-openapi2-spring-boot-starter 4.2.0 版本,其中 host 与 basePath 参数不能动态修改的问题,结合源码进行扩展。
从页面的展示现象上与代码的配置上结合分析,两个版本之间的文档属性 host 与 basePath 应该为接口内部自动处理。 当我配置域名后,host、basepath 不能检测变更,导致接口调用全部失败。
基于以上问题,我决定找到源码中对应的点进行扩展,将 host、basepath 也作为动态可配置的参数。
首先通过 F12
doc.html 文档页面,发现主体数据是通过 /V2/docs-api
接口返回数据进行页面填充,知道了接口是什么,接下来分析接口在源码的哪个类中。
思考
怎么快速定位到源码中的接口?
结合学习过的源码知识,我此时想到了通过 SpringMVC 的执行流程源码来定位,打开 DispatcherServlet #doDispatch
方法,在处理器执行链那一行打个断点
刷新 doc.html 页面,看断点进来的接口有哪些,几次过后就发现了 /V2/docs-api
接口的调用
通过控制台查看 mappedHandler 对象内部的 handler 属性,同时能查看到处理器的实现类,点击 Navigate
可定位到处理器的实现类
找到接口的出处 Swagger2ControllerWebMvc
类,查看接口,发现其内部获取 Swagger
对象后,单独设置了 host 与 basePath 属性,到了此时,已经定位了接口在源码中的位置,逻辑也已经完全清晰,接下来开始思考如何改造才能动态扩展出 host 与 basePath 属性?
思考
怎么扩展源码中Controller接口返回的数据?
对于 Controller 接口的处理,切入点还是比较多的,例如 HandlerInterceptor、ResponseBodyAdvice。
不过由于处理的是Jar包中的 Controller,它在加载到Spring容器的时候,方法的 HandlerInterceptor 已经确认,所以只能使用 ResponseBodyAdvice 方式。
定义动态的属性配置,通过 application.yaml 隔离
yml# dev
knife4j:
base-path: /
host: localhost:${server.port}
yml# prod
knife4j:
base-path: /yonger
host: liushigong.cn
声明 @RestControllerAdvice 的类,实现 ResponseBodyAdvice 接口
java@RestControllerAdvice
public class Knife4jResponseAdvice implements ResponseBodyAdvice<Json> {
@Value("${knife4j.base-path}")
private String basePath;
@Value("${knife4j.host}")
private String host;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.getDeclaringClass().equals(Swagger2ControllerWebMvc.class);
}
@SneakyThrows
@Override
public Json beforeBodyWrite(Json body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body != null) {
// 重写host 和 basePath
Swagger swagger = JSONObject.parseObject(body.value(), Swagger.class);
swagger.setHost(host);
swagger.setBasePath(basePath);
return new Json(JSONObject.toJSONString(swagger));
}
return null;
}
}
思路:
/V2/docs-api
接口返回的是 Json 对象,所以泛型是 JsonSwagger2ControllerWebMvc.class
方法返回true在平常遇到问题时,会去想如何去解决,当有一定的技术深度,掌握部分源码以后,遇到此类问题,脑海中会立马构思出一套思路,排查此类问题就会轻而易举,这就体现出了学习源码的价值。解决问题最重要的就是思路,提高深度就是拓展思路最好的方式!
本文作者:柳始恭
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!