第一次接触Swagger是在2015年参与一个电商平台项目时。当时团队正在为API文档的维护问题头疼——前端同事总抱怨文档更新不及时,后端则苦于手动维护文档的繁琐。直到架构师引入了Swagger,这个局面才彻底改变。而@ApiModel和@ApiModelProperty这两个注解,就是让Swagger能够自动生成漂亮文档的"魔法棒"。
简单来说,@ApiModel用来修饰整个Java类,相当于给这个模型贴个标签;@ApiModelProperty则用于修饰类中的字段,相当于给每个属性添加说明。它们就像图书馆里的分类标签和书籍简介,让查阅API文档的人能快速理解每个接口的参数和返回值结构。
要使用这些注解,首先需要在项目中引入Swagger依赖。以Maven项目为例:
xml复制<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.19</version>
</dependency>
在实际项目中,我习惯把这两个注解用在DTO(数据传输对象)和VO(视图对象)上。比如用户注册接口的请求体和响应体:
java复制@ApiModel(description = "用户注册请求体")
public class UserRegisterDTO {
@ApiModelProperty(value = "用户名", required = true, example = "user123")
private String username;
@ApiModelProperty(value = "密码", required = true, example = "P@ssw0rd")
private String password;
}
打开io.swagger.annotations.ApiModel的源码,你会发现这个注解的设计非常精巧。它使用了Java元注解来定义自己的行为:
java复制@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ApiModel {
String value() default "";
String description() default "";
Class<?> parent() default Void.class;
String discriminator() default "";
Class<?>[] subTypes() default {};
String reference() default "";
}
这里有几个关键点值得注意:
在实际开发中,description属性是我最常用的。比如在电商系统中:
java复制@ApiModel(description = "商品SKU信息,包含价格、库存等核心属性")
public class ProductSku {
// 类实现...
}
parent和subTypes属性在处理继承关系时特别有用。比如支付系统中有多种支付方式:
java复制@ApiModel(description = "支付基础信息",
subTypes = {AliPay.class, WechatPay.class, UnionPay.class})
public abstract class BasePayment {
// 公共字段...
}
@ApiModel(description = "支付宝支付信息")
public class AliPay extends BasePayment {
// 支付宝特有字段...
}
@ApiModelProperty的源码比@ApiModel复杂得多,因为它需要处理各种字段级别的细节:
java复制@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiModelProperty {
String value() default "";
String name() default "";
String allowableValues() default "";
boolean required() default false;
String example() default "";
// 其他属性...
}
value属性是最基础的用法,相当于字段的说明文字。但在实际项目中,我发现这些属性的组合使用更能发挥威力:
java复制@ApiModelProperty(value = "手机号码", required = true, example = "13800138000")
private String mobile;
java复制@ApiModelProperty(value = "订单状态",
allowableValues = "CREATED,PAID,SHIPPED,COMPLETED")
private String orderStatus;
java复制@ApiModelProperty(hidden = true) // 不显示在文档中
private String internalSecret;
在金融项目中,我们曾用position属性来调整字段显示顺序:
java复制@ApiModelProperty(value = "账户ID", position = 1)
private Long accountId;
@ApiModelProperty(value = "账户类型", position = 2)
private String accountType;
经过多个项目的实践,我总结出一些使用这两个注解的经验:
场景一:枚举类型的处理
java复制@ApiModel(description = "任务状态")
public enum TaskStatus {
@ApiModelProperty(value = "待处理")
PENDING,
@ApiModelProperty(value = "进行中")
PROCESSING,
@ApiModelProperty(value = "已完成")
COMPLETED
}
场景二:处理复杂嵌套对象
java复制@ApiModel(description = "订单详情")
public class OrderDetail {
@ApiModelProperty(value = "基础信息")
private OrderBaseInfo baseInfo;
@ApiModelProperty(value = "商品列表")
private List<OrderItem> items;
}
@ApiModel(description = "订单商品项")
public class OrderItem {
@ApiModelProperty(value = "商品ID")
private Long productId;
@ApiModelProperty(value = "购买数量")
private Integer quantity;
}
常见坑点:
在微服务架构中,我推荐在API模块中集中定义这些DTO,这样无论是提供方还是消费方都能看到一致的文档。同时,建议把Swagger文档集成到CI流程中,确保文档与代码同步更新。