各位好:
文章继续于 swagger map类型的参数(一)
关于swagger map类型的参数 的问题,文章提供一套解决方案。但是方案不够尽善尽美。比如 迫不得已引入BaseController 来解决顺序问题, BeanPospProcessor 和 MapContext 的额外引入,收集的同时 消耗了内存空间,同时也间接的与swagger内置的收集系统冗余。
还有一个比较严重的问题是,MapApiReader 会重复 创建Class类 对象,也正是因为这个原因,导致 swagger不支持Map类型 解析生成Molde的问题,没有暴露出来。这算是一个大大的巧合吧。
所以,又花了些时间,重构了整个项目,改动量挺大的 。代码地址 还是 https://github.com/SincereJ/swagger-demo.git
但是 是 rebuild 分支 ,rebuild 分支。
重构的整个思想没有变化,还是干的偷梁换柱的勾当。只不过这回“偷”的比较合情合理。
- 优化掉了 BaseController
- 去掉 BeanPospProcessor 和 MapContext 的额外引入
- 修复 MapApiReader 重复创建Class类 对象
- swagger不支持Map类型 解析生成Molde的问题 也间接的绕过了
东施效颦也好,嫫母自好也罢。欢迎各位 批评指正。目前 代码 还没有编写注释内容。等整个工程相对稳定了,会及时补充完善注释内容的。
最后 前一阵 有人qq 跟我说 项目打包后,执行java -jar xx.jar,注解里example和description 属性都不生效了。确实是有这个问题,本来这次重构也想着修复这个问题,奈何一直也没找到原因。等稍微空闲下来,再仔细研究一下。
关于 执行java -jar xx.jar,注解里example和description 属性都不生效 的问题已经找到问题原因了。是类加载器的问题。java -jar 使用的是 LaunchedURLClassLoader ,它继承于 URLClassLoader。java -jar xx.jar 启动后, jvm 的classpath 会自动变成 xx.jar,LaunchedURLClassLoader 也只会加载 xx.jar 里边的类。自然包括ApiModelProperty 这个注解类的信息。
对应我们通过asm生成的类呢(这里我们叫临时类),他的加载器 属于我们自定义的ClassLoader,代码里叫 ApiJsonClassLoader。在收集 临时类 的注解信息(也就是ApiModelProperty注解)的时候,会使用该临时类的加载器(临时类 对应的ClassLoader),再通过Class.ForName() 方法反射获取 “io.swagger.annotations.ApiModelProperty”的Class信息 。那么问题就出现了。临时类的ClassLoader,是我们自定义的ApiJsonClassLoader,它并没有加载过 ApiModelProperty 类信息,所有就会报“Type io.swagger.annotations.ApiModelProperty not present” 这个异常。有人会说,不是双亲委派嘛,应该向上查找并加载的嘛,遗憾的是 LaunchedURLClassLoader 和我们自定义的 ApiJsonClassLoader,并没有继承关系。
还有为什么这个异常没有被打印并且也没有终止程序呢,因为 再解析类 注解的时候 catch 住了该异常但是没有throw 该异常,同时跳过当前注解的解析,直接return null。这就是是为什么注解 不生效的原因了。
至于是怎么解决这个问题的呢。想了一个不太优雅的办法。 我们生成的临时类,不直接通过defineClass加载到jvm内存。而是生成一个.class 字节码文件,放到一个固定磁盘区域,代码里配置的是 “D:/temp/” ,你自己随便。再通过LaunchedURLClassLoader 去加载 这个路径 下的类信息,这样 我们的临时类 和 ApiModelProperty 类都在一个类加载器下,自然就都能加载到了。
关于 执行java -jar xx.jar,注解里example和description 属性都不生效 的问题。处理的方式不太优雅。这个也被优化了。去掉了 .class 字节码文件临时存放的中转方案。直接使用 Proxy 的一个加载类的方法替换的。
swagger map类型的参数(一) 里 截图还能看看,其他的内容可以不用读了,直接github 拉代码操作起来。