
很多人接触 Spring Boot都是从一句System.out.println(Hello World)开始的。跑通一个 Demo 并不难但如果你脑子里只有“能启动、能看到输出”那距离真实的开发与部署还有一大截配置从哪来、数据库怎么接、多个服务之间怎么通信、打包上服务器后为什么又起不来了。这篇文章写给真正想走完整个链条的人——零基础没关系但你要有耐心把这五六个环节都亲手过一遍。我会按照我这些年带项目、带新人的实际路径来写尽量把每一步背后的原因讲清楚而不是只丢给你 CtrlC/V 的代码。适合刚学完 Java 语法、想认真进入 Spring Boot 开发或者还没搞清楚“单体应用到了微服务到底发生了什么”的朋友。1. 为什么 Spring Boot 是零基础入门微服务最合适的入口1.1 先理解 Spring Boot 到底帮你“省”了什么很多教程上来就让你新建项目、写 Controller然后说“好了Spring Boot 入门了”。但你如果不知道它省掉了什么后面遇到任何报错都会不知所措。过去的 Java Web 开发典型配置是装 Tomcat把项目打成 war 包丢进 Tomcat 的 webapps 目录再配置一堆 XML 文件处理数据源、事务、拦截器、视图解析器……每一样都要手动声明。Spring Boot 做的事情就是把这些“约定俗成的配置”变成默认值——你只需要引入对应的 starter 依赖它就用自动配置帮你把默认环境搭好同时把 Tomcat 内嵌进来让你直接执行java -jar就能跑起来。这个“零配置文件”的思路极大降低了入门门槛。但要注意“零配置”不等于“不需要理解配置”。实际开发中你迟早要改端口、配数据库、调整超时时间所以我会在后面的章节里把配置文件里最常用的几个 key 讲透。1.2 微服务不是“一个服务”而是“一种拆分方式”你可能会问Spring Boot 跟微服务到底什么关系我个人的理解是微服务架构是目标Spring Boot 是每个服务的最小实现单元。一个微服务本质上就是一个能独立开发、独立部署、独立扩展的 Spring Boot 应用而服务之间的通信、发现、负载均衡、容错通常再交给 Spring Cloud 那一套组件去处理。所以零基础学习路线应该是先用 Spring Boot 把一个“独立的进程”写好再去学习怎么让多个进程协作。如果你的第一站直接扎进微服务的各种组件很容易被一堆概念淹没。先学会把单个服务做扎实再谈拆分和治理这是最稳的路径也是我带人时一贯的顺序。1.3 环境准备JDK、IDEA / VSCode、Maven开始写代码之前把环境理清楚。JDK 建议使用 8 或 17Spring Boot 2.x 用 JDK 8 没问题Spring Boot 3.x 则要求 JDK 17 以上。我个人目前给新人的建议是直接用 Spring Boot 2.7.x JDK 8 起步因为网上的资料最多、各种坑都有现成答案等你熟悉了再升 3.x 不迟。IDE 方面IDEA 社区版是免费的够用如果你更习惯 VSCode则需要安装 Extension Pack for Java 和 Spring Boot Extension Pack。说实话VSCode 跑 Spring Boot 我没少遇到问题主要是自动补全和启动配置没有 IDEA 顺手。如果你装了 VSCode记得在launch.json里配置主类和端口否则想同时启动多个微服务会很别扭。后面第 5 章我专门说说多服务统一启动的问题。Maven 是项目构建工具负责下载依赖和打包。装好后在命令行执行mvn -v能看到版本就说明 OK。如果你下载依赖很慢建议在settings.xml里配置阿里云镜像这一步能帮你节省大量人生。2. 动手写第一个能运行的服务Hello World 背后的配置与调试2.1 用 Spring Initializr 搭建骨架省时省力两种方式网页版 start.spring.io或者 IDEA 自带的 Initializr。选 Maven、Java、Spring Boot 2.7.xGroup 填com.exampleArtifact 填hello-service依赖只勾选 Spring Web。生成后解压用 IDEA 打开。如果你是 VSCode 用户也可以在 start.spring.io 上下载同样结构的压缩包再用 VSCode 打开文件夹。关键在于项目里这几个东西要做到心里有数pom.xml项目依赖和构建配置Maven 就是靠它拉包。src/main/javaJava 源码。src/main/resources/application.properties默认配置文件也可以写成application.yml。src/test/java测试样例可以暂时不管但别删。启动类比如HelloServiceApplication.java里只有一个SpringBootApplication注解加main方法。这个注解表面上很神奇本质是把SpringBootConfiguration、EnableAutoConfiguration、ComponentScan组合到了一起。你只需要知道Spring Boot 会从这个类所在包开始扫描所以启动类要放在项目包的根目录这个习惯别破坏。2.2 写一个真正能被访问的 Hello World写 Controller 的时候我见过很多新手卡住为什么输出了但访问不到因为你还没写接口入口。只需要建一个类RestController public class HelloController { GetMapping(/hello) public String hello() { return Hello World from Spring Boot; } }然后运行HelloServiceApplication浏览器访问http://localhost:8080/hello看到字符串返回这就是一个最小可访问的服务了。注意RestController表示这个类里的方法返回值直接写入 HTTP 响应体不是返回页面。如果你之前学过 Spring MVC 的Controller两者区别就在这。2.3 改端口号这一步早学早省心一个新手很容易忽略的问题默认端口是 8080。如果你本地的其他程序占了它启动就会报Port 8080 was already in use。解决办法不是杀掉进程而是学会改端口server.port8081也可以用命令行方式mvn spring-boot:run -Dspring-boot.run.arguments--server.port8081如果以后打包成 jar启动时还能临时覆盖java -jar hello-service.jar --server.port8081这三种方式优先级是命令行参数 application.properties 默认值。理解这个优先级很重要不然部署多环境时你会被配置文件坑到。2.4 实操过程中最常见的四个坑第一个坑是中文乱码。如果你是 Windows 环境控制台输出中文乱码时可以在 IDEA 的 VM options 里加-Dfile.encodingUTF-8或者把application.properties里配置server.servlet.encoding.forcetrue。第二个坑是热部署没生效改代码后必须重启。想舒服一点就引入spring-boot-devtools它会在代码变化后自动重启但我建议新手先别用因为热重启有时候会掩盖“你没理解服务生命周期”的问题。第三个坑是 Maven 依赖下载失败先确认镜像配置再检查网络。第四个坑是启动报Failed to determine a suitable driver class出现这个往往是你加了数据库依赖但没配数据源先移除依赖或者去第 3 章把数据库配上。3. 接入 MyBatis让接口真正读写数据库3.1 为什么要用 MyBatis而不是 JPA很多企业项目用 MyBatis因为 SQL 可控、复杂查询好优化团队协作时 DBA 也更容易接手。JPA 的“自动建表、自动查询”在简单场景下很舒服但一旦遇到多表联查、动态排序你往往要写很别扭的 JPQL。所以对国内大多数项目来说Spring Boot MyBatis 是更主流的组合。3.2 依赖、数据源与连接池配置在pom.xml里加两个依赖dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency然后在application.properties里配置数据源spring.datasource.urljdbc:mysql://localhost:3306/demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver这里要解释一下useUnicodetruecharacterEncodingutf8是防止中文存入数据库乱码的老套路serverTimezone必须配否则新版驱动会报时区异常。连接池方面Spring Boot 默认用 HikariCP你不需要额外配置就能用但我建议至少知道连接池是干嘛的——它就是多个数据库连接被重复使用避免每次访问数据库都新建连接造成开销。3.3 实体类、Mapper 接口与 XML 的对应关系假设有一张user表字段有id、name、email。我习惯的做法实体类对应的字段用驼峰命名表字段用下划线命名例如email对应emailcreate_time对应createTime。然后开启驼峰映射mybatis.configuration.map-underscore-to-camel-casetrueMapper 接口Mapper public interface UserMapper { User findById(Long id); }XML 放在src/main/resources/mapper/UserMapper.xml?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.hello.mapper.UserMapper select idfindById resultTypecom.example.hello.model.User select * from user where id #{id} /select /mapper然后在application.properties里告诉 MyBatis XML 文件在哪mybatis.mapper-locationsclasspath:mapper/*.xmlController 里注入 UserMapper一个“查用户”接口就活了。很多新人会问Mapper和MapperScan有什么区别Mapper加在单个接口上MapperScan加在启动类或配置类上、指定包路径二者用其一即可。3.4 这一步的常见坑SQL 语法没报错但数据不对第一种情况是字段映射不到——表字段是user_name实体字段是username没开驼峰映射就查出来全是 null。第二种情况是#{}和${}弄混#{}是预编译占位符安全${}是字符串拼接容易产生 SQL 注入只在必须动态传表名、列名时才用。第三种情况是 MyBatis 缓存导致的“查不到刚插入的数据”本地开发建议先关掉二级缓存mybatis.configuration.cache-enabledfalse我之前带过一个新人搞了一下午才发现问题出在 XML 里有符号没转义比如where status 2直接写在 XML 里会报错必须写成lt; 2。这类小问题很折磨人习惯上我都建议大家先单独在 Navicat 或命令行里把 SQL 跑一遍再拿到 MyBatis 里调试。3.5 Service 层不能省略实际项目里我强烈建议“Controller - Service - Mapper”三层结构。Controller 只做参数接收和响应封装Service 写业务逻辑Mapper 只管持久化。很多人初期为了省事把业务写在 Controller 里等到要加事务、加缓存、做单元测试时就要返工。Spring Boot 的事务用法很简单在 Service 方法上加Transactional就行一个方法里多个数据库操作要么全成功要么全回滚。这个习惯越早养成后面重构越轻松。4. 微服务架构思维课拆分哪些模块、怎么拆才不踩坑4.1 你不需要一上来就微服务这句话我必须放在最前面。很多人看到招聘要求“熟悉微服务”就想把单体项目硬拆成十个服务结果服务间调用延迟高、事务没法保证、部署脚本一大坨最后连运维都想骂人。微服务是解决“单体应用变得过大、过复杂”之后的问题的。如果项目还没到几十人协作、模块之间频繁冲突、某个模块需要独立扩缩容的程度老老实实写单体反而是最优雅的。零基础阶段你要做的是理解微服务的“形”而不是盲目追求架构的“时髦”。4.2 微服务架构的核心组件和它们各自干什么当你需要把单体拆成多个服务时一定会遇到这几个问题服务地址在哪里——需要“注册中心”如 Nacos、Eureka来维护所有服务的实例地址。服务之间怎么调用——需要 HTTP 客户端如 OpenFeign或消息队列。请求从哪进——需要“网关”如 Spring Cloud Gateway统一让外部访问。服务挂了怎么办——需要“熔断降级”如 Sentinel、Resilience4j。我画过很多架构图但图片不重要重要的是这些组件各自解决一个问题。慢一点理解注册中心就是通讯录服务启动时把自己的 IP 端口登记上去别的服务打电话时去通讯录查号码网关是前台所有外部请求先进前台前台再帮你转发给对应部门。4.3 拆分服务时的业务边界判断最常犯的错误是按“代码层”拆比如拆一个 Controller 服务、一个 Service 服务、一个 Mapper 服务。这样拆是灾难——每个业务请求要跨多次网络调用性能差、事务复杂、改个需求还要改好几个地方。判断边界最朴素的标准是如果两个功能总是要同时修改、同时部署、共享同一批数据表它们就不该拆开。正确的拆法按“业务域”拆比如用户服务、订单服务、库存服务。用户服务只管用户注册、登录、个人信息订单服务只管下单、订单查询。它们之间通过 API 或消息通信数据库也最好按业务独立不能所有服务连接同一个数据库实例。数据库拆开后才能获得真正的隔离性和独立扩展能力。4.4 版本选择是 Spring Cloud 新手最大的拦路虎Spring Cloud 的组件版本跟 Spring Boot 版本必须匹配否则各种 NoClassDefFoundError 层出不穷。以 Spring Boot 2.7.x 为例对应 Spring Cloud 2021.0.x如 2021.0.8。如果你用的是 Spring Boot 3.xSpring Cloud 要选 2022.0.x 以上。千万别自己随意指定版本去官网查 Release Notes或者直接用 Spring Initializr 展开的依赖版本。Nacos 也要注意与 Spring Cloud Alibaba 版本的对应关系我自己常用的组合是 Spring Boot 2.7.x Spring Cloud 2021.0.8 Spring Cloud Alibaba 2021.0.5.0 Nacos Server 2.2.3。这个组合踩过的坑最少如果大家问我我一般直接推荐这个搭配。5. 将单服务拆成多个可通信的微服务注册与调用实战5.1 先准备注册中心装一个 Nacos ServerNacos 同时提供服务注册、配置管理功能对新手非常友好。下载 Nacos Server 后在 Linux 或 Windows 下直接用startup.cmd -m standalone单机模式就可以启动默认端口 8848。启动看到Nacos started successfully后浏览器访问http://localhost:8848/nacos默认用户名密码都是nacos。这里有个经常出问题的点Nacos 如果以集群模式启动你需要配置 MySQL 存储但开发阶段单机模式已经够用。5.2 拆两个服务provider 和 consumer用一个最简单的业务场景用户服务provider提供查询用户接口订单服务consumer调用用户接口模拟服务间通信。新建一个 provider 服务依赖加 Spring Web、Nacos Discoverydependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependencyapplication.propertiesserver.port8081 spring.application.nameuser-provider spring.cloud.nacos.discovery.server-addr127.0.0.1:8848注意spring.application.name不能随便起注册中心靠它区分服务后面调用也靠它。再新建一个 consumer 服务端口设 8082名称order-consumer同样引入 Nacos Discovery再额外引入 OpenFeigndependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency在消费端启动类上加EnableFeignClients然后声明一个 FeignClientFeignClient(name user-provider) public interface UserFeignClient { GetMapping(/user/{id}) String getUser(PathVariable(id) Long id); }Controller 里面注入这个 FeignClient请求它。这个name user-provider不是随便写的它会被拿去 Nacos 找对应服务的实例列表然后自动做负载均衡访问其中一台。这就是服务发现和远程调用的最小闭环。5.3 多服务统一启动的实操技巧如果本地要同时启动多个 Spring Boot 服务你会发现两个问题第一每次都要手动一个个运行第二IDEA 控制台混乱分不清日志是谁的。这里分享我平时的工作方式。IDEA 里可以对每个服务创建 Run Configuration也可以直接 Run 多次VSCode 用户则需要配置launch.json。如果你用 VSCode 在一个文件夹里管理多个微服务建议不要直接改默认的 launch而是一个服务一个配置例如{ type: java, name: user-provider, request: launch, mainClass: com.example.user.UserProviderApplication, projectName: user-provider, vmArgs: -Dserver.port8081 }配置好后你可以同时启动多个配置每个配置独立窗口输出日志调试起来舒服很多。另外spring-boot-devtools在这时候要格外小心多个服务同时开启 devtools 会让端口冲突更频繁我建议调试多服务时关掉它。5.4 从单体到微服务必然面临的四个新问题服务调用后你会发现原来单体里的本地方法调用变成了网络调用于是遇到这些问题超时配置网络调用可能长时间无响应Feign 要配置连接超时和读取超时不能无限等。服务异常提供者挂了消费者要能降级OpenFeign 的fallback就是干这个的。日志追踪一个请求跨了 provider 和 consumer日志要带上同一个 TraceId不然排查问题无从下手。分布式链路追踪如 SkyWalking、Zipkin是进阶主题这里先埋个种子。配置管理服务变多后把每个配置散落在各个项目里会疯掉——同一个数据源配置要在十个项目里改十遍。这时要引入 Nacos 的配置中心把公共配置抽出来动态刷新。我不建议零基础第一天就全掌握但你应该在“从一个服务变两个服务”的这个瞬间意识到世界变了后面还有一道长长的楼梯要走。6. 部署到服务器打包、JVM 参数、启动脚本与日志6.1 打包这一步比你想的更简单也更讲究本地跑通只是热身能部署到别人的机器上才是真正“可用的服务”。Spring Boot 项目默认打成可执行的 jar执行mvn clean package -DskipTests然后在target目录下得到xxx.jar。直接运行java -jar target/hello-service-0.0.1-SNAPSHOT.jar注意三件事不要用java -jar直接按 CtrlC 停止服务这是一种粗暴的方式要用进程管理工具如 systemd来管理启动、停止和重启。服务器上如果没装 JDK服务起不来。你可以装完整 JDK也可以用 JRE。生产环境我建议直接装 JDK省得以后想看线程栈时发现没有jstack。打包前确认skipTests是否合理。单元测试写完后不要随便跳过但在你还没有测试体系时跳过可以帮你快速打出一个可用的包。6.2 JVM 参数与启动脚本真实生产环境不会裸启动java -jar至少会带上内存参数。比如你的服务器是 4G 内存java -Xms512m -Xmx1024m -XX:MaxMetaspaceSize256m -jar app.jar --server.port8080-Xms是初始堆大小-Xmx是最大堆大小MaxMetaspaceSize是元空间上限。新手容易犯的错是把-Xmx设置得比服务器内存还大结果启动一段时间后直接被系统杀掉。先保守再压测这是铁律。启动脚本我一般写成一个start.sh#!/bin/bash APP_NAMEhello-service.jar nohup java -Xms512m -Xmx1024m -jar $APP_NAME logs/app.log 21 echo $! app.pid停止脚本stop.sh#!/bin/bash kill -15 $(cat app.pid)用kill -15SIGTERM而不是kill -9是给 Spring Boot 留出优雅停机的时间让它处理完手头的事、释放资源。这一行命令体现了你对服务生命周期的理解。6.3 多服务部署时的端口、注册地址与防火墙部署多个微服务到一台机器时最容易被坑的是注册中心地址。本地大家写127.0.0.1:8848部署到服务器上没问题但如果你在服务器 A 部署服务服务器 B 部署 Nacos消费者的配置必须指向服务器 B 的地址如192.168.1.10:8848不能写localhost。同理服务之间的调用地址由 Nacos 分发你要注意 Nacos 注册的是“内网地址”还是“公网地址”如果注册成内网地址跨网络调用就会失败。还有防火墙和服务器安全组。很多新手明明服务启动成功浏览器就是访问不了多半是端口没开放。Linux 上执行firewall-cmd --add-port8081/tcp --permanent firewall-cmd --reload云服务器还要去控制台安全组规则里放行对应端口。这一环没搞通就算代码零 bug服务也永远“不可用”。6.4 日志里最应该关心的几个关键词部署完成不代表万事大吉。启动日志里看到Started xxxApplication in 5.2 seconds不算什么关键是后面要会看异常。最常见的启动失败信息包括Port 8080 was already in use端口被占找到占用进程或者换端口。APPLICATION FAILED TO START一般是配置问题比如数据源连不上、Bean 重复定义。java.lang.NoSuchMethodError多半是版本冲突检查依赖树用mvn dependency:tree定位是谁引入了旧版包。NacosException: java.net.ConnectException服务连不上注册中心先 ping 一下注册中心 IP再检查 8848 端口通不通。我自己的习惯是生产环境至少保留三个东西——启动日志、GC 日志、访问日志。启动日志看异常GC 日志看内存健康访问日志看业务趋势。以前遇到过拆分服务后某个接口时快时慢最后就是通过 GC 暂停发现是堆太小导致的这个经验说明日志排查不是矫情是真能救命。6.5 部署也可以上 Docker但别急着盲目追新如果你已经理解 jar 包运行原理再上一层就是 Docker。写一个最简单的DockerfileFROM openjdk:8-jdk-alpine COPY hello-service.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, /app.jar]这样构建的镜像就能在任何装 Docker 的机器上跑解决了“环境不一致”的问题。但我要提醒Docker 不是你部署的第一优先级。先学会普通 jar 部署理解进程、端口、日志再上 Docker 隔离否则容器报错你会更懵。零基础最忌讳一步跨两个台阶。写在最后的个人实操体会这条学习路径我用下来最舒服的节奏是每个阶段只加一个新变量。先只跑 Hello World不要同时纠结 MyBatis 和 Nacos接入数据库后也不要去碰微服务组件拆服务前先把单体项目的目录结构和依赖管明白。很多时候你觉得自己卡住了其实是“同时引入的新东西太多”不是某一个技术难。我见过不少同事在网上抄了一段微服务配置没改包名没改端口启动报错又回来问为什么——这种问题的根源往往在于不理解每个配置的作用。所以我一直主张配置文件里的每一行你都得能解释它是干什么的解释不了就不要往里写。等到你能熟练地启动一个服务、接一个数据库、调另一个服务、再把它打包丢到服务器上跑起来你才算真正迈过了 Spring Boot 的入门门槛。后面再去看 Spring Cloud Alibaba、Sentinel、SkyWalking 这类工具你会发现它们并不是新大陆只是给已经合格的“单兵”配上编队作战的能力。希望这篇经验贴能帮你少绕几个弯踏踏实实走完这条从 Hello World 到可部署微服务的路。