Java中SDK接口与普通Java程序的区别,以及如何编写规范的接口

羌俊恩 社区创始人
优质创作者: 运维技术领域
领域专家: 操作系统技术领域
2026-03-03 12:01:34

一、总体对比

维度SDK接口普通Java程序
使用方式被其他程序调用(API调用)独立运行(main方法启动)
交互方式网络通信(HTTP/RPC)本地方法调用
数据格式JSON/XML/Protobuf等标准格式Java对象直接传递
状态管理无状态(Stateless)设计可以有状态
并发处理需要处理高并发通常是单用户使用
安全要求需要鉴权、限流、防攻击相对较低
文档要求必须提供API文档通常只需要代码注释

二、理解

// 普通Java程序:就像自己做饭
public class CookDinner {
    public static void main(String[] args) {
        // 自己切菜、炒菜、吃饭
        Vegetables veggies = new Vegetables();
        veggies.wash();
        veggies.cut();
        
        Rice rice = new Rice();
        rice.cook();
        
        System.out.println("开饭啦!");
    }
}

// SDK接口:就像叫外卖
@RestController
public class TakeoutAPI {
    
    @PostMapping("/order")
    public ResponseEntity<Food> orderFood(@RequestBody OrderRequest request) {
        // 1. 验证用户身份
        if (!validateUser(request.getUserId())) {
            return ResponseEntity.status(401).build();
        }
        
        // 2. 检查库存
        if (!checkInventory(request.getFoodId())) {
            return ResponseEntity.status(400).body("库存不足");
        }
        
        // 3. 处理订单(可能调用厨房系统)
        Food food = kitchenService.cook(request);
        
        // 4. 返回标准化的响应
        return ResponseEntity.ok(food);
    }
}

三、Java接口编写标准规范

3.1. RESTful API设计规范

package com.example.api;

import io.swagger.annotations.*;
import lombok.Data;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.List;

/**
 * 用户管理接口
 * @version 1.0
 * @since 2024-01-01
 */
@RestController
@RequestMapping("/api/v1/users")
@Api(tags = "用户管理", description = "用户相关的CRUD操作")
public class UserController {

    /**
     * 创建用户
     * @param request 创建用户请求
     * @return 创建的用户信息
     */
    @PostMapping
    @ApiOperation(value = "创建用户", notes = "创建一个新的用户账号")
    @ApiResponses({
        @ApiResponse(code = 201, message = "创建成功"),
        @ApiResponse(code = 400, message = "请求参数错误"),
        @ApiResponse(code = 409, message = "用户已存在")
    })
    public ResponseEntity<UserResponse> createUser(
            @Valid @RequestBody CreateUserRequest request) {
        // 业务逻辑
        UserResponse response = userService.create(request);
        return ResponseEntity.status(201).body(response);
    }

    /**
     * 查询用户(分页)
     * @param page 页码(从0开始)
     * @param size 每页大小
     * @return 用户列表
     */
    @GetMapping
    @ApiOperation("分页查询用户")
    public ResponseEntity<PageResponse<UserResponse>> listUsers(
            @RequestParam(defaultValue = "0") @Min(0) int page,
            @RequestParam(defaultValue = "20") @Min(1) @Max(100) int size) {
        PageResponse<UserResponse> users = userService.list(page, size);
        return ResponseEntity.ok(users);
    }

    /**
     * 获取用户详情
     * @param userId 用户ID
     * @return 用户详情
     */
    @GetMapping("/{userId}")
    @ApiOperation("获取用户详情")
    public ResponseEntity<UserDetailResponse> getUser(
            @PathVariable @ApiParam("用户ID") String userId) {
        UserDetailResponse user = userService.getById(userId);
        return ResponseEntity.ok(user);
    }

    /**
     * 更新用户
     * @param userId 用户ID
     * @param request 更新信息
     * @return 更新后的用户
     */
    @PutMapping("/{userId}")
    @ApiOperation("更新用户信息")
    public ResponseEntity<UserResponse> updateUser(
            @PathVariable String userId,
            @Valid @RequestBody UpdateUserRequest request) {
        UserResponse user = userService.update(userId, request);
        return ResponseEntity.ok(user);
    }

