SpringBoot项目运行时动态生成接口被Swagger发现
admin
2024-05-10 10:23:30
0

上篇文章 https://blog.csdn.net/lmchhh/article/details/128634606?spm=1001.2014.3001.5502 我讲了SpringBoot动态生成接口,接下来要处理的就是新生成的接口如何被Swagger发现,并且可以通过/swagger-ui.html和/v2/api-docs查到

文章目录

  • 一,启动时加载
  • 二,运行时加载

一,启动时加载

第一种最简单的方式就是在SpringBoot项目启动时加载,就像在上篇文章中,我动态创建接口的接口基本上都是在main函数中添加

@SpringBootApplication
public class ServiceApiApplication {public static void main(String[] args) throws NoSuchMethodException {ApplicationContext application = SpringApplication.run(ServiceApiApplication.class, args);RequestMappingHandlerMapping bean = application.getBean(RequestMappingHandlerMapping.class);// 无参get方法RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths("/lmcTest").methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 带一参数的get方法RequestMappingInfo requestMappingInfo1 = RequestMappingInfo.paths("/lmcTest2").params(new String[]{"fileName"}).methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo1, "adapterController", AdapterController.class.getDeclaredMethod("myTest2", String.class));// 带多个参数的get方法RequestMappingInfo requestMappingInfo2 = RequestMappingInfo.paths("/lmcTest3").params(new String[]{"fileName", "type", "isSort"}).methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo2, "adapterController", AdapterController.class.getDeclaredMethod("myTest3", String.class, String.class, Boolean.class));// 无参post方法RequestMappingInfo requestMappingInfo3 = RequestMappingInfo.paths("/lmcTest4").methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo3, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 带参post方法RequestMappingInfo requestMappingInfo4 = RequestMappingInfo.paths("/lmcTest5").params(new String[]{"fileName", "type", "isSort"}).methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo4, "adapterController", AdapterController.class.getDeclaredMethod("myTest3", String.class, String.class, Boolean.class));// body带参的post方法RequestMappingInfo requestMappingInfo5 = RequestMappingInfo.paths("/lmcTest6").produces(new String[]{"text/plain;charset=UTF-8"}).methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo5, "adapterController", AdapterController.class.getDeclaredMethod("myTest4", HttpServletRequest.class));System.err.println("已经加载/lmcTest");}}

然后配置Swagger

@Configuration
@EnableSwagger2
@Slf4j
public class Swagger2Config {@Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.foxconn.serviceapi.controller")).build();}private ApiInfo apiInfo() {System.err.println("开始配置Swagger");log.info("开始配置Swagger");return new ApiInfoBuilder().title("superFA说明文档").description("API平台接口说明文档").termsOfServiceUrl("").contact(new Contact("zg", "npi-sw", "npi-sw@mail.foxconn.com")).version("0.0.1-SNAPSHOT") // 該數據從配置文件中獲取.build();}
}

运行后发现,程序日志打印中,

开始配置Swagger
已经加载/lmcTest

先加载Swagger配置,再加载自定义接口,启动成功后,即使通过 /actuator/mappings可以查到自定义接口,但是在/v2/api-docs中查不到,此时需要让Swagger配置的优先级在新增接口之后,新创建接口配置类:

MappingConfig.java

/*** @ClassName: MappingConfig* @author: Leemon* @Description: TODO* @date: 2023/1/6 14:04* @version: 1.0*/
@Configuration
public class MappingConfig {@Autowiredprivate ApplicationContext applicationContext;@PostConstructpublic void construct() throws NoSuchMethodException {RequestMappingHandlerMapping bean = applicationContext.getBean(RequestMappingHandlerMapping.class);// 无参get方法RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths("/lmcTest").methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 带一参数的get方法RequestMappingInfo requestMappingInfo1 = RequestMappingInfo.paths("/lmcTest2").params(new String[]{"fileName"}).methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo1, "adapterController", AdapterController.class.getDeclaredMethod("myTest2", String.class));// 带多个参数的get方法RequestMappingInfo requestMappingInfo2 = RequestMappingInfo.paths("/lmcTest3").params(new String[]{"fileName", "type", "isSort"}).methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo2, "adapterController", AdapterController.class.getDeclaredMethod("myTest3", String.class, String.class, Boolean.class));// 无参post方法RequestMappingInfo requestMappingInfo3 = RequestMappingInfo.paths("/lmcTest4").methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo3, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 带参post方法RequestMappingInfo requestMappingInfo4 = RequestMappingInfo.paths("/lmcTest5").params(new String[]{"fileName", "type", "isSort"}).methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo4, "adapterController", AdapterController.class.getDeclaredMethod("myTest3", String.class, String.class, Boolean.class));// body带参的post方法RequestMappingInfo requestMappingInfo5 = RequestMappingInfo.paths("/lmcTest6").produces(new String[]{"text/plain;charset=UTF-8"}).methods(RequestMethod.POST).build();bean.registerMapping(requestMappingInfo5, "adapterController", AdapterController.class.getDeclaredMethod("myTest4", HttpServletRequest.class));System.err.println("已经加载/lmcTest");}}

然后把main函数中多余代码删除

再重新配置Swagger2Config,对其类新增注解@AutoConfigureAfter

@Configuration
@EnableSwagger2
@AutoConfigureAfter(MappingConfig.class)
@Slf4j
public class Swagger2Config{}

此时重新启动后加载顺序就会改变

二,运行时加载

启动时加载还是算比较简单的,但是运行时加载就比较麻烦。我一开始的思路是在运行时重新加载Swagger2Config类,但是无效(可能是我操作问题),实在找不到实现的思路和方法

我重新加载的方式如下所示:

RequestMappingHandlerMapping bean = applicationContext.getBean(RequestMappingHandlerMapping.class);// 无参get方法RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths("/leenai").methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 重新加载beanDefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();defaultListableBeanFactory.destroySingleton("swagger2Config");defaultListableBeanFactory.registerSingleton("swagger2Config", swagger2Config);

在尝试了很多种方式后,我找到了加载/swagger-ui.html页面时会调用的类ServiceModelToSwagger2MapperImpl和其方法public Swagger mapDocumentation(Documentation from)

public Swagger mapDocumentation(Documentation from) {if (from == null) {return null;} else {Swagger swagger = new Swagger();swagger.setVendorExtensions(this.vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));swagger.setSchemes(this.mapSchemes(from.getSchemes()));swagger.setPaths(this.mapApiListings(from.getApiListings()));swagger.setHost(from.getHost());swagger.setDefinitions(this.modelMapper.modelsFromApiListings(from.getApiListings()));swagger.setSecurityDefinitions(this.securityMapper.toSecuritySchemeDefinitions(from.getResourceListing()));ApiInfo info = this.fromResourceListingInfo(from);if (info != null) {swagger.setInfo(this.mapApiInfo(info));}swagger.setBasePath(from.getBasePath());swagger.setTags(this.tagSetToTagList(from.getTags()));List list2 = from.getConsumes();if (list2 != null) {swagger.setConsumes(new ArrayList(list2));} else {swagger.setConsumes((List)null);}List list3 = from.getProduces();if (list3 != null) {swagger.setProduces(new ArrayList(list3));} else {swagger.setProduces((List)null);}return swagger;}}

可以看到,返回类型swagger作为方法内部变量使用,然后我重写ServiceModelToSwagger2MapperImpl类

@Primary
@Component("serviceModelToSwagger2MapperImpl2")
@Slf4j
public class ServiceModelToSwagger2MapperImpl2 extends ServiceModelToSwagger2Mapper{public Swagger swagger = new Swagger();public static Boolean isFirstProcess = true;// 是否第一次访问}

将这个方法中的swagger作为类变量使用,改写的方法如下所示:

