Spring Boot 第一天我给自己定了个规矩不啃书、不看教程视频的前半段、不纠结“为什么要这样设计”直接开干。作为一个之前主要写PHP和Node.js的人Java那套繁琐的配置我早有耳闻——XML配置文件堆成山、Tomcat手动部署、各种Bean定义绕来绕去。所以当身边同事反复跟我提Spring Boot“约定大于配置”“自动装配”这些词的时候我心里是有怀疑的。真有那么神一天能上手事实证明这天过得比我想象中刺激也确实踩了不少坑。这篇就算是我和Spring Boot“闪婚”第一天的实录把从零到能跑通项目、能查数据、能写接口的全过程以及那些坑一次性倒给你们。我先把话说清楚这篇适合谁看Java基础薄弱但会写代码的人、第一次接触Spring Boot的小白、想快速把一个Web项目跑起来但不打算花一整周研究原理的人。如果这些里面有你那这篇你拿回去直接照做就行。1. “闪婚”前的心理建设为什么我敢第一天就上手Spring Boot1.1 传统的Spring到底卡在哪在我真正动手之前我先花了大概半小时搞明白一件事Spring Boot到底是不是一个全新的框架答案是不是它只是把Spring家族那些好东西打包再加工了一遍。就好比你会开手动挡的车突然给你一辆自动挡油门刹车都一样换挡的事它替你干了而已。传统Spring开发一个Web项目典型流程是这样的先用Maven建立项目结构然后引入spring-webmvc、spring-tx、mybatis等一堆依赖。引入之后还不算完你得手动配置DispatcherServlet、视图解析器、数据源连接池、事务管理器每一步都要在XML文件里写标签或者用Configuration类逐项声明。这中间经常出现的场景是配置漏了一个Bean项目启动直接报错报错信息还特别隐晦你对着日志翻半天也不知道哪个Bean没注入。Spring Boot把这一步彻底砍掉了。它提供了一堆starter依赖比如spring-boot-starter-web你只要把它加进pom.xml框架自动帮你把内嵌Tomcat、Spring MVC、JSON解析器全都配置好。启动项目不再需要外部Tomcat直接跑到main方法里点运行就行。这背后的机制叫自动配置后续我专门讲。1.2 第一天就该强攻还是该先懂原理网上很多建议是“先学Spring再学Spring Boot”理由是Boot只是封装不懂底层迟早吃亏。这个说法对但对大多数人来说它导致的结果就是拖了三个月还没写出一行能跑的代码。我个人的判断是第一天直接上手Spring Boot原理可以边用边补。原因很简单——Spring Boot的自动配置是建立在Spring成熟机制之上的你如果完全没接触过Spring一上来啃Bean生命周期、AOP代理大概率当场劝退。但我用一个Crud项目把路走通之后再回头去看那些原理就会发现每个设计都能对得上学习效率反而更高。我也不否认这种“闪婚”式学习会留下盲区比如我第一天完全没搞懂内嵌Tomcat是怎么被启动的也没研究spring.factories文件里那些配置类的加载顺序。但先跑起来、后补原理对建立信心和动手能力非常有效。2. 开工前必备的“彩礼”环境准备中那些容易被忽略的细节2.1 JDK和Maven的版本匹配是第一道坎工欲善其事必先利其器。Spring Boot 3.0之后要求JDK 17及以上如果你的电脑装的是JDK 8那你第一天最可能遇到的就是项目创建成功但启动秒失败报错信息类似于UnsupportedClassVersionError。我自己的机器装的是JDK 17配合Spring Boot 2.7.x版本这个组合非常稳建议抄作业的人直接照搬组件推荐版本说明JDK17LTSSpring Boot 2.x和3.x都兼容建议起步就装17Maven3.8.x以上注意Maven本身也依赖JDK版本太低会报错IDEA社区版即可我用的Ultimate版但社区版足够跑Boot项目这里有个特别隐蔽的坑IDEA社区版不带Spring Initializr的Web页面入口但你可以直接访问Spring官方初始化网站start.spring.io生成项目压缩包然后解压导入IDEA。千万别因为IDE版本问题卡住一整天。另外Maven的中央仓库在国内访问速度很慢我第一天就吃过这个亏一个Spring Boot Starter下载了十分钟。解决办法是在Maven的settings.xml里配置阿里云镜像这段配置我建议直接存起来mirrors mirror idaliyunmaven/id mirrorOfcentral/mirrorOf nameAliyun Maven Mirror/name urlhttps://maven.aliyun.com/repository/public/url /mirror /mirrors2.2 用Spring Initializr创建项目时的必选项打开start.spring.io你会面对一堆选项。第一次接触的人很容易懵我帮你把必须选的列出来Project选择MavenGradle也行但Maven的资料更多就不给自己找麻烦了LanguageJavaSpring Boot版本选最新的稳定版Group和Artifact相当于包名和项目名比如Group填com.exampleArtifact填demoDependencies先只勾Spring Web不要贪多为什么建议只勾Spring Web因为第一天的问题排查能力有限依赖越多冲突越多。比如你同时勾了Spring Web和Spring Security你会发现访问任何一个接口都跳登录页你甚至不知道是谁在拦你。第一天把路走通最重要其它依赖按需添加。项目生成后先做一件很重要的事删除自动生成的src目录下所有内容然后自己重新创建。这样做有两个好处一是熟悉标准目录结构二是避免被Spring Initializr生成的示例代码带偏。标准结构就这几层src/main/java/com/example/demo DemoApplication.java -- 启动类 controller/Controller.java -- 控制器 src/main/resources application.properties -- 配置文件2.3 启动类里藏着第一个“闪婚”秘密IDEA导入项目后你会在启动类上看到这样一个注解SpringBootApplication。第一天不用完全理解它但要知道它把三个注解组成了一个组合——SpringBootConfiguration声明这是配置类、EnableAutoConfiguration开启自动配置、ComponentScan开启组件扫描。组件扫描意味着只要你的Controller和其它组件放在启动类的同级或子级包下Spring就能自动找到它们。这个包结构问题特别容易踩坑。我第一天就干过这种蠢事把Controller放在了com.example.controller包下而启动类在com.example.demo包下结果Controller一直不被识别接口404。后来才知道ComponentScan默认扫描启动类所在的包和子包你的类如果不在这个范围内就必须手动指定ComponentScan的路径或者干脆遵守约定把Controller包建在启动类的子包下。3. 第一个走通的接口从Controller到Postman的完整链路3.1 写一个接口比我想象中短环境准备好后我做的第一件正事是写一个接口。在Spring MVC传统写法里你需要配置一堆东西才能处理HTTP请求但Spring Boot里只需要一个类加几个注解package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/api) public class DemoController { GetMapping(/hello) public MapString, String hello() { MapString, String result new HashMap(); result.put(message, hello spring boot); return result; } }这段代码的含义是RestController告诉Spring这个类的所有方法都直接返回数据而不是返回视图RequestMapping(/api)定义了这个控制器的公共前缀GetMapping(/hello)声明了具体的GET接口路径。方法内部建了一个Map并返回Spring Boot会通过内置的Jackson库把它自动序列化成JSON。整个过程不需要任何配置项方法返回什么客户端就收到什么。写好之后启动项目控制台会看到一行非常显眼的日志Tomcat started on port(s): 8080。这说明内嵌Tomcat已经起来了。然后打开浏览器访问http://localhost:8080/api/hello就能看到返回的JSON数据。那种第一次跑通的感觉确实有点像“闪婚”领证那一刻——明明认识才几个小时居然就踏踏实实跑起来了。3.2 端口冲突和参数接收是实操必踩的两个坑看到Tomcat在8080端口启动顺利我顺手开了电脑里的另一个服务直接在IDEA里重新Run。结果控制台刷出一行报错Port 8080 was already in use。处理办法有两个一是找到占用进程并kill掉Windows下用netstat -ano | findstr 8080然后taskkill /PID 进程号 /F二是在配置文件里改端口。我强烈建议新手学会第二种毕竟在配置文件里调整应用属性本身就是Spring Boot的核心用法。在application.properties中加一行server.port8081重启项目就会改用8081端口。这引出一个很重要的概念application.properties是Spring Boot的全局配置入口所有自动配置的默认值都可以在这里被覆盖。换句话说框架帮你配好的大部分东西你都有一票否决权。接口的正常传参也建议第一天就练一遍。GET请求带参数用的注解是RequestParam路径参数用的是PathVariable接收前端POST过来的JSON体用的是RequestBody。举个例子一个接收用户信息的POST接口长这样PostMapping(/user) public MapString, Object createUser(RequestBody MapString, Object user) { MapString, Object result new HashMap(); result.put(success, true); result.put(data, user); return result; }这里值得多说一句RequestBody能直接把JSON字符串绑定成Java对象背后是HttpMessageConverter在起作用。Spring Boot默认已经预置了Jackson的消息转换器所以只要Content-Type写application/json框架就自动帮你完成反序列化。如果你传的JSON里某个字段类型不匹配会直接报400错误而且错误信息默认非常简略这算是第一天最容易困惑的点之一——不是你代码错了是参数类型没对上。4. 一眨眼接上数据库初探Spring Data JPA还是MyBatis4.1 ORM框架的首次交锋接口能跑了之后我马上想到下一步如果数据要存库怎么办Spring Boot生态里主流的数据库访问方案有两套Spring Data JPA和MyBatis。JPA的玩法是让你通过定义实体类和接口方法自动生成SQL写起来非常省事但它的自动SQL有时会在复杂查询里产生一些让人摸不着头脑的语句。MyBatis则把SQL控制权留给了你灵活性更高代价是你得自己写SQL映射文件。我第一天选的是Spring Data JPA因为它的上手成本最低。只需要定义一个实体类、一个继承JpaRepository的接口增删改查就全都齐了。实体类长这样package com.example.demo.entity; import javax.persistence.*; Entity Table(name user) public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name name) private String name; Column(name age) private Integer age; // 标准的getter/setter省略 }我提醒一下如果你用Spring Boot 3.xjavax.persistence包已经改成了jakarta.persistence这是迁移过程中最让人容易忽略的差异。我在网上查资料时经常看到旧代码直接复制报错就是因为这个包名变更。4.2 配置数据源时的时区和驱动坑项目要连数据库就得在application.properties里配上数据源信息。以MySQL为例配置如下spring.datasource.urljdbc:mysql://localhost:3306/test?useSSLfalseserverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-autoupdate这一行配置里藏着一个问号“serverTimezoneAsia/Shanghai是什么意思”它是告诉JDBC驱动当前连接使用哪个时区。新版MySQL驱动对时区很敏感如果你不设这一项很多情况下会直接报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized的错。这个报错我第一天就撞上了当时还以为是数据库连不上排查了半天发现是驱动在时区问题上做了严格校验。我不想只讲JPA。MyBatis也是Spring Boot项目里出现频率极高的词。如果你后续发现自己需要写复杂SQL建议切到MyBatis。把spring-boot-starter-data-jpa换成mybatis-spring-boot-starter配置差不多但使用方式从JpaRepository接口变成Mapper接口加XML文件。第一天就用JPA把增删改查跑通即可MyBatis可以第二天再研究。4.3 写一个简单的增删改查接口串起全部内容数据源配置好后我定义了一个接口来验证CRUD链路是否畅通package com.example.demo.repository; import com.example.demo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface UserRepository extends JpaRepositoryUser, Long { ListUser findByAge(Integer age); }你没看错接口里这个方法findByAge在JPA里会自动生成select * from user where age ?这样的SQL不需要你写任何实现。这是Spring Data JPA最让人上瘾的地方——方法名即查询语句。然后我在Controller里注入这个仓库接口完成保存和查询RestController RequestMapping(/api/user) public class UserController { private final UserRepository userRepository; public UserController(UserRepository userRepository) { this.userRepository userRepository; } PostMapping(/save) public User save(RequestBody User user) { return userRepository.save(user); } GetMapping(/list) public ListUser list() { return userRepository.findAll(); } }这里有一个非常关键的Spring机制首次出现了构造函数注入。UserController没有用Autowired注解而是直接把UserRepository放在构造方法参数里。Spring Boot会扫描这个类发现只有一个构造函数需要UserRepository类型的Bean就会自动把它传进来。这个模式在官方文档和社区里被越来越推荐因为它在测试时可以直接自己new一个假仓库传入不会像字段注入那样难以替换。我用Postman往/api/user/save发了一个JSON数据库里立刻多了一条记录再调/api/user/list能看到刚才插入的数据。这个链路走通意味着Spring Boot的Web层、数据访问层、JSON序列化、自动配置这四块核心能力都验证完毕了。5. 第一天晚上拆开“闪婚”对象的面纱初步摸清自动配置原理5.1 spring-boot-starter-web到底帮我干了多少活白天的时候我一直在用Spring Boot但没搞明白它为什么能这样“懂事”。晚上我决定先看一个东西依赖里那个spring-boot-starter-web一点进去发现它本身几乎没多少代码它主要干的事情是把好几个库聚合在一起——Spring MVC、Jackson、内嵌Tomcat、Spring Boot的自动配置模块。所以引入一个starter的语义是我声明了这个场景你帮我把这个场景所有需要的东西都带进来。这个概念就像你买一个“电脑套装”里面包含主机、屏幕、键鼠而不是分别从不同厂商凑齐配置。这种设计在项目里的好处是依赖管理变得非常集中你在pom.xml里看到的是一行starter而不是长长的jar列表。5.2 从spring.factories到configurationProperties自动配置的核心机关在META-INF/spring.factories文件Spring Boot 2.7之前的版本或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件新版。框架启动时扫描这些文件找到所有标注了AutoConfiguration的类再按条件判断是否要执行它们的配置。条件判断通过ConditionalOnClass、ConditionalOnProperty等注解实现。我用一个例子解释这个过程你在pom.xml里加了mysql-connector-j依赖和spring-boot-starter-data-jpa依赖框架扫描时发现DataSource类和LocalContainerEntityManagerFactoryBean类都存在于是自动创建一个数据源Bean并配置好连接池、事务管理器。如果你没有任何数据库驱动那么ConditionalOnClass条件不满足相关自动配置直接跳过不会报错。这就是Spring Boot“智能”的地方——它根据你依赖了什么来决定配置什么而不是一股脑都配置。如果你想看自己的项目里当前到底配置了哪些Bean可以在启动类里加一行SpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context SpringApplication.run(DemoApplication.class, args); String[] beanNames context.getBeanDefinitionNames(); System.out.println(Total beans: beanNames.length); for (String name : beanNames) { System.out.println(name); } } }我当时跑了一下发现一个刚创建的空项目居然有一两百个Bean被注册进来其中包括处理JSON的jacksonObjectMapper、管理事务的transactionManager、内嵌Tomcat相关的一堆组件。它们全是自动配置的产物你写代码时根本感知不到它们的切换过程但它们确实时刻在后台工作。这让我第一次对“约定大于配置”有了具体感受框架替你做了大量默认决策你只需要在需要偏离默认值时写一行配置来覆盖它。5.3 关于SpringBoot Admin和监控能力的第一印象搜热词的时候我看到Spring Boot Admin这个词顺带研究了一下。它对Spring Boot应用来说是一个监控管理界面只需要引入一个客户端依赖注册到Admin服务端就能在Web页面上看到应用的内存占用、线程池状态、请求日志和健康检查情况。我没在第一天部署它但搞清楚了它的设计思路Spring Boot内置了Actuator模块专门对外暴露项目的运行指标和管理端点Admin只是把这些端点的数据可视化。Endpoints默认只暴露了health和info两个其它诸如metrics、beans、env都需要在配置里手动打开。我当时在application.properties里加了management.endpoints.web.exposure.include*然后访问/actuator/health看到{status:UP}。虽然只是几个字节的JSON但那一刻我知道这个“闪婚”对象不仅上得厅堂方便写接口还下得厨房自带体检报告。6. 一天的收尾之“踩坑清单”这些错误我赌你也会犯6.1 从报错信息反推问题根源的三条经验第一天的报错信息其实没少看我把其中最典型的几个记录在这里并标注了问题本质报错现象问题本质解决思路Port 8080 was already in use端口冲突换端口或杀进程Consider defining a bean of type xxx in your configuration依赖注入缺少Bean检查接口实现类是否加了注解、是否被组件扫描Property or field xxx not found配置项缺失检查application.properties里是否配置了对应项Failed to configure a DataSource数据源配置失败检查有没有引入JDBC驱动、url和账号密码是否正确UnsupportedClassVersionErrorJDK版本过低升级JDK或使用匹配的Spring Boot版本第三条经验是报错信息是英文的但它的结构非常固定。凡是出现Consider defining a bean九成是依赖注入问题检查顺序是启动类包结构是否正确、实现类是否加了Service或Repository、接口路径是否拼错。很多人第一次遇到这种报错会去百度“Spring Boot启动失败”其实细看这段英文它已经把答案告诉你了。6.2 第一天最容易产生的两个误解误解一是把内嵌Tomcat当成需要在代码里显式创建的东西。其实Spring Boot启动时自动创建Tomcat容器你不需要new Tomcat()也不需要在src/main/webapp下放web.xml。这一点尤其困扰从传统Java Web转过来的朋友他们总觉得少了点什么。误解二是以为所有配置都必须写在application.properties里。实际上Spring Boot支持YAML格式还支持通过环境变量、命令行参数、ConfigurationProperties绑定自定义配置类来覆盖配置。配置优先级从高到低分别是命令行参数、Java系统属性、环境变量、application文件。我当时用一个很简单的方式验证了这一点启动时在IDEA的Program arguments里输入--server.port8082项目果然就在8082端口起来了配置文件里的8081也被覆盖了。这是Spring Boot的配置覆盖机制理解它之后多环境切换dev/prod就很好理解了。6.3 为什么我建议你把“第一天”的目标定小一点我见过太多人在第一天就想做一个包含注册登录、权限管理、在线支付的完整系统结果被一堆框架加进来的安全配置、事务回调整得焦头烂额。第一天真正该做的就是把一个接口跑通把一条数据存进数据库再查出来看一眼Actuator的健康状态。没有更多了。Scope越小反馈越快正反馈越强。如果社区版的IDEA让你觉得集成度不够高也千万别在第一天折腾IdeaVim插件、代码模板、热部署插件JRebel这些东西。等第二天想把开发效率提上来时再弄也不迟。我第一天的环境就是最简单的IDEA加Maven连Lombok都没装全部用原生getter/setter。虽然代码长了一点但每一行都能看懂——这对第一天建立掌控感非常有帮助。6.4 顺带一提多商户跨境商城那些事热搜词里出现了“Spring Boot MyBatis的Java开源多商户跨境商城源码”我点进去看过一眼。这种项目通常包含商品、订单、支付、多商户管理、物流追踪等模块工程结构比第一天的Demo复杂得多一般会拆成多个模块common公共模块、system系统模块、order订单模块、mall商城模块等。我建议初学者等第3-5天再去看这类源码第一天强行分析会陷入细节的海洋。但有一点可以提前记住这类项目会大量使用MyBatis-Plus框架它是在MyBatis上做了增强的工具内置了通用的CRUD方法不再需要你手写单表操作的Mapper和XML文件。如果你后续要走Java开发路线MyBatis-Plus和Spring Data JPA是两条不同流派都值得了解。7. 顿时清醒第一天之后的路线图一天下来“闪婚”对象到底值不值得长期处下去我心里已经有了明确判断。第二天我给自己安排的内容是把JPA的实体类关联关系一对一、一对多过一遍然后转向学习Spring MVC的拦截器、全局异常处理器。因为第一天只验证了正常流程还没有处理过参数校验失败、业务异常抛出这类负面场景。全局异常处理在真项目里几乎必用RestControllerAdvice配合ExceptionHandler能统一管理错误返回格式省得每个Controller都try-catch。第三天才是我想研究MyBatis的时机把XML映射文件里的动态SQL练一遍。第四天可能会看Spring Security做登录认证这之后才有能力去拆解那些开源商城项目的源码。路线图不是定死的但我跟所有新手朋友说一句掏心窝的话Spring Boot的上手难度真的被严重高估了。它没有门槛没有玄学也没有必须要背下来的神秘密码。你可能需要一天甚至半天就能完成“闪婚”到“同居”的跨越。剩下的事情就是过日子的功夫——遇到报错查文档遇到不理解的机制打印Bean列表遇到版本问题查官方发布说明。它就像一个特别愿意配合你的伴侣你把需求写在pom.xml里它就把对应的能力打包送货上门。用我第一天的原话说就是“早该见面了。”