    /**
     * 删除用户
     * @param userId 用户ID
     * @return 无内容
     */
    @DeleteMapping("/{userId}")
    @ApiOperation("删除用户")
    public ResponseEntity<Void> deleteUser(@PathVariable String userId) {
        userService.delete(userId);
        return ResponseEntity.noContent().build();
    }
}

3.2、 请求/响应对象规范

package com.example.dto;

import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import javax.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 创建用户请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CreateUserRequest {
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
    private String username;
    
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
    private String password;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
    
    @Min(value = 1, message = "年龄必须大于0")
    @Max(value = 150, message = "年龄必须小于150")
    private Integer age;
    
    private String address;
    
    private List<@NotBlank String> tags;
}

/**
 * 用户响应
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserResponse {
    private String id;
    private String username;
    private String email;
    private String phone;
    private Integer age;
    private String status;
    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;
    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updatedAt;
}

/**
 * 分页响应
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PageResponse<T> {
    private List<T> content;        // 数据列表
    private int page;                // 当前页码
    private int size;                // 每页大小
    private long totalElements;      // 总记录数
    private int totalPages;          // 总页数
    private boolean first;           // 是否是第一页
    private boolean last;            // 是否是最后一页
}

3.3、统一异常处理

package com.example.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.BAD_REQUEST.value())
                .error("参数验证失败")
                .message("请求参数不符合要求")
                .details(errors)
                .path(getCurrentPath())
                .build();
        
        return ResponseEntity.badRequest().body(response);
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        log.error("业务异常: {}", ex.getMessage());
        
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(ex.getCode())
                .error("业务处理失败")
                .message(ex.getMessage())
                .path(getCurrentPath())
                .build();
        
        return ResponseEntity.status(ex.getCode()).body(response);
    }

    /**
     * 处理系统异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleSystemException(Exception ex) {
        log.error("系统异常: ", ex);
        
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
                .error("系统内部错误")
                .message("服务器处理请求时发生错误")
                .path(getCurrentPath())
                .build();
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }

    private String getCurrentPath() {
        // 获取当前请求路径
        return "";
    }
}

/**
 * 错误响应
 */
@Data
@Builder
public class ErrorResponse {
    private LocalDateTime timestamp;   // 错误时间
    private int status;                 // HTTP状态码
    private String error;               // 错误类型
    private String message;             // 错误信息
    private Map<String, String> details;// 详细错误
    private String path;                // 请求路径
}

/**
 * 业务异常
 */
public class BusinessException extends RuntimeException {
    private final int code;
    
    public BusinessException(String message) {
        this(400, message);
    }
    
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }
    
    public int getCode() {
        return code;
    }
}

四、API文档规范(Swagger/OpenAPI)

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Arrays;
import java.util.List;

@Configuration
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
                .paths(PathSelectors.any())
                .build()
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("文件上传服务API文档")
                .description("提供文件上传、下载、管理等接口")
                .version("1.0.0")
                .contact(new Contact("技术团队", "https://example.com", "dev@example.com"))
                .license("Apache 2.0")
                .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
                .build();
    }

    private List<SecurityScheme> securitySchemes() {
        return Arrays.asList(
            new ApiKey("Authorization", "Authorization", "header")
        );
    }

    private List<SecurityContext> securityContexts() {
        return Arrays.asList(
            SecurityContext.builder()
                .securityReferences(securityReferences())
                .operationSelector(operation -> 
                    operation.requestMappingPattern().startsWith("/api/"))
                .build()
        );
    }

    private List<SecurityReference> securityReferences() {
        return Arrays.asList(
            new SecurityReference("Authorization", 
                new AuthorizationScope[]{
                    new AuthorizationScope("global", "访问所有接口")
                }
            )
        );
    }
}

五、接口版本管理规范

/**
 * API版本管理示例
 */
@RestController
public class VersionedApiController {

    // 方式1:URL路径版本
    @GetMapping("/v1/users")
    public ResponseEntity<List<User>> getUsersV1() {
        return ResponseEntity.ok(userService.getUsersV1());
    }
    
