
1. 动手之前先搞清楚这几件事Spring项目在Java社区里早就不是新鲜词了但直到今天还是经常看到有人一上来就踩坑JDK版本不匹配、Maven仓库下载慢、项目创建成功后跑不起来。说句实话大部分问题在动手创建项目之前就能避免关键就看你有没有在开工前花十分钟确认环境。这篇东西主要想聊清楚一件事在IDEA里从零创建一个Spring项目完整走一遍包括选型、踩坑、调试和排查。适合三类人看刚接触Spring的Java初学者准备把老项目迁移到Spring Boot的开发者以及想搞清楚IDEA里那些项目参数到底是什么意思的同学。至于已经能用Spring Boot糊业务的老手可以直接跳到第6章看问题排查那部分我放了不少实测教训。先说结论创建Spring项目今天最靠谱的路径是通过Spring Initializr生成骨架然后用IDEA打开工程。不是手动建Maven项目再慢慢加依赖那样既慢又容易漏配置。Spring Initializr相当于一个项目脚手架生成器它帮你把目录结构、构建脚本、启动类、配置文件全部铺好你只需要填上自己关心的高级选项比如用什么构建工具、Java版本多少、要引入哪些依赖。IDEA对Initializr的支持已经很成熟旗舰版内置社区版也能用官方网页版生成再导入后面我会把两种方式都写清楚。不过在创建之前环境版本得先顺一遍。Spring Boot 3.x要求JDK 17起步如果你还在用JDK 8只能选择Spring Boot 2.7.x及其之前的版本。这不是什么可选的建议而是硬性门槛——Spring框架升级后底层字节码编译级别跟着提升JDK版本低了直接扫描不到注解。很多同学创建项目时Spring Boot选了3.2结果IDEA里配置的Project SDK还是1.8启动类连SpringApplication.run都编译不过问题就出在这个匹配关系上。再一个容易忽略的点是Maven的镜像源。IDEA自带的Maven会从中央仓库拉依赖国内网络环境下经常卡在Downloading阶段半天不动。这不是你欠了Maven什么债纯粹是网络链路问题。提前在settings.xml里配好阿里云镜像能让后续所有依赖下载都顺畅很多。这个文件的位置和配法我在第2章会展开别跳过。IDEA本身的选择也提一嘴社区版是免费且开源的完全够用来创建和运行Spring Boot项目不需要为这个功能付费旗舰版的优势在于多了Spring相关的辅助能力比如Beans面板的可视化依赖图、Autowired注入点的智能提示、Spring MVC的Request Mapping跳转等。如果你暂时不想订阅社区版加上官方网页版Initializr完全可以覆盖项目创建这条流程。网上各种版本绕来绕去的方法我就不讲了没必要也希望大家别折腾那类话题用官方渠道安装省心干净。2. 创建Spring项目的三种方式实测下来哪种最舒服2.1 网页版Initializr IDEA导入兼容所有版本最稳打开Spring Initializr官网start.spring.io这个页面几乎就是整个Spring世界的入口。它的作用不是给你一个宣传页而是根据你勾选的选项在后端动态组装一个zip压缩包里面就是完整的可执行工程骨架。网页版我建议按这个顺序配置Project选择Maven。Gradle在Android和部分新Java项目里用得挺多但Java服务端这边Maven仍然占据绝对主流教程多、插件全、排查问题容易找到现成案例。Language选Java这是Spring Boot服务端开发最主流的表达方式虽然Kotlin也能写但没有必要在这里引入额外的学习成本。Spring Boot版本根据你本机的JDK来。JDK 8选2.7.18JDK 11可以选2.7.x也可以直接上3.xJDK 17及以上无脑选最新的稳定版比如3.2.x或3.3.x。版本并不是越新越好而是要和你的运行环境匹配。我见过有人JDK 8硬是选3.2.0构建的时候一堆编译错误最后查根源就是版本不兼容。Group填公司的域名倒序比如com.exampleArtifact填项目名比如demo。Type选MavenJava版本下拉框里选你本机JDK对应的版本号。依赖那栏是关键。最基础的是Spring Web这会给项目装上嵌入式的Tomcat和Spring MVC让你可以直接写REST接口。其他常用的还有Spring Data JPA、MySQL Driver、Lombok等但你如果是第一次创建建议先只勾Spring Web等工程跑通了再加别的依赖免得依赖之间版本冲突时无从排查。把这些都选完后点Generate按钮下载一个zip包。IDEA里通过File - New - Project from Existing Sources选择解压后的目录或者直接在欢迎页Open打开文件夹IDEA会自动识别Maven结构开始导入依赖。这个路径其实比IDEA内置的创建向导更透明因为你能清楚地看到生成器做了哪些工作。2.2 IDEA内置的Spring Initializr社区版也支持省去网页切换IDEA本身集成了Spring Initializr的客户端版本新版本的IDEA里新建项目时的界面和网页版几乎一致左边选Spring Initializr然后填项目元数据、勾依赖、选版本最后FinishIDEA会自己下载模板并导入。社区版同样也有这个入口和旗舰版并没有功能上的差别这点和网上很多旧教程的描述不太一样——他们总说社区版不支持Spring项目创建实际上新版的社区版已经把这个能力开放了。如果你发现社区版里没有这个入口那多半是IDEA版本太老升级就好。用IDEA内置向导的好处是省去解压、导入这中间两步直接在IDE里选好就能生成。但个人经验来看网页版反而更可控——因为你能先预览zip包的结构也能在浏览器里检查有没有多勾了依赖。内置向导偶尔切换版本源时不稳定网页版在这一点上更让人放心。2.3 纯手动Maven项目不建议但你需要理解它背后的原理手动创建Maven项目然后自己补Spring依赖逻辑上完全可行新建一个普通的Maven工程在pom.xml里引入spring-boot-starter-parent作为父工程添加spring-boot-starter-web依赖再写一个带SpringBootApplication注解的主类效果和生成的骨架一模一样。这么做的好处是你能彻底理解Spring Boot项目的依赖来源和启动机制坏处是容易出低级错误——比如parent版本忘了写、plugin少了spring-boot-maven-plugin、主类路径放错层级导致组件扫描不到。我建议初学者不要从这条路径开始。先走Initializr把骨架自动生成一遍然后打开pom.xml一行一行去看搞清楚每个依赖是干什么的这样比自己手动敲一遍更高效。骨架是老师手动是考试顺序不能反过来。3. 项目结构拆解创建完之后这些文件分别干什么3.1 主类、配置文件、启动逻辑生成的Spring Boot项目结构高度一致核心就三板斧第一板斧是src/main/java下的主类类名一般是项目名Application。SpringBootApplication是一个组合注解它内部聚合了SpringBootConfiguration、EnableAutoConfiguration和ComponentScan。main方法里只有一句SpringApplication.run但这一句背后做的事足够写三本书启动嵌入式的ApplicationContext、注册所有配置类和Bean、触发自动配置、启动内嵌的Web容器。你写的接口之所以能被访问到就是因为这个类把Spring MVC的DispatcherServlet装配起来了。第二板斧是src/main/resources/application.properties默认是空文件只有几行注释。可以改成application.ymlYAML格式缩进友好层次结构一目了然。比如端口配置在properties里写server.port8081在YAML里写server: port: 8081效果一样看你习惯哪种。YAML的优势在于配置项多了以后可读性确实更好但缩进错了也不会报错运行起来才暴雷这点要小心。第三板斧是测试目录src/test/java骨架里默认带了一个SpringBootTest的上下文加载测试。每次构建的时候它都会真的启动一个Spring上下文这会让测试时间变长。如果暂时不想跑集成测试可以在创建项目时去掉测试依赖也可以暂时不管构建时用-DskipTests跳过就行。3.2 pom.xml的关键节点打开pom.xml你会发现它最外层是spring-boot-starter-parent这个parent帮你锁定了Spring Boot各模块的版本号所以你在下面引入其他Spring Boot生态组件时绝大多数都不用写version版本号统一由parent管理。如果你哪天发现某个依赖没有版本号却标红多半是parent版本里没有它需要自己补全版本——这种情况一般发生在第三方库上。然后是spring-boot-starter-web它本身不是一个大而全的依赖而是一个聚合描述符帮你引入了spring-webmvc、spring-web、内嵌Tomcat、Jackson序列化器等全套Web开发需要的东西。理解这个聚合思想很重要因为Spring Boot整个生态都建立在“按需引入starter”的思路上每引入一个starter就相当于告诉Spring Boot你需要某一类能力剩下的交给自动配置去完成。spring-boot-maven-plugin这个插件也不能忽略。它有两个核心用途一是打包时可以生成可执行的fat jar把内嵌的Tomcat和应用本身打成一个完整jar直接java -jar运行二是提供了spring-boot:run命令在命令行直接启动应用。如果这插件缺失应用跑起来暂时没问题但打包部署时就会遇到找不到主清单属性的经典报错。4. 把项目跑起来从启动到直接开一个REST接口4.1 启动应用的三条路径和端口配置创建出来的项目能立即运行的标志就是你写一个RestController在里面加一个GetMapping(/hello)方法然后启动主类浏览器访问http://localhost:8080/hello返回一串文本。启动方式有三种直接点主类旁边的绿色三角在命令行执行mvn spring-boot:run或者先mvn package打成jar再java -jar target/demo.jar。开发阶段用第一种最方便IDEA会记住你的启动配置下次直接点。第二种适合不熟悉IDE命令行的窗口党第三种是部署到服务器时用的。端口默认是8080如果被系统里其他进程占用了启动日志会直接报Port already in use。不要急着重启先把占用端口的进程找出来杀掉或者干脆换个端口。在application.properties里写server.port8081就能改。顺手再提一个经验加上spring.application.name给应用起个名字。别看它不起眼后面做服务注册、日志区分应用、配置中心拉取配置时这个属性就是应用的身份证。启动日志值得认真看一眼。Spring Boot启动时会打印一个横幅下面是几行关键日志Tomcat初始化的端口、Active profiles有几份不同环境的配置时这里会显示当前用了哪份、Spring上下文启动耗时。这些日志对确认是否启动成功很有用启动失败时会在这里给出异常堆栈的第一现场。4.2 接口、过滤器、参数绑定Spring Web的基础认知创建一个Controller其实就三件事类上加RestController方法上加路由注解方法返回的Java对象自动被Jackson序列化成JSON。比较常见的坑是返回对象时字段为nullJSON里就直接缺失如果对接方要求字段必须出现要么把字段初始化成空串要么用Jackson的注解控制序列化策略。参数绑定也有讲究。接路径参数用PathVariable例如/user/{id}里拿id接查询参数用RequestParam例如?page1接JSON用RequestBody加一个DTO对象。没经验的开发者容易把三个混着用路径参数用RequestParam接结果空指针JSON体用PathVariable接结果报400。老实说分清这三个其实不难逐个记住就行。过滤器是Spring Web里很常用但容易被忽略的扩展点。如果你实现了Filter接口并注册成一个Bean它会在请求进入DispatcherServlet之前执行适合做登录校验、日志记录、跨域处理。注意注册过滤器时Component和Bean要选对如果用Component让Spring自动扫描过滤器会被注册到/*路径拦截所有请求包括静态资源如果你只想拦截部分API需要显式在FilterRegistrationBean里配置urlPatterns。5. 理解Spring框架创建完项目你还得知道它是怎么运作的5.1 IoC容器与自动配置为什么项目能直接跑起来Spring框架最核心的两个词就是IoC控制反转和AOP面向切面。IoC理解起来其实一句话你不需要自己new对象把创建和管理的权利交给容器你要用哪个对象直接声明依赖容器给你注入进来。项目骨架里的主类之所以能直接运行就是因为它一启动就创建了ApplicationContext容器扫描指定的包路径把RestController、Service、Repository标注的类全部实例化放进容器里管理。你写的Controller里如果需要调Service不用在Controller里new只需要加一个Autowired字段容器启动时会把匹配的Bean放进去。这就是控制反转最直观的体现。自动配置则是Spring Boot的杀手锏。你引入spring-boot-starter-web之后Spring Boot的spring.factories或者新的AutoConfiguration.imports文件里罗列了一批自动配置类它们会检查当前项目的依赖里有谁如果发现引入了Web相关依赖就自动装配Tomcat、DispatcherServlet、Jackson等组件。这个机制的好处是让你无感坏处是出了问题排查路径会变长——如果某个自动配置类装配了一个不符合你预期的Bean你得找到排除它的方式。最常见的操作是在SpringBootApplication上加exclude属性把那个不想用的自动配置类排除掉或者用配置文件里的spring.autoconfigure.exclude。5.2 三级缓存与Bean的生命周期问的人多但实战中怎么用得上“Spring三级缓存”这个概念在面试里出现频率极高不少人都背过一级缓存放成品Bean二级缓存放早期引用三级缓存放ObjectFactory核心目的是解决循环依赖的问题。但我要说句实在话实战中你基本不会直接操作这些缓存它们对开发者是透明的。理解三级缓存的意义在于当你遇到两个Bean互相注入导致的启动报错时你能判断这是哪一类问题以及知道该用什么姿势解决。解决方案也不是只有一种。最干净的方法是重构设计打破循环依赖把公共部分抽成一个独立的Bean或接口其次可以在注入点加Lazy让其中一个Bean延迟初始化打破启动时的立即依赖实在不行还能用Primary指定注入哪一个。这里不要背答案而是建议你创建项目后故意写一个循环依赖试试让Spring报错给你看再去查日志里的The dependencies of some of the beans in the application context form a cycle手动踩一次比看十篇文章有用得多。Bean的生命周期理解起来也不复杂容器启动后定义被读取实例化后先执行属性注入再依次执行BeanNameAware、BeanFactoryAware等回调接口接着是BeanPostProcessor的postProcessBeforeInitialization然后执行PostConstruct标注的初始化方法最后是postProcessAfterInitialization。如果你有资源需要在Bean创建后初始化、销毁前清理就在方法上标注PostConstruct和PreDestroy。有一个常见的习惯问题要提醒构造函数里直接用一个还没被注入的Bean这时候会拿到空对象但PostConstruct方法里就可以因为此时依赖注入已经完成。6. 常见问题与排查技巧实录6.1 依赖下载缓慢与仓库源配置这应该是新手最容易卡住的环节。创建完项目后IDEA右下方开始疯狂下载依赖几百兆的jar包排队进度条几乎不动典型的症状就是长时间停在Resolving dependencies或者Downloading...。根因就是Maven默认从中央仓库拉取跨网络慢是常态。推荐的做法是配置阿里云公共仓库镜像。在Maven的conf/settings.xmlIDEA自带的Maven路径一般在安装目录的plugins/maven/lib/maven3/conf下如果你自己装了Maven则会用你自己的那个路径里找到mirrors节点加入mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror改完重新导入项目下载速度会明显改善。另外不要忽略IDEA里Maven的本地仓库路径设置Settings - Build, Execution, Deployment - Build Tools - Maven里面Local repository项如果默认选的是IDEA内置的路径和你命令行里Maven用的仓库不一致会导致同一个依赖在两条路径下重复下载。建议统一到一个自定义目录。6.2 启动失败的全家桶端口占用、类冲突、Bean创建异常启动报错见得多了以后你会发现大部分都能归类到几类常见场景。端口占用时日志会出现Web server failed to start. Port 8080 was already in use.。解决思路是找到占用进程的PIDWindows用netstat -ano | findstr 8080macOS或Linux用lsof -i:8080拿到PID后在任务管理器或kill -9处理。如果你根本不知道哪个进程占了端口直接换一个端口也行但搞清楚占用者总归更稳妥不然容易埋下隐患。依赖冲突的典型症状是ClassNotFoundException或NoSuchMethodError通常发生在不同starter传递依赖时引入了同一个类库的不同版本。排查时先在pom.xml里按CtrlShiftF搜类名所属的groupId然后打开IDEA右侧的Maven面板点击Run Dependency Analyzer查看冲突。确认冲突来源后在pom.xml里显式声明你需要的版本或者用exclusions排除掉不需要的那个传递依赖。Bean创建异常的定义是启动日志能定位到某个方法报Error creating bean with name xxx。要么是这个类的构造函数里抛了异常要么是它依赖的某个Bean没找到。处理思路是顺着堆栈里Caused by那一层层往下看真正的根源多半在最后一层。初学者往往盯着第一行看半天实际上关键是最后一行Caused by。6.3 IDEA显示问题与缓存故障和代码无关但很耽误时间有段时间遇到奇怪的事项目里明明有target目录IDEA侧边栏的Project视图里却不显示同事说是文件被忽略了。原因确实是IDEA的File Types设置里把target目录加进了忽略规则列表导致它不出现在视图里但文件系统又真实存在。解决方式Settings - Editor - File Types - Ignored Files and Folders把target从列表里删掉项目就正常显示了。代码格式化失效也遇到过表面上是IDEA不管怎么按CtrlAltL代码都不变样。排查思路先从scope开始格式化选的是Selection还是Whole File如果是Selection但没有选中代码自然没有效果然后检查项目里有没有.editorconfig文件它的优先级高于IDEA内置格式化规则最后看IDEA的Code Style方案是不是被切换成了Project之外的方案。插件异常的表现一般是启动突然报Plugin Error或者某个功能按钮消失。这种问题多数是IDEA升级后插件版本不兼容先试试File - Invalidate Caches / Restart清掉缓存重启。如果还不行把具体插件禁用掉去插件市场装最新兼容版。习惯上我建议保持IDEA小版本更新但大版本升级前先查一下常用插件的兼容性列表以免工作到一半被环境问题打断了。7. 延伸思考项目创建后还能做什么以及几个实战建议7.1 Spring Boot的监控与运维能力别等生产环境再补骨架运行起来只是开始真正好用的项目通常还会接上监控。Spring Boot Actuator这个starter专门干这事引入后项目会暴露一堆运维端点默认提供health健康检查、info应用信息还能配置打开metrics、env、beans等更敏感的端点。本地开发时可以先把health和info打开看效果dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency配置里设置management.endpoints.web.exposure.includehealth,info访问/actuator/health你会看到{status:UP}。这个状态表明应用被外部探活时怎么回应。生产环境最重要的是不要把所有端点都暴露出去尤其env、beans、shutdown这种带敏感信息的端点默认关闭或限制可见范围才是稳妥的做法。如果你还要做更细的监控Spring Boot Admin是个不错的选择它会把多台应用的健康状态聚合到一个管理界面上适合服务数量增多以后统一观察。7.2 对外接口应该单独拆服务吗“Spring Boot对外提供的接口给第三方应该放在哪里是单独的服务还是放在对应的服务里”这个问题在团队里永远有争论。我的经验和建议是如果对接方只有零星几个、调用量不大、接口逻辑和业务服务关联紧密直接放在现有服务里没问题加上独立的/open或/api/v1/external前缀做区分即可。但如果第三方多了接口有独立的鉴权方式比如API Key、HMAC签名或者调用量可能影响核心业务那就值得拆成独立服务原因是隔离了两个方向的变动和故障第三方接口变更不会污染核心服务第三方流量打爆也只影响自己那一亩三分地。拆不拆不是一个纯技术题本质上是一个组织边界题。你只需要问自己两个问题出故障时第三方接口被打崩会不会影响主营业务需求变更时第三方接口的开发节奏是否会拖累主流程迭代如果两个问题里有一个答案明显偏向“是”那就拆。7.3 Spring Security与Spring AI后续扩展的两个方向创建完一个基础Spring项目后最常见的演进方向之一就是接入Spring Security。这个框架管的是认证和授权默认有一套完整但需要配置的过滤链。很多人在集成时被概念的复杂度劝退其实最开始只需要掌握三个模块Web安全配置、用户身份信息来源、自定义鉴权规则。社区里讨论得比较火的Spring Security本质上就是在过滤链里插入若干认证过滤器每个过滤器检查当前请求有没有携带有效的凭证。学习时不用死记搭一个最小可运行案例设置一个内存用户跑通登录与放行再一步步换成数据库用户。另一个热门方向是Spring AI。名字听着新本质上就是Spring生态对AI能力访问的抽象层类似早期Spring对数据库访问的抽象。Spring AI可以让你用一套统一接口接入不同模型服务商把对话、推理、Agent编排这些能力像Bean一样注入到业务代码里。如果你对这块感兴趣可以从最简单的ChatClient开始在controller里调用模型接口把一个对话接口跑通再慢慢理解prompt模板、Message历史和工具调用这些概念。热度高不代表它成熟到无懈可击早期版本API变动很正常做技术选型时要留意这一点。最后再分享一个建议创建Spring项目这件事本身没有多少难度难的是你如何理解这个项目启动之后的一系列机制的协同运作。我踩过不少坑之后最大的体会是遇到问题别急着搜答案先看一眼日志再想一步“这个报错是发生在Bean创建阶段、依赖注入阶段还是Web请求处理阶段”分类清晰了解决思路就明确了一半。你在这个流程里积累的每一个报错和排查路径都是你往后写生产代码时最有价值的经验储备。