60分钟搞懂Spring Boot,我这个Rust迷都学会了

韩秀英
2026-05-04 06:37
阅读 1610

上周五晚上十点半,我坐在工位上盯着IDEA发呆。窗外深圳湾的灯光还亮着,隔壁组又在联调双11大促的接口。而我,一个天天琢磨Rust内存安全的“叛徒”,居然被产品经理拉着要临时搭个Java后端服务——就因为他说“你不是会编程嘛”。

说实话,自从开始研究Rust那套所有权机制以后,我对Java生态有点疏远了。但没办法,项目紧急,领导一句“先跑起来再说”,我就得硬着头皮上。好在Spring Boot这玩意儿这几年进化得挺人性化,花了不到一小时居然真把基础架子搭起来了。

这篇文章就是记录我这个“准跳槽人”在迷茫期的一次快速突击。坐标深圳南山科技园,每天8点准时开工的早起选手,最近一边刷LeetCode一边看Rust异步编程,结果突然被拉回Java世界——不得不说,Spring Boot确实比五年前友好太多了。

为啥又是Java?

我知道很多人(包括我自己)都在观望新语言,比如Rust、Go、甚至TypeScript全栈。但在国内大厂,特别是腾讯系公司扎堆的深圳,Java依然是后端主力。上周和前同事吃饭,他现在在某大厂做AI平台,后端还是Spring Cloud全家桶。他说:“RAG系统?前端用React,后端照样是Java接Embedding API。”

说到RAG(Retrieval-Augmented Generation),最近我们团队也在搞类似的东西。用户输入问题,系统从知识库里检索相关文档,再交给大模型生成答案。这种架构里,后端要处理向量检索、文档切片、API编排,前端负责交互展示。资源调度、缓存策略、错误降级——这些都不是玩具项目能覆盖的。

所以即便我想跳槽去玩Rust,也得先把眼前这套Java技术栈理清楚。毕竟简历上不能写“只会Hello World in Rust”吧?

环境准备:别被Maven吓退

首先,别被Maven那一堆XML劝退。现在的Spring Initializr已经做得像脚手架一样简单。打开 https://start.spring.io,选好Java版本(建议17)、打包方式(jar)、依赖项,点Generate就行。

我当时选了这些核心依赖:

  • Spring Web(必须的)
  • Spring Data JPA(操作数据库)
  • H2 Database(内存数据库,开发用)
  • Lombok(省掉getter/setter,爽!)
<!-- pom.xml 关键依赖节选 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

注意:生产环境千万别用H2!这只是为了快速验证逻辑。我们线上用的是TiDB,兼容MySQL协议,但事务隔离级别要特别注意——去年双11就因为幻读问题导致库存超卖,运维半夜打电话骂人。

写个最简API:别一上来就分层

很多教程一上来就给你整Controller-Service-DAO三层,搞得新人一脸懵。其实Spring Boot最香的地方就是“约定优于配置”。你完全可以先写个单文件搞定所有逻辑,跑通再说。

@RestController
@SpringBootApplication
public class QuickStartApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuickStartApplication.class, args);
    }

    @GetMapping("/hello")
    public String sayHello(@RequestParam(defaultValue = "World") String name) {
        return "Hello, " + name + "! It's " + LocalDateTime.now();
    }
}

启动后访问 http://localhost:8080/hello?name=SpringBoot,立刻看到响应。这就是Spring Boot的魅力——不需要web.xml,不需要Tomcat手动部署,内嵌容器直接跑。

当然,这种写法不能上生产。但作为快速验证,它节省了大量时间。我上周五就是靠这个,10分钟给前端同学返回了一个mock接口,让他们先干活,我再慢慢拆分结构。

数据库操作:JPA不是银弹

接下来要连数据库。假设我们要存一个“文档片段”,用于RAG系统的检索源。

@Entity
@Table(name = "document_chunks")
@Data // Lombok注解,自动生成getter/setter/toString
public class DocumentChunk {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;
    private String metadata; // 存JSON字符串,简单场景够用
    private String embedding; // 向量字符串,实际项目应存为byte[]
    
    @CreationTimestamp
    private LocalDateTime createdAt;
}

然后写个Repository:

@Repository
public interface DocumentChunkRepository extends JpaRepository<DocumentChunk, Long> {
    List<DocumentChunk> findByContentContaining(String keyword);
}

再在Controller里调用:

@Autowired
private DocumentChunkRepository chunkRepo;

@PostMapping("/chunks")
public ResponseEntity<DocumentChunk> createChunk(@RequestBody DocumentChunk chunk) {
    // 简单校验
    if (chunk.getContent() == null || chunk.getContent().isEmpty()) {
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(chunkRepo.save(chunk));
}

踩坑提醒
JPA的save()方法看起来很美好,但默认会触发SELECT判断是否存在——这意味着每次插入都要多一次查询!线上高并发时这是性能杀手。后来我们改用EntityManager.persist(),或者直接上MyBatis Plus的insert(),才解决这个问题。

另外,metadata字段存JSON虽然方便,但无法建立索引。后来运维同学建议用MySQL 5.7+的JSON类型,配合函数索引,查询效率提升明显。

安全意识:别让API裸奔

作为在深圳互联网圈混的人,我对安全特别敏感。上周刚听说隔壁公司因为没加CORS限制,导致前端跨域请求被劫持。所以哪怕是个demo,我也习惯性加上基础防护。

首先,加个简单的全局异常处理:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException e) {
        return ResponseEntity.badRequest().body("Invalid input: " + e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneric(Exception e) {
        // 生产环境不要暴露堆栈!
        log.error("Unexpected error", e);
        return ResponseEntity.status(500).body("Internal server error");
    }
}

其次,限制请求频率。虽然Spring Boot没有内置限流,但可以用Guava的RateLimiter临时顶一下:

@Component
public class RateLimitInterceptor implements HandlerInterceptor {
    private final RateLimiter limiter = RateLimiter.create(10.0); // 每秒10次

    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) {
        if (!limiter.tryAcquire()) {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            return false;
        }
        return true;
    }
}