    @GetMapping("/v2/users")
    public ResponseEntity<List<UserV2>> getUsersV2() {
        return ResponseEntity.ok(userService.getUsersV2());
    }
    
    // 方式2:请求头版本
    @GetMapping(value = "/users", headers = "API-VERSION=1")
    public ResponseEntity<List<User>> getUsersHeaderV1() {
        return ResponseEntity.ok(userService.getUsersV1());
    }
    
    @GetMapping(value = "/users", headers = "API-VERSION=2")
    public ResponseEntity<List<UserV2>> getUsersHeaderV2() {
        return ResponseEntity.ok(userService.getUsersV2());
    }
    
    // 方式3:内容类型版本
    @GetMapping(value = "/users", produces = "application/vnd.company.v1+json")
    public ResponseEntity<List<User>> getUsersContentV1() {
        return ResponseEntity.ok(userService.getUsersV1());
    }
    
    @GetMapping(value = "/users", produces = "application/vnd.company.v2+json")
    public ResponseEntity<List<UserV2>> getUsersContentV2() {
        return ResponseEntity.ok(userService.getUsersV2());
    }
}

六、接口安全规范

package com.example.security;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping("/api/secure")
public class SecureApiController {

    /**
     * 需要管理员权限
     */
    @PostMapping("/admin/operation")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<Result> adminOperation(@Valid @RequestBody AdminRequest request) {
        // 只有管理员才能调用
        return ResponseEntity.ok(adminService.execute(request));
    }

    /**
     * 需要用户权限,且只能操作自己的数据
     */
    @PutMapping("/users/{userId}")
    @PreAuthorize("authentication.principal.id == #userId")
    public ResponseEntity<UserResponse> updateUser(
            @PathVariable String userId,
            @Valid @RequestBody UpdateUserRequest request) {
        // 只能更新自己的信息
        return ResponseEntity.ok(userService.update(userId, request));
    }

    /**
     * 接口限流
     */
    @GetMapping("/limited-api")
    @RateLimiter(key = "#userId", rate = 10, capacity = 10)
    public ResponseEntity<String> limitedApi(@RequestParam String userId) {
        return ResponseEntity.ok("success");
    }
}

/**
 * 自定义注解实现限流
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
    String key() default "";
    int rate() default 10;      // 速率
    int capacity() default 10;  // 容量
}

七、日志记录规范

package com.example.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

@Slf4j
@Aspect
@Component
public class ApiLogAspect {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object logApiCall(ProceedingJoinPoint joinPoint) throws Throwable {
        // 生成请求ID
        String requestId = UUID.randomUUID().toString();
        
        // 获取请求信息
        ServletRequestAttributes attributes = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        
        long startTime = System.currentTimeMillis();
        
        try {
            // 记录请求日志
            log.info("Request [{}]开始: method={}, url={}, ip={}, params={}", 
                requestId,
                request.getMethod(),
                request.getRequestURL(),
                request.getRemoteAddr(),
                objectMapper.writeValueAsString(joinPoint.getArgs())
            );
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 记录响应日志
            long costTime = System.currentTimeMillis() - startTime;
            log.info("Request [{}]完成: 耗时={}ms, 响应={}", 
                requestId, 
                costTime,
                objectMapper.writeValueAsString(result)
            );
            
            return result;
            
        } catch (Exception e) {
            long costTime = System.currentTimeMillis() - startTime;
            log.error("Request [{}]异常: 耗时={}ms, 错误={}", 
                requestId, costTime, e.getMessage(), e);
            throw e;
        }
    }
}

八、接口性能优化规范

package com.example.optimization;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@RestController
public class OptimizedApiController {

    /**
     * 1. 使用缓存
     */
    @GetMapping("/api/cached-data")
    public ResponseEntity<Data> getCachedData(@RequestParam String key) {
        // 先从缓存获取
        Data cached = redisTemplate.opsForValue().get("data:" + key);
        if (cached != null) {
            return ResponseEntity.ok(cached);
        }
        
        // 缓存不存在,从数据库查询
        Data data = dataService.queryFromDB(key);
        
        // 存入缓存(5分钟过期)
        redisTemplate.opsForValue().set("data:" + key, data, 5, TimeUnit.MINUTES);
        
        return ResponseEntity.ok(data);
    }

    /**
     * 2. 异步处理
     */
    @GetMapping("/api/async-process")
    @Async
    public CompletableFuture<ResponseEntity<ProcessResult>> asyncProcess() {
        // 异步处理耗时操作
        ProcessResult result = heavyProcess();
        return CompletableFuture.completedFuture(ResponseEntity.ok(result));
    }

    /**
     * 3. 批量操作
     */
    @PostMapping("/api/batch-users")
    public ResponseEntity<List<UserResponse>> batchGetUsers(
            @RequestBody List<String> userIds) {
        // 批量查询,避免循环单个查询
        List<UserResponse> users = userService.batchGetByIds(userIds);
        return ResponseEntity.ok(users);
    }

    /**
     * 4. 压缩响应
     */
    @GetMapping(value = "/api/large-data", produces = "application/json")
    public ResponseEntity<LargeData> getLargeData() {
        LargeData data = dataService.getLargeData();
        return ResponseEntity.ok()
                .header("Content-Encoding", "gzip")
                .body(data);
    }
}

