1. 为什么我们需要一本Java API设计指南
在过去的十年里,我参与过数十个Java项目的API设计工作,从简单的工具类到企业级分布式系统的接口规范。最深刻的体会是:一个糟糕的API设计就像城市里规划混乱的单行道系统——即使每个路口都有交警(文档),开发者仍然会不断迷路。而优秀的API则像精心设计的立交桥,让调用者无需思考就能到达目的地。
API(Application Programming Interface)作为系统对外的契约,其质量直接影响着:
- 开发效率:直观的API能减少50%以上的文档查阅时间
- 系统稳定性:合理的抽象能避免80%以上的误用场景
- 维护成本:良好的扩展性使接口生命周期延长3-5倍
特别是在微服务架构成为主流的今天,API已从单纯的代码接口演变为服务间通信的核心媒介。我曾见证一个电商平台因为支付API的版本设计缺陷,导致所有商户系统在升级时集体崩溃,直接损失超过七位数。
2. API设计的核心原则
2.1 一致性高于一切
在Spring框架的源码中,你会发现所有配置类的方法都以with前缀开头(如withCacheManager),而查询方法都用get开头。这种严格的一致性让数百万开发者能凭直觉正确使用API。
一致性体现在:
- 命名规范:同类操作使用相同动词(create/insert/add的选择)
- 参数顺序:总是上下文对象在前,配置项在后
- 异常处理:统一使用checked exception还是unchecked exception
- 返回值:成功时返回对象,失败时抛出异常,不要混用返回码
反例:JDK中的Date类同时包含getYear()和getMonth(),但前者返回1900基准的偏移量,后者返回0-based数值,这种不一致性困扰了无数开发者。
2.2 最小惊讶原则(POLA)
好的API应该符合开发者直觉。比如:
List.add()应该将元素添加到末尾而非随机位置File.delete()应该在失败时抛出异常而非返回falseStringUtils.isEmpty()不应该对数字0返回true
我曾设计过一个分页查询API,最初版本使用offset和limit参数,但实际使用中发现60%的调用方都会错误计算offset。改为pageNum和pageSize后,错误率降到了5%以下。
2.3 防御性设计
考虑这个简单的坐标转换API:
java复制public Point convertCoordinate(Point input) {
return new Point(input.x * factor, input.y * factor);
}
存在的问题:
- 没有校验input是否为null
- 未处理整数乘法溢出
- 未说明factor的有效范围
改进版本:
java复制/**
* @param input 非空坐标点
* @param factor 必须在1-1000范围内
* @throws IllegalArgumentException 参数违反约束时抛出
*/
public Point convertCoordinate(@NonNull Point input, int factor) {
Objects.requireNonNull(input);
if (factor < 1 || factor > 1000) {
throw new IllegalArgumentException("Factor must be 1-1000");
}
try {
return new Point(
Math.multiplyExact(input.x, factor),
Math.multiplyExact(input.y, factor)
);
} catch (ArithmeticException e) {
throw new IllegalArgumentException("Result exceeds integer range", e);
}
}
3. 现代Java API设计模式
3.1 Builder模式的进阶用法
传统的Builder模式存在两个痛点:
- 必须按固定顺序调用方法
- 无法表达必选/可选参数
通过接口隔离可以解决这些问题:
java复制public interface UserBuilder {
interface NameBuilder {
AgeBuilder name(String name);
}
interface AgeBuilder {
OptionalBuilder age(int age);
}
interface OptionalBuilder {
OptionalBuilder email(String email);
OptionalBuilder phone(String phone);
User build();
}
static NameBuilder builder() {
return new UserConcreteBuilder();
}
}
// 使用示例
User user = UserBuilder.builder()
.name("张三")
.age(30)
.email("zhang@example.com")
.build();
这种设计强制调用者必须提供name和age,而email和phone是可选的。IntelliJ IDEA的自动补全功能会清晰地引导开发者完成构建过程。
3.2 响应式API设计
在Spring WebFlux等响应式框架中,API设计需要考虑背压(Backpressure)处理。对比两种返回类型:
传统方式:
java复制public List<Product> getProducts(int categoryId) {
// 可能加载百万级数据导致OOM
}
响应式改进:
java复制public Flux<Product> getProducts(int categoryId) {
return Flux.fromIterable(() -> database.streamProducts(categoryId))
.onBackpressureBuffer(1000); // 控制缓冲大小
}
关键设计点:
- 使用Publisher接口明确表示可能延迟的数据
- 提供背压策略配置(buffer/drop/latest)
- 在javadoc中注明线程安全要求
3.3 模块化API设计
Java 9引入的模块系统(JPMS)要求重新思考API边界。一个典型的模块化设计:
code复制module com.example.library {
exports com.example.library.api; // 公开API包
exports com.example.library.spi to com.example.plugin; // 受限SPI包
requires transitive java.sql; // 传递依赖
provides com.example.library.spi.ExtensionPoint
with com.example.library.internal.DefaultImpl;
}
最佳实践:
- 将API和实现分离到不同包
- 使用
requires transitive暴露必要依赖 - 通过
provides...with声明服务实现
4. API版本管理策略
4.1 语义化版本控制(SemVer)
版本号格式:主版本.次版本.修订号(MAJOR.MINOR.PATCH)
变更类型与版本号提升规则:
- 不兼容的API修改:MAJOR+1
- 向下兼容的功能新增:MINOR+1
- 向下兼容的问题修正:PATCH+1
示例:从1.4.3到:
- 2.0.0:移除了废弃方法
- 1.5.0:新增了查询条件参数
- 1.4.4:修复了空指针异常
4.2 多版本共存方案
在REST API中常见的实现方式:
code复制/v1/users/{id}
/v2/users/{id}
对于Java库,可以通过不同包路径实现:
java复制com.example.lib.v1.Api
com.example.lib.v2.Api
Spring框架采用的@RequestMapping版本控制:
java复制@GetMapping(value = "/users/{id}", headers = "X-API-Version=1")
public User getUserV1(@PathVariable String id) { ... }
@GetMapping(value = "/users/{id}", headers = "X-API-Version=2")
public UserV2 getUserV2(@PathVariable String id) { ... }
4.3 废弃API的优雅处理
分阶段废弃策略:
- 第一阶段:添加@Deprecated注解和@deprecated javadoc标记
java复制/**
* @deprecated 使用{@link #newMethod(String)}替代
*/
@Deprecated(since = "1.5", forRemoval = true)
public void oldMethod() {}
- 第二阶段:在编译时警告
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-Xlint:deprecation</arg>
</compilerArgs>
</configuration>
</plugin>
- 第三阶段:运行时警告
java复制public void oldMethod() {
Logger.global.warning("oldMethod is deprecated");
// 原有实现
}
5. 文档与测试的最佳实践
5.1 自文档化API技巧
- 使用枚举代替布尔参数
java复制// 模糊写法
public void refresh(boolean force) {}
// 清晰写法
public void refresh(RefreshMode mode) {}
enum RefreshMode { NORMAL, FORCE }
- 参数校验注解
java复制public User createUser(
@NotBlank String username,
@Email String email,
@Min(18) @Max(100) Integer age) {}
- 智能默认值
java复制public class Pagination {
private int size = 20; // 默认页大小
private int page = 1; // 默认页码
// 设置合理的边界
public void setSize(int size) {
this.size = Math.min(Math.max(size, 1), 100);
}
}
5.2 契约测试(Contract Test)
使用Pact框架验证API契约:
java复制@Pact(consumer = "ConsumerApp")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("user 123 exists")
.uponReceiving("get user request")
.path("/users/123")
.method("GET")
.willRespondWith()
.status(200)
.body(new PactDslJsonBody()
.integerType("id", 123)
.stringType("name", "John Doe"))
.toPact();
}
@Test
@PactTestFor(pactMethod = "createPact")
void testUserApi(MockServer mockServer) {
UserClient client = new UserClient(mockServer.getUrl());
User user = client.getUser(123);
assertThat(user.getName()).isEqualTo("John Doe");
}
5.3 可视化文档工具
结合Swagger和Asciidoctor生成交互式文档:
java复制@OpenAPIDefinition(
info = @Info(
title = "用户服务API",
version = "1.0",
description = "用户管理相关接口"
)
)
@Path("/users")
public class UserResource {
@GET
@Path("/{id}")
@Operation(summary = "获取用户详情")
@APIResponse(
responseCode = "200",
description = "用户对象",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = User.class)
)
)
public User getUser(@PathParam("id") int id) { ... }
}
通过maven插件生成HTML和PDF文档:
xml复制<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<executions>
<execution>
<phase>prepare-package</phase>
<goals><goal>process-asciidoc</goal></goals>
</execution>
</executions>
</plugin>
6. 性能敏感的API设计
6.1 对象复用策略
可变对象与不可变对象的选择:
java复制// 不可变设计(线程安全但产生GC压力)
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
// 可变设计(需注意线程安全)
public class MutablePoint {
private int x;
private int y;
public void set(int x, int y) {
this.x = x;
this.y = y;
}
}
// 对象池模式
public class PointPool {
private static final Queue<MutablePoint> pool = new ConcurrentLinkedQueue<>();
public static MutablePoint acquire(int x, int y) {
MutablePoint p = pool.poll();
if (p == null) p = new MutablePoint();
p.set(x, y);
return p;
}
public static void release(MutablePoint p) {
pool.offer(p);
}
}
6.2 零拷贝设计
避免不必要的对象复制:
java复制// 反例:产生临时byte数组
public String readString(InputStream in) throws IOException {
byte[] data = new byte[in.available()];
in.read(data);
return new String(data);
}
// 正例:使用ByteBuffer直接映射
public String readString(FileChannel channel) throws IOException {
ByteBuffer buf = channel.map(READ_ONLY, 0, channel.size());
return StandardCharsets.UTF_8.decode(buf).toString();
}
6.3 批处理API设计
对比两种查询接口设计:
java复制// 单次查询(N+1问题)
public interface UserDao {
User findById(int id);
}
// 批量查询
public interface UserDao {
Map<Integer, User> findByIds(Collection<Integer> ids);
default User findById(int id) {
return findByIds(Set.of(id)).get(id);
}
}
批量写入的优化方案:
java复制// 反例:逐条插入
public void saveUsers(List<User> users) {
for (User user : users) {
jdbcTemplate.update("INSERT...", user.getName(), user.getAge());
}
}
// 正例:批量操作
public void saveUsers(List<User> users) {
jdbcTemplate.batchUpdate("INSERT...",
users.stream()
.map(u -> new Object[]{u.getName(), u.getAge()})
.collect(Collectors.toList())
);
}
7. 安全敏感的API设计
7.1 敏感数据保护
安全的密码存储API设计:
java复制public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
}
// 实现示例
public class SCryptEncoder implements PasswordEncoder {
private final int cpuCost;
private final int memoryCost;
private final int parallelization;
public SCryptEncoder(int cpuCost, int memoryCost, int parallelization) {
this.cpuCost = 1 << cpuCost; // 2^N
this.memoryCost = memoryCost;
this.parallelization = parallelization;
}
@Override
public String encode(CharSequence rawPassword) {
byte[] salt = SecureRandom.getSeed(16);
byte[] hash = SCrypt.scrypt(
rawPassword.toString().getBytes(StandardCharsets.UTF_8),
salt,
cpuCost,
memoryCost,
parallelization,
32
);
return String.format(
"$s0$%s$%s",
encodeParameters(),
Base64.getEncoder().encodeToString(salt),
Base64.getEncoder().encodeToString(hash)
);
}
private String encodeParameters() {
return String.format(
"e=%d,m=%d,p=%d",
Integer.numberOfTrailingZeros(cpuCost),
memoryCost,
parallelization
);
}
}
7.2 权限控制设计
基于注解的权限控制:
java复制@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Permission {
String[] value();
Logical logical() default Logical.AND;
}
public enum Logical {
AND, OR
}
// 使用示例
public class OrderService {
@Permission({"order:read", "order:query"})
public Order getOrder(long id) { ... }
@Permission(value = {"order:create", "order:approve"}, logical = Logical.OR)
public void createOrder(Order order) { ... }
}
7.3 审计日志集成
审计日志API设计要点:
java复制public interface AuditLogger {
void log(AuditEvent event);
default void log(String action, String targetType, String targetId) {
log(new AuditEvent(
Instant.now(),
SecurityContext.getCurrentUser(),
action,
targetType,
targetId
));
}
}
public record AuditEvent(
Instant timestamp,
String principal,
String action,
String targetType,
String targetId,
Map<String, String> details
) {
public AuditEvent {
Objects.requireNonNull(timestamp);
Objects.requireNonNull(principal);
Objects.requireNonNull(action);
details = details == null ? Map.of() : Map.copyOf(details);
}
public AuditEvent withDetail(String key, String value) {
Map<String, String> newDetails = new HashMap<>(details);
newDetails.put(key, value);
return new AuditEvent(
timestamp, principal, action, targetType, targetId, newDetails
);
}
}
8. 设计模式在API中的妙用
8.1 装饰器模式增强API
JDK中的经典案例:IO流体系
java复制// 基础接口
public interface DataSource {
InputStream getInputStream() throws IOException;
}
// 装饰器基类
public abstract class DataSourceDecorator implements DataSource {
private final DataSource delegate;
protected DataSourceDecorator(DataSource delegate) {
this.delegate = delegate;
}
@Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
}
// 具体装饰器:压缩功能
public class CompressingDataSource extends DataSourceDecorator {
public CompressingDataSource(DataSource delegate) {
super(delegate);
}
@Override
public InputStream getInputStream() throws IOException {
return new GZIPInputStream(super.getInputStream());
}
}
// 使用示例
DataSource source = new CompressingDataSource(
new EncryptingDataSource(
new FileDataSource("data.bin")
)
);
8.2 策略模式实现灵活扩展
排序策略的可插拔设计:
java复制public interface SortStrategy<T> {
void sort(List<T> items, Comparator<? super T> comparator);
}
public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(List items, Comparator comparator) {
Collections.sort(items, comparator); // 使用快速排序
}
}
public class MergeSortStrategy implements SortStrategy {
@Override
public void sort(List items, Comparator comparator) {
// 实现归并排序
}
}
public class Sorter<T> {
private SortStrategy<T> strategy;
public Sorter(SortStrategy<T> strategy) {
this.strategy = strategy;
}
public void setStrategy(SortStrategy<T> strategy) {
this.strategy = strategy;
}
public void sort(List<T> items, Comparator<? super T> comp) {
strategy.sort(items, comp);
}
}
8.3 观察者模式实现事件通知
现代化的事件API设计:
java复制public class EventBus {
private final ConcurrentMap<Class<?>, CopyOnWriteArrayList<Consumer<?>>> handlers = new ConcurrentHashMap<>();
public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
.add(handler);
}
public <T> void publish(T event) {
@SuppressWarnings("unchecked")
var eventHandlers = (List<Consumer<T>>) (List<?>)
handlers.getOrDefault(event.getClass(), List.of());
eventHandlers.forEach(handler -> {
try {
handler.accept(event);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler()
.uncaughtException(Thread.currentThread(), e);
}
});
}
}
// 使用示例
EventBus bus = new EventBus();
bus.subscribe(OrderEvent.class, event -> {
System.out.println("处理订单事件: " + event);
});
bus.publish(new OrderCreatedEvent(orderId));
9. 国际化与本地化支持
9.1 错误消息国际化
基于ResourceBundle的设计:
java复制public class ApiException extends RuntimeException {
private final String errorCode;
private final Object[] args;
public ApiException(String errorCode, Object... args) {
super(resolveMessage(errorCode, args));
this.errorCode = errorCode;
this.args = args;
}
private static String resolveMessage(String code, Object[] args) {
// 从ThreadLocal获取当前Locale
Locale locale = LocaleContextHolder.getLocale();
ResourceBundle bundle = ResourceBundle.getBundle("errors", locale);
String pattern = bundle.getString(code);
return MessageFormat.format(pattern, args);
}
public String getErrorCode() { return errorCode; }
public Object[] getArgs() { return args; }
}
// 错误资源文件 errors_zh_CN.properties
user.not_found=用户ID {0} 不存在
9.2 日期时间处理
时区敏感的API设计:
java复制public class DateTimeApi {
private final Clock clock;
public DateTimeApi(Clock clock) {
this.clock = clock;
}
public ZonedDateTime now() {
return ZonedDateTime.now(clock);
}
public ZonedDateTime parse(String text) {
return ZonedDateTime.parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME);
}
// 测试时注入固定时钟
public static DateTimeApi forTest(LocalDateTime fixedTime) {
return new DateTimeApi(
Clock.fixed(fixedTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC"))
);
}
}
9.3 多语言文档生成
结合Maven和Asciidoc的多语言文档流程:
xml复制<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-en</id>
<configuration>
<sourceDirectory>src/main/docs</sourceDirectory>
<sourceDocumentName>index_en.adoc</sourceDocumentName>
<attributes>
<lang>en</lang>
</attributes>
</configuration>
</execution>
<execution>
<id>generate-zh</id>
<configuration>
<sourceDocumentName>index_zh.adoc</sourceDocumentName>
<attributes>
<lang>zh</lang>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
10. API性能监控与调优
10.1 埋点设计
基于Micrometer的监控指标:
java复制public class ApiMetrics {
private final MeterRegistry registry;
private final Map<String, Timer> timers = new ConcurrentHashMap<>();
public ApiMetrics(MeterRegistry registry) {
this.registry = registry;
}
public <T> T measure(String apiName, Supplier<T> supplier) {
Timer.Sample sample = Timer.start(registry);
try {
T result = supplier.get();
sample.stop(getTimer(apiName, "success"));
return result;
} catch (Exception e) {
sample.stop(getTimer(apiName, "error"));
throw e;
}
}
private Timer getTimer(String apiName, String outcome) {
return timers.computeIfAbsent(apiName + outcome, k ->
Timer.builder("api.calls")
.tags("name", apiName, "outcome", outcome)
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry)
);
}
}
10.2 分布式追踪
OpenTelemetry集成示例:
java复制public class TracedApi {
private final Tracer tracer;
public TracedApi(Tracer tracer) {
this.tracer = tracer;
}
public void execute() {
Span span = tracer.spanBuilder("api.execute")
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 业务逻辑
innerStep1();
innerStep2();
} finally {
span.end();
}
}
private void innerStep1() {
Span span = tracer.spanBuilder("api.step1")
.setAttribute("param", "value")
.startSpan();
try {
// 步骤逻辑
} finally {
span.end();
}
}
}
10.3 性能分析技巧
使用JMH进行基准测试:
java复制@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class ApiBenchmark {
private ApiClient client;
@Setup
public void setup() {
client = new ApiClient();
}
@Benchmark
public String simpleCall() {
return client.getData("test");
}
@Benchmark
@Threads(4)
public String concurrentCall() {
return client.getData("test");
}
}
分析火焰图生成步骤:
- 使用async-profiler收集数据
bash复制./profiler.sh -d 60 -f profile.html <pid>
- 在浏览器中打开profile.html分析热点
- 重点关注:
- 同步阻塞调用(红色部分)
- 高CPU占用方法(宽柱体)
- 不必要的对象分配(内存火焰图)
11. 前沿API设计趋势
11.1 响应式编程接口
Project Reactor的API设计哲学:
java复制public interface UserRepository {
Mono<User> findById(int id);
Flux<User> findAll();
Mono<Void> save(User user);
}
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
public Flux<User> getActiveUsers() {
return repository.findAll()
.filter(User::isActive)
.timeout(Duration.ofSeconds(5))
.onErrorResume(e -> {
log.warn("查询超时,返回缓存数据", e);
return getCachedUsers();
});
}
}
11.2 协程与虚拟线程
Java 19+虚拟线程API示例:
java复制public class VirtualThreadApi {
public CompletableFuture<String> fetchDataAsync(String url) {
return CompletableFuture.supplyAsync(() -> {
try (var httpClient = HttpClient.newHttpClient()) {
var request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
return httpClient.send(request, BodyHandlers.ofString())
.body();
} catch (Exception e) {
throw new CompletionException(e);
}
}, Executors.newVirtualThreadPerTaskExecutor());
}
public Stream<String> batchProcess(List<String> inputs) {
return inputs.parallelStream()
.map(input -> {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var subtask = scope.fork(() -> processSingle(input));
scope.join();
return subtask.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
11.3 GraalVM原生镜像支持
使API兼容原生编译的要点:
java复制// 注册反射配置
@RegisterReflectionForBinding({
User.class,
Page.class,
ApiResponse.class
})
public class NativeApi {
// 避免动态类加载
public static final List<String> ALLOWED_TYPES = List.of(
"type1", "type2", "type3"
);
public void validateType(String type) {
if (!ALLOWED_TYPES.contains(type)) {
throw new IllegalArgumentException("Invalid type");
}
}
// 资源文件处理
public InputStream getResource(String path) {
return getClass().getResourceAsStream(path);
}
}
12. 从设计到维护的全生命周期
12.1 API评审流程
有效的API评审清单:
- 一致性检查
- 是否遵循项目命名规范?
- 参数顺序是否符合惯例?
- 可用性验证
- 是否容易误用?
- 常见场景是否需要复杂调用?
- 扩展性评估
- 未来新增参数是否会破坏兼容性?
- 是否预留了扩展点?
- 性能考量
- 是否有潜在的性能陷阱?
- 大流量下是否稳定?
- 安全审查
- 是否暴露了敏感信息?
- 是否有注入风险?
12.2 变更管理策略
向后兼容的变更方式:
- 加法原则
- 只新增方法,不修改现有方法
- 新参数提供默认值
- 包装模式
java复制@Deprecated public void oldMethod() { newMethod(defaultValue); } public void newMethod(Param param) { // 新实现 } - 适配器层
java复制public class NewApi { private final OldApi delegate; public Result newMethod() { OldResult old = delegate.oldMethod(); return convert(old); } }
12.3 废弃API的迁移方案
分阶段迁移示例:
- 阶段一:标记废弃
java复制/** * @deprecated 使用{@link NewService#process()}替代 */ @Deprecated(since = "2.1", forRemoval = true) public void oldProcess() {} - 阶段二:兼容层
java复制public class NewService { public void process() { // 新实现 } } public class OldService { private final NewService newService; @Deprecated public void oldProcess() { newService.process(); } } - 阶段三:静态分析
xml复制<plugin> <artifactId>maven-enforcer-plugin</artifactId> <executions> <execution> <id>ban-deprecated</id> <goals><goal>enforce</goal></goals> <configuration> <rules> <bannedPlugins> <level>WARN</level> <includes> <include>com.example:old-artifact</include> </includes> </bannedPlugins> </rules> </configuration> </execution> </executions> </plugin>
13. 工具链与生态系统
13.1 API设计辅助工具
- ArchUnit架构测试
java复制@AnalyzeClasses(packages = "com.example.api")
public class ApiArchitectureTest {
@Test
public void dto_should_not_reference_domain() {
JavaClasses classes = new ClassFileImporter()
.importPackages("com.example.api");
ArchRule rule = noClasses()
.that().resideInAPackage("..dto..")
.should().dependOnClassesThat()
.resideInAPackage("..domain..");
rule.check(classes);
}
}
- Javadoc质量检查
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>check-javadoc</id>
<goals><goal>javadoc</goal></goals>
<phase>verify</phase>
<configuration>
<detectOfflineLinks>false</detectOfflineLinks>
<failOnError>true</failOnError>
<quiet>true</quiet>
</configuration>
</execution>
</executions>
</plugin>
13.2 代码生成技术
基于Annotation Processor的代码生成:
java复制@AutoService(Processor.class)
public class BuilderProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
for (Element element : env.getElementsAnnotatedWith(GenerateBuilder.class)) {
TypeElement classElement = (TypeElement) element;
String className = classElement.getSimpleName() + "Builder";
JavaFileObject file = processingEnv.getFiler()
.createSourceFile(classElement.getQualifiedName() + "Builder");
try (Writer writer = file.openWriter()) {
writeBuilderClass(writer, classElement, className);
} catch (IOException e) {
processingEnv.getMessager().printMessage(
ERROR, "Failed to generate builder: " + e
);
}
}
return true;
}
}
13.3 开发者体验(DX)优化
提升开发者体验的实践:
- 有意义的错误消息
java复制// 差的做法
throw new IllegalArgumentException("Invalid input");
// 好的做法
throw new IllegalArgumentException(
"Page size must be between 1 and 100, got " + size
);
- IDE智能提示增强
java复制/**
* @param timeout 超时时间(毫秒),建议值1000-5000
* @param retries 重试次数,0表示不重试
*/
public void configure(int timeout, int retries) {}
- 快速失败原则
java复制public class ApiConfig {
private final String endpoint;
public ApiConfig(String endpoint) {
this.endpoint = validateEndpoint(endpoint);
}
private String validateEndpoint(String url) {
try {
new URI(url).parseServerAuthority();
return url;
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid endpoint: " + url, e);
}
}
}
14. 行业特定API设计模式
14.1 金融行业API设计
货币处理的最佳实践:
java复制public final class Money implements Comparable<Money> {
private final BigDecimal amount;
private final Currency currency;
private Money(BigDecimal amount, Currency currency) {
this.amount = amount.setScale(currency.getDefaultFractionDigits());
this.currency = currency;
}
public static Money of(BigDecimal amount, Currency currency) {
return new Money(
Objects.requireNonNull(amount),
Objects.requireNonNull(currency)
);
}
public Money add(Money other) {
checkCurrencyMatch(other);
return new Money(amount.add(other.amount), currency);
}
private void checkCurrencyMatch(Money other) {
if (!this.currency.equals(other.currency)) {
throw new CurrencyMismatchException(
this.currency + " != " + other.currency
);
}
}
// 其他算术运算...
}
14.2 电商行业API设计
购物车API的并发控制:
java复制public class CartService {
private final CartRepository repository;
@Transactional(isolation = Isolation.REPEATABLE_READ)
public Cart addItem(long cartId, Item item, int quantity) {
Cart cart = repository.findById(cartId)
.orElseThrow(() -> new CartNotFoundException(cartId));
cart.addItem(item, quantity);
repository.save(cart);
return cart;
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public CheckoutResult checkout(long cartId) {
Cart cart = repository.findByIdWithLock(cartId)
.orElseThrow(() -> new CartNotFoundException(cartId));
if (cart.isEmpty()) {
throw new EmptyCartException();
}
InventoryReservation reservation = inventoryService.reserve(
cart.getItems()
);
Order order = createOrder(cart, reservation);
cart.clear();
repository.save(cart);
return new CheckoutResult(order.getId());
}
}
14.3 物联网(IoT)API设计
设备状态上报接口:
java复制public interface DeviceGateway {
CompletableFuture<Void> reportStatus(
String deviceId,
DeviceStatus status,
Instant timestamp
);
Flux<DeviceCommand> subscribeCommands(String deviceId);
}
// 协议缓冲区定义
message DeviceStatus {
string device_id = 1;
map<string, double> metrics = 2;
google.protobuf.Timestamp timestamp = 3;
}
message DeviceCommand {
string command_id = 1;