作为一个天天跟Spring打交道的开发者,我几乎每周都要在IDEA里创建新的Spring项目。很多人觉得这就是个“下一步下一步”的体力活,没什么技术含量,但实际上我见过太多人在这一步就掉坑里了:用社区版找不到Spring Initializr、选错JDK版本导致项目起不来、创建完Spring Boot项目却不知道入口在哪、甚至有人折腾半天连个Hello World都没跑通。这篇就从我实际操作的视角出发,把IDEA中创建Spring项目这件事讲透,覆盖两种经典姿势:用Spring Initializr快速生成Spring Boot项目,以及在IDEA社区版里手写一个Spring项目。不管你是刚入门想跑通第一个接口,还是老手想理清背后的机制,这篇都值得看完。
1. 先弄明白:你要创建的是哪种Spring项目
很多初学者一上来就卡住了,因为“Spring项目”这个概念本身就太宽泛。我在指导新人的时候,经常被问到“为什么我在IDEA里新建项目没看到Spring选项”,或者“别人说用Spring Boot很简单怎么我这儿这么费劲”。这类问题的根源,就在于没有先分清楚项目的类型和创建工具的差异。
1.1 Spring Boot项目与经典Spring项目到底差在哪
简单粗暴地理解,经典的Spring项目指的是用Spring Framework核心模块搭起来的工程,你需要手动配置很多东西,比如把spring-context、spring-webmvc这些依赖一条条加进pom.xml,自己写配置类、自己配置DispatcherServlet,甚至还要考虑怎么把项目部署到Tomcat。这种方式的优点是你能清楚地看到Spring容器是怎么一步步建立起来的,对理解底层机制非常有帮助。
而Spring Boot是在Spring之上做了大量自动配置的框架,它内置了Tomcat,你只需要引入spring-boot-starter-web这个依赖,就能用main方法直接启动一个Web应用,完全不用关心传统部署那套流程。所以Spring Boot项目本质上还是Spring项目,只是它把很多繁琐的配置工作替你完成了。我在实际工作中几乎都是用Spring Boot来写业务代码,但学习的时候一定要先动手写过纯Spring项目,否则你对IoC容器、Bean的生命周期、包扫描这些概念的认知会很虚。
1.2 用什么方式创建,取决于你这台IDEA是什么版本
这是很关键的一点。IntelliJ IDEA分社区版(Community)和付费版(Ultimate),两个版本在新建Spring项目时的体验完全不同。
- 付费版(Ultimate)在新建项目向导里直接内置了Spring Initializr,你可以在IDEA界面里勾选依赖、选择Spring Boot版本,一键生成完整的Spring Boot工程。
- 社区版(Community)是完全免费的,但代价是新建项目向导里没有Spring Initializr,也没有内置的Spring/Spring Boot模板。不过这不代表社区版就不能建Spring项目,你有两条路可以走:一条是去Spring官网的start.spring.io这个在线初始化服务生成一个工程压缩包,下载后导入IDEA;另一条是直接创建一个空白Maven工程,手动把Spring依赖写进去,从零手搭。
我自己在社区版上练习过无数次手写Spring项目,其实体验不比付费版差,反而更清楚整个工程是怎么组装起来的。很多新手看到网上的教程用的是付费版界面,就以为社区版建不了Spring项目,这是个很大的误解。
| 对比项 | IDEA Ultimate | IDEA Community |
|---|---|---|
| 获取方式 | 付费订阅,可试用 | 完全免费开源 |
| Spring Initializr | 内置在新建向导 | 没有,需用在线start.spring.io |
| Spring Boot运行配置 | 内置支持 | 可通过Maven插件运行 |
| Tomcat/应用服务器集成 | 内置 | 需要借助Smart Tomcat插件或Maven插件 |
| 适合谁 | 日常开发主力、企业办公 | 学习、练习、轻量开发 |
我在给新人的建议里最常说的一点是:如果你还没毕业或者刚入行,完全可以用社区版学Spring,别把时间浪费在找激活码这件事上,把精力放在理解工程本身才是正路,开发工具这块不用省,但也不是非付费版不可。
2. 用Spring Initializr生成Spring Boot项目:Ultimate版最快的路径
如果你用的是IDEA Ultimate版,创建Spring Boot项目确实很省心。我就按平时操作的顺序,把每一步拆开讲,顺便把为什么这么选的原因也摆出来。
2.1 新建项目时的关键参数怎么选
在IDEA里依次打开File → New → Project,左侧选择Spring Initializr,接下来你会看到一堆需要填写的参数。这里我逐个说下实际项目里我一般怎么填:
- Name:项目名称,一般用小写字母加连字符,比如
demo-service。 - Location:项目存放路径,这个没什么讲究,但不要放在含中文、空格和特殊字符的目录下,否则后面Maven打包很可能报一些奇怪的路径错误。
- Language:选择Java。虽然IDEA也支持Kotlin和Groovy,但绝大多数Spring项目还是Java为主。
- Type:选择Maven。Gradle适合复杂构建和Android系项目,但国内Java生态里Maven的通用性更高,团队协作时用Maven也更保守稳妥。
- Group:一般写成公司/个人域名的倒序,比如
com.example。它和Artifact一起构成Maven坐标,也就是这个项目在仓库里的唯一标识。 - Artifact:通常和项目名保持一致。如果你想让生成的jar包名更简短,这里可以直接改,但Maven坐标和项目名不一致有时候会引起混淆,我习惯保持一致。
- JDK版本:这一步很关键。Spring Boot 2.x一般要求JDK 8或11,Spring Boot 3.x则必须JDK 17及以上。我现在的开发环境默认用JDK 17,对应的Spring Boot版本选3.x系列。版本不匹配是新人最容易踩的坑,经常看到有人Spring Boot 3.x配JDK 8,项目一跑就报错,报错信息还看不明白。
2.2 依赖怎么勾选:新手的第一个项目尽量克制
在向导里进入Dependencies这一步时,很多人恨不得把所有勾选框都勾上,看到Spring Web、Security、JPA、Redis都想加,觉得加上以后功能就齐全了。我真心建议第一次练手就只选一个Spring Web,最多再选Spring Boot DevTools。原因很简单:每多一个依赖,就多一堆自动配置和潜在的问题概率。你还没跑通Hello World,就先被Spring Security的登录拦截搞得怀疑人生,没必要。
我这里拿“我要建一个供第三方调用的接口服务”来举例。你若真要做对第三方提供的接口服务,我建议的方式也不是把所有接口堆在一个大而全的工程里,而是按照业务边界拆分:核心能力沉淀成内部服务,对外暴露的接口单独做一个薄薄的适配层服务。这个思路在新建项目的时候就要想清楚,否则后面代码一多,你想拆都拆不动。
2.3 项目生成后的第一眼检查
点击Create之后,IDEA会花点时间下载依赖,第一次通常比较慢,因为要把Spring Boot相关的jar包都拉到你本地的Maven仓库。等待期间,观察左下角进度条,如果长时间卡着不动,大概率是网络问题,可以考虑给Maven配置阿里云镜像,这个我在后面问题排查里再详细讲。
依赖下载完成后,你会看到一个标准的Spring Boot工程结构。我第一眼会先看src/main/java下有没有一个带@SpringBootApplication注解的启动类,比如DemoApplication。这个东西就是整个项目的入口,记住一个规律:SpringApplication.run(DemoApplication.class, args)这行代码一执行,内嵌的Tomcat就启动了,你的Web服务就在8080端口开始监听了。为了验证项目是不是真能起来,我会在项目里快速写一个测试接口:
package com.example.demospring; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, Spring!"; } }写完直接运行启动类的main方法,等控制台出现Started DemoApplication这样的日志后,浏览器访问http://localhost:8080/hello,能看到返回的Hello, Spring!,这个Spring Boot项目就算真正创建并验证成功了。
注意:这个小接口帮你验证的是“项目创建对不对、环境通不通”,它不值得保留在你的正式代码里,跑通以后就该把它删掉。我见过有人把HelloController留在生产项目里几年不删的,虽然不影响功能,但很不专业。
3. IDEA社区版创建纯Spring项目:一步不落的手工搭建
接下来这部分是重头戏,因为我身边不少朋友用的就是社区版,而且很多人想学的其实是“怎么在不依赖Spring Boot的情况下,手动搭一个Spring项目”。这个需求太真实了,我自己刚学Spring的时候就是这么干的。下面我把完整过程写出来,每一步都给出明确的代码和理由。
3.1 创建空Maven工程并引入Spring核心依赖
在IDEA社区版里,打开File → New → Project,选择Maven,不勾选任何骨架模板,直接创建一个空的Maven工程。创建完之后,IDEA会生成一个最基本的pom.xml。接下来手动把Spring依赖加进去。
我推荐的最小可用依赖配置是这样:
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>5.3.41</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies>这里我特意把spring-context单独拎出来,是因为它包含了Spring IoC容器的核心实现。只加这么一个是刻意为之,让案例足够简单,因为我们的目的不是堆功能,而是验证Spring能不能把Bean管理起来。
如果你要做的是Web项目,还需要额外引入Spring MVC相关的东西,比如spring-webmvc。但对于刚创建项目、先验证容器能不能用来说,spring-context已经足够。
3.2 写配置类、定义Bean和测试容器
依赖加好以后,在src/main/java下创建一个包,比如com.example.demo。然后写一个最普通的类,业务上完全没有什么技术含量,就是验证用的:
package com.example.demo; import org.springframework.stereotype.Component; @Component public class GreetingService { public String greet(String name) { return "Hello, " + name + "!"; } }再写一个配置类,用来告诉Spring去哪里扫描组件:
package com.example.demo; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.example.demo") public class AppConfig { }@Configuration这行注解的作用,是把这个类标记为Spring的配置类;@ComponentScan则指定了扫描路径。Spring启动的时候,会扫描这个包下面所有带有@Component、@Service、@Repository等注解的类,把它们注册成Bean。
最后写一个入口类,用AnnotationConfigApplicationContext来测试:
package com.example.demo; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class DemoApplication { public static void main(String[] args) { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) { GreetingService greetingService = context.getBean(GreetingService.class); System.out.println(greetingService.greet("Spring")); } } }运行这个main方法,控制台如果输出Hello, Spring!,说明你的手写Spring项目已经创建成功,容器能正常扫描、注册并获取Bean了。这个小例子别看简单,它把IoC容器的工作过程完整走了一遍,比直接拉一个Spring Boot项目跑起来所理解到的东西要深刻得多。
3.3 让手写的Spring Web项目在Tomcat里跑起来
上面那个例子验证了IoC容器,但很多人的目的是做Web开发,所以需要跑出一个能被浏览器访问的页面。如果是Spring Boot项目,内嵌Tomcat已经帮你解决了一切;但社区版里纯手写的Spring MVC项目,还需要想办法配一个Servlet容器。
我的经验是做Web项目时直接引入spring-boot-starter-web依赖,然后用手写@Configuration而不是使用被广泛提及的自动装配,这样既能感受Spring Boot的便利,又能保持手动创建项目的感觉。具体做法是保留前面工程的基础上,在pom.xml里增加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.18</version> </dependency>然后写一个Controller和一个带@EnableAutoConfiguration注解的启动类:
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @EnableAutoConfiguration @ComponentScan("com.example.demo") @RestController public class ManualSpringApplication { public static void main(String[] args) { SpringApplication.run(ManualSpringApplication.class, args); } @GetMapping("/manual") public String manual() { return "Manual Spring Project is running!"; } }运行main方法后,访问http://localhost:8080/manual,看到返回字符串就说明成功了。这种方式的好处是:你没有用IDEA内置的Spring Initializr,但依然靠着Spring Boot自动配置和内置Tomcat把Web项目跑了起来。
如果你希望走更经典的纯Spring MVC路线,可以在pom.xml里配置tomcat7-maven-plugin,然后用mvn tomcat7:run命令启动。但说实话,这样既绕远又容易出现插件版本兼容问题,我现在给朋友推荐的都是上面这种“手动写配置类 + Spring Boot启动器”的方式,兼顾学习意义和实际效率。
4. 项目创建出来之后,骨架为什么会是长这样
很多人创建完Spring项目,看一眼目录结构就赶紧写代码去了,根本不思考这个骨架是怎么来的。我觉得这很可惜,因为骨架里每个目录、每个文件的摆放都有讲究,把这些弄明白了,你对项目的掌控力会高一个档次。
4.1 Maven目录结构与分层职责
标准Maven工程的目录结构长这样:
src/main/java 放Java源代码 src/main/resources 放配置文件、静态资源、模板文件 src/test/java 放单元测试代码 target Maven构建输出目录,编译后的class文件、打好的jar包都在这我经常被问到一个问题:为什么IDEA里看不到target目录?其实不是没有,而是IDEA默认会过滤掉这个目录,因为它是构建产物,一般不需要纳入版本管理,也不该出现在代码审查里。真要看的话,可以在IDEA的设置里取消对该目录的过滤,或者直接在磁盘上打开工程文件夹查看。
分层这块,我一般建议在src/main/java下按照包名区分业务层次,比如controller、service、repository、entity、config等。这样做让代码职责清晰,也是Spring项目最主流的风格。不要把所有类都堆在同一个包下面,那是给自己找麻烦。
4.2 从启动类到Bean注册,一个请求是怎么走起来的
我拿一个标准的Spring Boot项目来拆解。启动类DemoApplication上的@SpringBootApplication是一个组合注解,它相当于@Configuration + @EnableAutoConfiguration + @ComponentScan这三个注解的集合。也就是说,启动类本身就标记了扫描范围和自动配置的开关。
当程序运行时,Spring Boot会先启动内嵌Tomcat,然后根据你在pom里引入的依赖执行对应的自动配置。引入spring-boot-starter-web就自动配置了DispatcherServlet和Spring MVC相关组件,引入spring-boot-starter-data-jpa就自动配置数据源和ORM映射。
接着,@ComponentScan会扫描当前启动类所在的包和所有子包,把标有@RestController、@Service、@Repository等注解的类都注册进容器。你写一个@GetMapping("/hello")的接口,Spring MVC框架接收到外部HTTP请求后,就是根据这个注解做路由匹配,找到对应的方法去执行的。
4.3 顺便说说Spring三级缓存和Bean创建
既然热搜词里反复出现“Spring三级缓存原理”,这里就借着“项目创建后容器是怎么管理Bean的”这个话题,把它说清楚。所谓三级缓存的本质,是Spring在处理Bean循环依赖时的三个缓存集合:
- 一级缓存:保存完整创建好的单例Bean。
- 二级缓存:保存提前暴露的早期Bean引用(尚未完成全部属性赋值的对象)。
- 三级缓存:保存一个对象工厂,这个工厂能在需要的时候生成早期Bean引用。
循环依赖的场景就是A依赖B、B依赖A。容器创建A时发现需要B,就去创建B,B又要依赖A,这时候容器从三级缓存里找到A的工厂,提前拿到A的早期引用,先塞给B,等B创建完后,A再拿到完整的B。整个过程靠这三层缓存兜底,既绕开了“鸡生蛋蛋生鸡”的死循环,又确保最终拿到的是完整对象。
我刚学的时候总觉得这是很高深的原理,直到自己在手写Spring项目、调试Bean创建日志的时候才彻底理解,它其实就是一套为了解决循环依赖而设计的“先给引用、后补完整”的机制。你不需要背概念,等项目创建多了、看多了启动日志,自然就懂了。
提示:Spring Boot 2.6版本之后,默认把循环依赖的允许开关关掉了,你如果新建项目遇到
Consider defining a bean of type这类报错,优先想想是不是循环依赖导致的,而不是急着改配置强行放行。
5. 实操中的高频问题与排查速查表
这部分全是干货。我这些年创建、维护Spring项目,踩过无数回坑,这里挑出现频率最高的几个,做成速查表,你们遇到类似的问题直接照着定位就行。
5.1 创建阶段最常见的问题
| 问题现象 | 直接原因 | 解决办法 |
|---|---|---|
| 新建项目时没有Spring Initializr选项 | 使用的是IDEA社区版 | 去start.spring.io在线生成后导入,或参照第3章手动搭建 |
项目启动报Invalid source release: 17 | 项目编译级别和JDK不匹配 | 检查Project Structure里的SDK和Java版本,确保统一 |
| 依赖下载失败或超时 | 访问中央仓库慢/被限制 | 在Maven的settings.xml里配置阿里云镜像 |
运行启动类报Port 8080 was already in use | 8080端口被其他进程占用 | 换端口,在application.properties中配置server.port=8081 |
Failed to configure a DataSource | 引入了数据库依赖但没有配置连接信息 | 去掉不需要的数据库依赖,或配置数据源信息 |
这里特别展开说下Maven阿里云镜像怎么配置。打开你本地的Maven安装目录下的conf/settings.xml,在<mirrors>节点里加:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>加了之后,依赖下载速度会明显提升,这是国内开发者必做的一步,我不管在哪个电脑上配Maven都会顺手配上。
5.2 IDEA使用中的几个疑难症状
这里说几个IDEA使用过程中常见的诡异问题,虽然不是创建项目时必踩的,但特别容易让人烦躁。
第一,代码格式化失效。新手经常碰到“按了Ctrl+Alt+L没反应,代码缩进乱糟糟”的情况。这时候优先检查File → Settings → Keymap里快捷键有没有被别的插件占用,然后再检查Editor → Code Style里的格式化配置是不是被改动了。我印象里换过IdeaVim插件后格式化就会冲突,禁用插件或者改掉快捷键就恢复正常了。
第二,target目录不显示。新建项目后默认确实不显示,这在前面提到过。但有一种情况是你明明在磁盘上看到target里有编译好的class文件,IDEA项目树里不显示。解决办法是File → Settings → Editor → File Types,看看有没有把target目录误认成忽略项;或者在Project Structure → Modules里把target目录标记为普通目录,刷新一下就好了。
第三,项目跑不起来但代码看着完全没问题。这种时候我建议先执行一遍mvn clean compile,看Maven输出的具体报错。IDEA的缓存有时候会跟实际依赖不同步,执行File → Invalidate Caches and Restart清一次缓存,能解决大部分莫名其妙的编译问题。我保持一个习惯,换分支、拉代码、改完pom.xml后,都会先执行一次mvn clean package,通过后再用IDEA跑。
5.3 如何避免创建一个“跑不起来的项目”
我在团队里带过不少新人,总结下来,“跑不起来”的主要原因就三个:版本不匹配、依赖堆积、不看日志。
版本不匹配我已经反复强调了,Java版本、Spring Boot版本、Maven编译级别必须匹配。依赖堆积就是指看到什么依赖都往上加,加了又不用,最后版本冲突、自动配置打架,项目自然起不来。不看日志则是最要命的,很多新人一看到控制台飘红就慌,把报错信息略过,直接截图问人。我建议训练自己先读第一行报错,大多数情况下报错里已经写清楚了原因和解决方法。
我这里还有一个非常管用的经验:创建一个新Spring项目后,第一件事不是写业务代码,而是先把项目跑起来一次,再往上加东西。如果基础工程都跑不起来,后面叠加任何代码都会让你分不清问题出在业务逻辑还是工程配置上。
收尾的几句实在话
写到这里,常见的创建Spring项目方法和问题都聊得差不多了。我个人这几年下来的体会是:创建项目这件事,熟练以后三分钟内搞定,但真正值钱的能力是你不依赖工具也能搭出来,理解Spring容器在背后做了什么,出了问题能顺着日志和配置找到原因。尤其是初学者,我强烈建议你去IDEA社区版里手写一个带@Configuration和@ComponentScan的纯Spring工程,别看它麻烦,这一趟走下来,你对IoC的理解绝对比直接套Spring Boot模板要扎实得多。
最后再分享一个小技巧:每次新建Spring项目的时候,都把Spring Boot版本和JDK版本固化下来,写成一个团队层面的模板或笔记。我自己的标准是:新项目一律JDK 17 + Spring Boot 3.2.x,除非遇到兼容限制,否则不降级。这样你在不同项目、不同电脑之间切换的时候,踩坑的概率会大大降低,也让整个团队的技术栈保持稳定。