 @Overridepublic Swagger mapDocumentation(Documentation from) {if (from == null) {return null;} else {swagger.setVendorExtensions(this.vendorExtensionsMapper.mapExtensions(from.getVendorExtensions()));swagger.setSchemes(this.mapSchemes(from.getSchemes()));if (isFirstProcess) {swagger.setPaths(this.mapApiListings(from.getApiListings()));isFirstProcess = false;}swagger.setHost(from.getHost());
//            swagger.setDefinitions(this.modelMapper.modelsFromApiListings(from.getApiListings()));swagger.setSecurityDefinitions(this.securityMapper.toSecuritySchemeDefinitions(from.getResourceListing()));ApiInfo info = this.fromResourceListingInfo(from);if (info != null) {swagger.setInfo(this.mapApiInfo(info));}swagger.setBasePath(from.getBasePath());swagger.setTags(this.tagSetToTagList(from.getTags()));List list2 = from.getConsumes();if (list2 != null) {swagger.setConsumes(new ArrayList(list2));} else {swagger.setConsumes((List) null);}List list3 = from.getProduces();if (list3 != null) {swagger.setProduces(new ArrayList(list3));} else {swagger.setProduces((List) null);}log.info("enter serviceModelToSwagger2MapperImpl class method mapDocumentation()");for (Map.Entry o : swagger.getPaths().entrySet()) {System.err.println(JSON.toJSONString(o.getValue()));}return swagger;}}

然后在每次创建新自定义接口的时候,手动对其添加

	@GetMapping("create")public String create() throws NoSuchMethodException {System.err.println(serviceModelToSwagger2MapperImpl2.swagger.getPaths().keySet().toString());RequestMappingHandlerMapping bean = applicationContext.getBean(RequestMappingHandlerMapping.class);// 无参get方法RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths("/leenai").methods(RequestMethod.GET).build();bean.registerMapping(requestMappingInfo, "adapterController", AdapterController.class.getDeclaredMethod("myTest"));// 重新加载beanDefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();Path path = new Path();Operation get = new Operation();get.consumes(Arrays.asList());get.setOperationId("leenaiUsingGET");get.setProduces(Arrays.asList("*/*"));get.setDeprecated(false);get.setTags(Arrays.asList("adapter-controller"));Map responseMap = new HashMap<>(2);Response r200 = new Response();r200.setDescription("OK");r200.setExamples(new JSONObject());r200.setHeaders(new HashMap<>());Property schema = new Property() {@Overridepublic Property title(String title) {return null;}@Overridepublic Property description(String description) {return null;}@Overridepublic String getType() {return "String";}@Overridepublic String getFormat() {return null;}@Overridepublic String getTitle() {return null;}@Overridepublic void setTitle(String title) {}@Overridepublic String getDescription() {return null;}@Overridepublic void setDescription(String title) {}@Overridepublic Boolean getAllowEmptyValue() {return null;}@Overridepublic void setAllowEmptyValue(Boolean value) {}@Overridepublic String getName() {return null;}@Overridepublic void setName(String name) {}@Overridepublic boolean getRequired() {return false;}@Overridepublic void setRequired(boolean required) {}@Overridepublic Object getExample() {return null;}@Overridepublic void setExample(Object example) {}@Overridepublic void setExample(String example) {}@Overridepublic Boolean getReadOnly() {return null;}@Overridepublic void setReadOnly(Boolean readOnly) {}@Overridepublic Integer getPosition() {return null;}@Overridepublic void setPosition(Integer position) {}@Overridepublic Xml getXml() {return null;}@Overridepublic void setXml(Xml xml) {}@Overridepublic void setDefault(String _default) {}@Overridepublic String getAccess() {return null;}@Overridepublic void setAccess(String access) {}@Overridepublic Map getVendorExtensions() {Map map = new HashMap<>();map.put("$ref", "$.get.responses.200.responseSchema.vendorExtensions");return map;}@Overridepublic Property rename(String newName) {return null;}};ModelImpl model = new ModelImpl();model.setType("String");model.setSimple(false);model.setVendorExtensions(new HashMap<>());r200.setSchema(schema);r200.setResponseSchema(model);responseMap.put("200", r200);Response r401 = new Response();responseMap.put("401", r401);r401.setDescription("Unauthorized");get.setResponses(responseMap);get.setSchemes(Arrays.asList());get.setSecurity(Arrays.asList());get.setSummary("leenai");path.setGet(get);serviceModelToSwagger2MapperImpl2.swagger.getPaths().put("/leenai", path);System.err.println(serviceModelToSwagger2MapperImpl2.swagger.getPaths().keySet().toString());return "success to create and reload createRestApi()";}

同时,还要到Swagger2Config类中添加@PostConstruct方法

    @PostConstructpublic void test() {System.err.println("enter swaggerConfiguration test()");DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();defaultListableBeanFactory.destroySingleton("serviceModelToSwagger2MapperImpl");defaultListableBeanFactory.registerSingleton("serviceModelToSwagger2MapperImpl", serviceModelToSwagger2MapperImpl2);}

此时新增接口后就可以直接通过/swagger-ui.html访问得到。

总之,这种方式确实解决了我的问题,但实在很不友好,自己修改swagger类实在需要补充太多内容,如果大佬们有更多好的方式,请务必指教我这个菜鸡。

相关内容

热门资讯

谁说我爱你你就会爱我的? 谁说我爱你你就会爱我的?对于“谁爱我我就爱谁”的情感观,我认为它并不是一种健康的情感观。以下是我对此...
床尺寸规格都有哪些 床尺寸规格都有哪些按照尺寸来划分,床可以分为四大类,分别是单人床、双人床、大床还有定做的床。床的尺寸...
物理上回到原点什么意思? 物理上回到原点什么意思?一个质点运动了3s后回到原点然后静止,能不能说它在4s时回到原点?是不是在回...
《青春有你3》余景天和罗一舟,... 《青春有你3》余景天和罗一舟,你觉得谁会C位出道?青春有你三余景天和罗一周相比之下,我觉得余景天更有...
男孩爱哭是什么原因 男孩爱哭是什么原因孩子哭是天生的,在他们一出生的时候就会哭,所以哭是他们唯一不用学习的事情,也是他们...
小学生开学前疯狂赶作业:妈妈气... 小学生开学前疯狂赶作业:妈妈气出心脏病说起孩子写作业是很多家长比较头疼的一件事,老师布置作业之后,要...
暗黑3两分钟就能干掉成就怎么完... 暗黑3两分钟就能干掉成就怎么完成 猎魔人速想听实话么。猎魔人装备如果不行,2分钟是打不了的。大多数职...
异形是什么意思 异形是什么意思  异形释义:  1.不同于一般类型,表现多种不同类型 2.发育不同阶段有不同形状的 ...
北京到广州飞机几个小时? 北京到广州飞机几个小时?北京到广州的飞机飞行时间大约为**3个半小时**。具体的肆芦瞎飞行时间可能哗...
失恋失去的到底是什么? 失恋失去的到底是什么?失去的是一个伤害自己的人,一段糟糕的感情,而获得的却是重生,所以失恋没必要去难...
铁碎牙,天生牙,乾坤刀,斗鬼神... 铁碎牙,天生牙,乾坤刀,斗鬼神,这四把刀哪个最厉害?犬夜叉里面的铁碎牙第一,乾坤刀第二,斗鬼神第三,...
男人对女人说!懂你为情感,懂你... 男人对女人说!懂你为情感,懂你为守候是什么意思?男人对女人说,懂你为情感,懂你为守候的意思是:男人懂...
风起霓裳中李治的结局如何? 风起霓裳中李治的结局如何?在里面的结局还是非常不错的,而且这也是一部超级好看的作品。最后的大结局也非...
不要停下来,请继续往前走。的英... 不要停下来,请继续往前走。的英文是什么?Don't stop. ______ ______ go不要...
暗恋一个人会有结果吗? 暗恋一个人会有结果吗?我觉得如果暗恋一个人,但是一直不跟他说的话,是不会有结果的。只在心底默默的喜欢...
赛尔号异能星怎么去 赛尔号异能星怎么去真笨,这都进不去在裂空星系 ,或者你直接去任务里面可以直接传送过去打开地图,裂空星...
暗能量为什么能让宇宙膨胀,它真... 暗能量为什么能让宇宙膨胀,它真的可以撕碎宇宙?它会撕碎宇宙的,这样的能量让宇宙一直在变化。它有节奏的...
露营睡帐篷不怕坏人吗 露营睡帐篷不怕坏人吗 选择安全的坏人多的地方,坏人也不是到处都是。注意些,同时带上强力伙伴
一家之主有什么含义 一家之主有什么含义  一家之主释义:  家庭的当家人  [拼音] [yī jiā zhī zh...
鱼龙有哪些特点? 鱼龙有哪些特点?鱼龙早在三叠纪中期就出现了,它是恐龙的同代动物。在中生代,或庆它们都是海洋里的“霸主...