注册到WebMvcConfigurer里就行。当然,生产环境应该用Redis+Lua做分布式限流,或者直接上Sentinel。

前端联调:别让CORS坑了你

前端同事昨天抱怨:“你们后端怎么又没开CORS?” 我赶紧补上:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("https://your-frontend.com") // 别写*
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}

重点来了:allowedOrigins 千万别写成 "*",尤其是当你的API需要携带Cookie或认证头时。否则浏览器会直接拒绝请求。我们之前上线时就犯过这错,测试环境OK,生产环境死活不通,查了两小时才发现是CORS配置问题。

另外,前端传过来的数据一定要校验。别相信任何来自客户端的内容。可以用@Valid配合Bean Validation:

@PostMapping("/chunks")
public ResponseEntity<DocumentChunk> createChunk(
    @Valid @RequestBody DocumentChunk chunk) {
    // ...
}

// 在DocumentChunk类里加注解
@NotBlank(message = "Content cannot be blank")
private String content;

@Size(max = 10000, message = "Content too long")
private String content;

这样非法请求会在进入业务逻辑前就被拦截,减少无效计算。

资源管理:别让连接池拖垮服务

Spring Boot默认用HikariCP做数据库连接池,性能不错。但参数要调!

我们在压测时发现,QPS一高就报“Connection is not available”。一看配置,默认最大连接数只有10。赶紧改:

# application.yml
spring:
  datasource:
    hikari:
      maximum-pool-size: 50
      minimum-idle: 10
      connection-timeout: 3000
      idle-timeout: 30000
      max-lifetime: 1800000

经验之谈:

  • maximum-pool-size 不是越大越好。一般设为 (核心数 * 2) + 有效磁盘数
  • max-lifetime 要小于数据库的wait_timeout,避免连接被DB主动断开
  • 监控连接池使用率!我们用Micrometer+Prometheus,阈值超过80%就告警

另外,文件上传、网络请求这些IO操作也要注意资源释放。比如用OkHttp调外部API:

try (Response response = client.newCall(request).execute()) {
    // 处理响应
} // 自动关闭连接

别忘了try-with-resources!否则连接泄露分分钟让你OOM。

部署与监控:别等出事才看日志

Spring Boot打成jar包后,一行命令就能跑:

java -jar -Xmx1g -Xms1g -XX:+UseG1GC your-app.jar

但我们线上用K8s,所以还得写Dockerfile:

FROM openjdk:17-jdk-slim
COPY target/app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

关键是要加健康检查:

@RestController
public class HealthCheckController {
    @GetMapping("/health")
    public Map<String, String> health() {
        return Map.of("status", "UP", "timestamp", Instant.now().toString());
    }
}

K8s的livenessProbe就靠这个。另外,一定要开启Actuator:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

然后暴露metrics:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus

这样Prometheus就能抓取数据,Grafana画图。我们团队有个Dashboard,实时看CPU、内存、HTTP错误率——上周五那个紧急需求上线后,盯着图表看了半小时才敢回家。

总结:60分钟能做什么?

回到开头的问题:60分钟真能学会Spring Boot吗?

我的答案是:能跑通,但离生产还有距离。

这60分钟让我:

  • 搭起可运行的服务框架
  • 实现基本CRUD接口
  • 连接数据库并操作数据
  • 加上基础安全防护
  • 和前端完成初步联调

但真正上生产,还需要:

  • 分布式事务(Seata or Saga)
  • 链路追踪(SkyWalking)
  • 配置中心(Apollo/Nacos)
  • 灰度发布策略
  • 压测与容量规划

不过对于想跳槽的我来说,这已经足够写进简历的“项目经验”栏了。毕竟面试官问“用过Spring Boot吗?”,我可以自信地说:“不仅用过,还在紧急需求中救过火。”

最后说句心里话:虽然我现在沉迷Rust的零成本抽象和内存安全,但Java生态的成熟度和社区资源依然是无可替代的。特别是在深圳这种大厂聚集地,掌握Spring Boot就像会说普通话——不一定代表你多厉害,但至少不会被排除在外。

所以,别纠结语言之争。该用Java时就用Java,该学Rust时就学Rust。程序员的核心能力,从来不是绑定在某门语言上的。

对了,今天早上8点我又坐到工位上了。这次不是改Bug,而是继续看《Rust异步编程》。但桌角还放着Spring Boot官方文档——谁知道下次紧急需求什么时候来呢?


附:常用配置速查表

配置项 开发环境建议值 生产环境建议值 说明
server.port 8080 8080 或通过K8s Service暴露 默认端口
spring.datasource.hikari.maximum-pool-size 10 30-100(根据DB能力) 连接池上限
logging.level.root DEBUG INFO 日志级别
management.endpoints.web.exposure.include "*" health,info,metrics Actuator暴露端点
spring.jpa.show-sql true false 是否打印SQL

依赖版本参考(2024年中)

  • Spring Boot: 3.2.x(支持Java 17+)
  • Java: 17(LTS版本,主流选择)
  • Lombok: 1.18.30+
  • H2: 2.2.224(仅开发)

记住:别在生产用快照版(SNAPSHOT)!我们吃过亏,半夜升级后启动失败,哭都没地方哭。

评论 0

最热最新
暂无评论
韩秀英Lv.1
0
影响力
0
文章
0
粉丝