九、接口测试规范

package com.example.test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.*;

@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    /**
     * 测试创建用户
     */
    @Test
    public void testCreateUser() throws Exception {
        String requestJson = """
            {
                "username": "testuser",
                "password": "password123",
                "email": "test@example.com",
                "phone": "13800138000",
                "age": 25
            }
            """;

        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestJson))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.username", is("testuser")))
                .andExpect(jsonPath("$.email", is("test@example.com")))
                .andExpect(jsonPath("$.id", notNullValue()));
    }

    /**
     * 测试参数校验
     */
    @Test
    public void testCreateUser_Validation() throws Exception {
        String requestJson = """
            {
                "username": "te",  // 太短
                "password": "123", // 太短
                "email": "not-email" // 格式错误
            }
            """;

        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestJson))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.details.username", notNullValue()))
                .andExpect(jsonPath("$.details.password", notNullValue()))
                .andExpect(jsonPath("$.details.email", notNullValue()));
    }
}

十、接口设计原则总结

1. RESTful设计原则

  • 资源导向:使用名词而不是动词(/users 而不是 /getUsers)

  • HTTP方法语义:GET(查询)、POST(创建)、PUT(更新)、DELETE(删除)

  • 状态码使用:200(成功)、201(创建成功)、400(参数错误)、404(资源不存在)

2. 命名规范

  • URL:小写字母,单词间用连字符(/user-profiles)

  • 字段:驼峰命名(userId, createdAt)

  • 枚举:大写字母,下划线分隔(ACTIVE, PENDING_APPROVAL)

3. 安全规范

  • 鉴权:所有接口都需要鉴权(除登录注册外)

  • 防篡改:敏感操作需要签名验证

  • 防重放:添加时间戳和nonce

  • 限流:防止恶意调用

4. 版本控制

  • URL路径:/v1/users, /v2/users

  • 请求头:Accept: application/vnd.company.v1+json

  • 向后兼容:新增字段不影响旧版本

...全文
543 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

136,364

社区成员

发帖
与我相关
我的任务
社区描述
欢迎各位小伙伴积极活跃投稿,将日常IT打怪中遇到的问题,优秀解决方案,或自己的博客认为比较经典的文章分享到这里,共同营造一个悦享的问题库,Review及减少重复造车轮的无限循环,此致敬礼
devops经验分享开源 技术论坛(原bbs) 北京·西城区
社区管理员
  • 羌俊恩
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

本社区:旨在推进运维云化知识库建设,为更多运维提供参考及经验交流;加入社区的同学,如果运维工作中遇到问题或最后得到解决,有劳凑空发帖,分享共建FAQ社区。

得到解决方案的同学,请积极为发帖的同学点赞、评论、能力范围内打赏激励。另外本社区采用游戏修仙角色作为鼓励,对不同贡献者授予对应“勋章”。

最后祝愿社区繁荣昌盛,此致敬礼!!!

试试用AI创作助手写篇文章吧