未命名
今天发现项目启动时,会打印一堆报错。吓了一跳。
查看之后发现是swagger的错误提示。并不影响功能。但是看着一堆错误,也闹心不是?
所以还是要解决一下。
1 | 2020-11-04 10:50:46,383 [http-nio-9292-exec-3] WARN i.s.m.parameters.AbstractSerializableParameter:421 - Illegal DefaultValue null for parameter type integer |
通过查看AbstractSerializableParameter412行的源码
1 | if (BaseIntegerProperty.TYPE.equals(type)) { |
发现如果属性类型是Integer,那么就转成Long
而example默认为””,导致转换错误。
第一种做法
所以,得到解决方法1:
实体类中,Integer类型的属性加@ApiModelProperty时,必须要给example参数赋值,且值必须为数字类型。
1 | @ApiModelProperty(value = "", example = "0") |
但是,如果项目中有大量的修改点,那改动起来就太累了。
第二种做法
然后再看这个代码
1 | if (example == null) { |
在前面有个null判断,如果加个“”空字符串判断其实也就ok了
但是,这需要我们能下载swagger的源码,自己打包发布。
然后替换掉官方版本
第三种做法
这可以算是swagger的一个小bug,在网上看到说这个问题是swagger-models:1.5.20的bug。
看了下我这确实是这个版本。

1.5.21版本已经修改了这个问题,所以我们也可以通过替换依赖解决
swagger-models是springfox-swagger2依赖进来的
1 | <dependency> |
查看了下他的依赖,是1.5.20版本

然后查看了最新的3.0.0版本的springfox-swagger2,依赖的依然是1.5.20的swagger-models
所以只能手动指定swagger-models的版本。话说回来即使高版本的swagger2依赖了1.5.21的models,我也不敢直接升级springfox-swagger2,毕竟这动作有点大。需要做全面的回归测试才敢发生产。
1 | <!--Swagger-UI API文档生产工具--> |
通过在springfox-swagger2依赖之前,手工加上swagger-models1.5.21版本的依赖
根据maven的依赖原则,同路径长度下,谁先声明谁优先,把1.5.21放在上面,即可排除springfox-swagger2依赖的1.5.20版本
不需要手动加exclusions

推荐第三种做法。我这边发版测试ok。
未命名
用java打tar包
写了一个打tar包的工具类,可以递归打整个目录。没有中文文件名的问题。
引入依赖:
1 | <dependency> |
代码:
1 | public class TarUtil { |
效果:

未命名
未命名
未命名
开发自己的starter
starter的好处是,集成众多依赖,提供一个一站式的依赖项。 Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件)进行自动配置。 使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖, Spring Boot就能自动扫描并加载相应的模块。
命名
spring官方的starter命令为spring-boot-starter-xxx,所以我们开发的项目不要以spring-boot开头。 建议写成:xxx公司组织-spring-boot-starter-yyy模块
开发步骤
1.新建Maven项目,在项目的POM文件中定义使用的依赖;
2.新建配置类,写好配置项和默认的配置值,指明配置项前缀;
3.新建自动装配类,使用@Configuration和@Bean来进行自动装配;
4.新建spring.factories文件,指定Starter的自动装配类;
具体代码
1,新建一个springboot项目,在pom文件中增加以下依赖:
1 | <dependency> |
主要的作用是在编译时在META-INF下生成spring-configuration-metadata.json 文件,该文件主要为IDE使用。 即可以通过在application.properties文件中通过ctrl + 点击进入配置属性所在的类中
2,配置类
1 | @ConfigurationProperties(prefix = "spring101") |
关键是要指定配置项的前缀。这些配置项,也可以设置默认值。
这些配置项可以让starter的使用者进行配置
3,自动装配类
自动装配类是整个starter的逻辑核心。根据配置项的值,自动注入合适的bean。
在这个项目中,有一个抽象的AbstractMyService,代表客户端会使用的服务bean。
MyStarterServiceV1和MyStarterServiceV2是根据条件注入的具体实现类。
也就是客户端在配置spring101.version=v1会使用MyStarterServiceV1,
配置spring101.version=v2会使用MyStarterServiceV2
1 | @Configuration |
4,新建spring.factories文件,指定Starter的自动装配类。
在resources下新建META-INF文件夹,新建spring.factories文件。内容为:
1 | #指定autoconfigure加载的自动装配类是哪个 |
5,打包测试 maven clean install
先install到本地仓库测试
新创建一个普通的springboot工程:mystarter-use 依赖
在application.properties增加配置
1 | spring101.age=22 |
写一个测试类
1 | @Component |
通过修改spring101.version的值可以观察到,实例化了不同的service
注意:
pom文件中的
1 | <build> |
要去掉,否则项目jar包的目录不合规范
未命名
未命名
未命名
未命名
使用MapStruct 高效优雅的进行Bean转换
烦人的Bean 转换
对于代码中 JavaBean之间的转换, 一直是困扰我很久的事情。 在开发的时候我看到业务代码之间有很多的 JavaBean 之间的相互转化, 非常的影响观感, 却又不得不存在。 我后来想的一个办法就是通过反射, 或者自己写很多的转换器。
第一种通过反射的方法确实比较方便, 但是现在无论是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能。 虽然我们可以进行反射信息的缓存来提高性能。 但是像这种的话, 需要类型和名称都一样才会进行映射, 有很多时候, 由于不同的团队之间使用的名词不一样, 还是需要很多的手动 set/get 等功能。
第二种的话就是会很浪费时间, 而且在添加新的字段的时候也要进行方法的修改。 不过, 由于不需要进行反射, 其性能是很高的。
MapStruct 带来的改变
MapSturct 是一个生成 类型安全, 高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。
抓一下重点:
- 注解处理器
- 可以生成
JavaBean之间那的映射代码 - 类型安全, 高性能, 无依赖性
从字面的理解, 我们可以知道, 该工具可以帮我们实现 JavaBean 之间的转换, 通过注解的方式。
同时, 作为一个工具类,相比于手写, 其应该具有便捷, 不容易出错的特点。
MapStruct 入门
引入依赖
1 | <dependency> |
我现在在对接一个系统,传过来的是支付信息PayInfo。后面数据库存的是TExpensesRecords消费记录。我需要进行bean的转换。如果我一点点的写get/set真是太烦人了,无脑的体力劳动。
甚至中间还牵涉了很多类型转换,嵌套之类的繁琐操作,而我们想要的只是建立它们之间的映射关系而已。有没有一种通用的映射工具来帮我们搞定这一切。当然有而且还不少。有人说apache的BeanUtil.copyProperties可以实现,但是性能差而且容易出异常,很多规范严禁使用这种途径。以下是对几种对象映射框架的对比,大多数情况下 MapStruct 性能最高。原理类似于lombok ,MapStruct都是在编译期进行实现,而且基于Getter、Setter,没有使用反射所以一般不存在运行时性能问题。
测试
我现在有两个类
一个是接口VO类
1 | @Data |
一个是数据库DO类
1 | @Data |
可以看到,他们有一些属性是同名的。
我们一步步来写。
先写一个转换接口

写一个测试类
1 | @Test |

可以看到,大部分相同名字的属性已经完成转换。但是名字不一致的,需要我们单独配置下。
处理不同名的属性

代码也很好理解,就是将源的payCode字段映射到目标的tradeId字段
看下测试结果
1 | TExpensesRecords(id=null, tradeId=20200513_01471111_120_40_1589359038744, userCode=null, serviceId=001, serviceName=服务商A, restaurantCode=A001, restaurantName=餐厅A, machineCode=M001, expensesType=1, amount=39.3, expensesDate=null) |
tradeId映射成功。而且totalMoney是String,映射成BigDecimal的amount也成功了
原理


原理类似于lombok ,MapStruct都是在编译期对接口进行实现,而且基于Getter、Setter,没有使用反射所以一般不存在运行时性能问题。 类型不同,会自动进行转换。
Spring 注入的方式
上面的例子是默认的方式
1 | PayInfoMapper INSTANCE = Mappers.getMapper(PayInfoMapper.class); |
在正常的项目中,一般和spring整合使用
就是在 @Mapper 后面加入 componentModel=”spring”
在用到的地方就可以使用@Autowired注入了
注解说明
1 | @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口 |

高级使用
多对一
两个源对象都有同样的字段,需要指定使用哪个
1 | @Mappings({ |
类型转换
List和String互转
需求背景:有一个业务对象,数据库里村的是分号隔开的String,在返回给前台是需要转换成List
dao类中
1 | @ApiModelProperty(value = "附件地址,以分号隔开") |
vo类中
1 | @ApiModelProperty(value = "附件地址列表") |
mapStruct类中的写法
1 | @Mapper(componentModel = "spring") |
当需要String转List时,MapStruct会自动调用str2List。当需要list转string时,会自动调用
list2Str




