Spring项目从零搭建:IDEA+Maven配置与依赖注入实战

📅 2026/7/30 7:35:07
Spring项目从零搭建:IDEA+Maven配置与依赖注入实战
1. 从零开始的困惑为什么你的Spring项目总在第一步卡住每次打开IntelliJ IDEA新建一个Maven项目然后兴冲冲地想把Spring依赖加进去结果不是pom.xml报红就是项目死活跑不起来。这场景是不是很熟悉我见过太多新手包括几年前的我自己都卡在这个看似简单的“配置”环节上。问题往往不在于Spring本身有多复杂而在于IDEA、Maven和Spring这三者之间的“握手协议”没对上。网上的教程要么太老要么太简略只告诉你“复制这段XML”却不解释为什么一旦环境稍有不同你就得花几个小时去排查。今天我们就来彻底解决这个问题。这不是一个简单的“复制粘贴”教程而是一个带你理解背后逻辑的“保姆级”指南。我会假设你是一个刚装好IDEA和Java的纯新手从Maven的配置原理讲起一步步带你搭建一个能跑起来的Spring项目骨架。更重要的是我会分享那些官方文档里不会写、但实际开发中一定会遇到的坑和技巧。比如为什么你的Maven仓库总是下载失败为什么Spring的版本号选6.x还是5.xIDEA里那些让人眼花缭乱的设置到底哪个才是关键我们的目标很简单让你不再惧怕这个初始配置过程建立起一个清晰、稳固的项目基础。以后无论是要集成Spring Boot、Spring Security还是Spring Cloud你都能从容应对因为根已经扎稳了。2. 基石准备别让Maven成为你的绊脚石在接触Spring之前我们必须先确保Maven这个“项目经理”能正常工作。很多Spring配置问题根源其实在Maven。2.1 Maven的核心settings.xml与本地仓库Maven不像一个普通的软件装好就能用。它的行为几乎完全由一个叫settings.xml的文件控制。这个文件通常位于你的用户目录下的.m2文件夹里例如C:\Users\你的用户名\.m2\settings.xml。如果这个文件不存在Maven会使用它自带的全局配置但那通常不适合国内网络环境。首先我们需要配置镜像仓库。由于Maven中央仓库在国外直接访问速度慢且不稳定配置国内镜像如阿里云镜像是第一步。!-- 在 settings.xml 的 mirrors 标签内添加 -- mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror注意mirrorOf*/mirrorOf表示对所有仓库请求都使用这个镜像。这能极大提升依赖下载速度。但如果你需要从某些特定私有仓库下载公司内部包可能需要更精细的配置例如将*改为central只对中央仓库生效。其次是本地仓库路径。默认的.m2/repository在C盘随着项目增多会占用大量系统盘空间。我强烈建议你修改它。!-- 在 settings.xml 的 settings 标签下与mirrors同级添加 -- localRepositoryD:\Develop\maven-repository/localRepository这样所有下载的jar包都会存放在D盘指定目录重装系统也不怕丢失。修改后记得把原来C盘.m2/repository里的内容如果有复制到新路径避免重复下载。2.2 IDEA与Maven的绑定关键设置项解析IDEA内置了Maven但为了统一和可控我建议使用你自己安装并配置好的Maven。打开IDEA进入File - Settings - Build, Execution, Deployment - Build Tools - Maven。这里有三个核心设置Maven home path指向你本地安装的Maven目录例如D:\apache-maven-3.8.8。不要使用Bundled (Maven 3)用你自己的。User settings file一定要勾选Override然后指向我们刚才修改好的那个settings.xml文件。这样IDEA才会应用你配置的镜像和本地仓库路径。Local repository这个路径会自动根据你的settings.xml文件更新确认它显示的是你修改后的路径如D:\Develop\maven-repository。完成这一步点击Apply。然后强烈建议你点击右侧的Maven工具窗口通常在IDEA右侧边栏点击那个像刷新一样的按钮Reimport All Maven Projects。这个操作会让IDEA根据新的配置重新加载所有Maven项目并开始从新镜像下载依赖到新仓库。第一次可能会花点时间但这是确保环境一致的关键。一个常见的坑是即使你配置了镜像IDEA在后台构建时可能还是用的旧缓存或配置。每次修改settings.xml或Maven主路径后执行一次Reimport是好习惯。3. 创建项目与Spring依赖引入做出正确的选择环境配好了现在开始创建项目。打开IDEA选择New Project。3.1 项目创建Maven Archetype的取舍在左侧选择Maven右侧你会看到Archetype选项。Archetype是项目模板对于纯Spring项目我建议不要勾选任何Archetype就创建一个最简单的maven-archetype-quickstart这是默认的或者直接空项目。因为很多Spring教程和Archetype可能版本过时依赖关系复杂不利于初学者理解。我们从一张白纸开始画。在GroupId通常填公司域名倒写如com.example、ArtifactId项目名如spring-demo和Version默认1.0-SNAPSHOT后点击创建。项目创建好后第一件事是检查JDK版本。在IDEA中按CtrlShiftAltS打开项目结构确保Project SDK是你安装的JDK建议JDK 11或17这是Spring 5和6的长期支持版本。Project language level建议与SDK版本一致。3.2 pom.xml的编写理解每一个标签打开项目根目录下的pom.xml这是Maven项目的核心配置文件。我们将一步步构建它。首先设置项目元数据和JDK编译版本?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdspring-demo/artifactId version1.0-SNAPSHOT/version properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding !-- 定义Spring版本方便统一管理 -- spring.version6.1.6/spring.version /properties在properties里定义版本号是最佳实践后续所有依赖引用${spring.version}即可升级时只需改这一处。接下来是重头戏添加Spring依赖。Spring是一个模块化的框架我们通常从核心容器开始。对于现代Spring5.x及以上最小化的核心依赖是spring-context它包含了IoC容器、AOP、事件等核心功能。dependencies !-- Spring Context (包含核心容器、依赖注入等) -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency /dependencies保存pom.xml文件后IDEA会自动开始下载依赖。你可以在底部的Build工具窗口看到下载日志也可以在右侧Maven工具窗口的Dependencies里看到添加的spring-context及其传递依赖如spring-core,spring-beans,spring-aop等。实操心得版本选择是个大学问。Spring 6.x需要JDK 17而Spring 5.x兼容JDK 8。对于新项目无历史包袱我强烈建议直接上Spring 6 JDK 17能享受到最新的语言特性和框架优化。如果你看到教程里还在用spring-webmvc这种较老的聚合依赖而你现在只是想学核心IoC用spring-context更精准、更现代。4. 编写第一个Spring Bean与配置让容器转起来依赖有了我们来写点代码看看Spring容器到底是怎么工作的。4.1 创建简单的Bean与配置类首先在src/main/java下创建包com.example.service然后创建一个简单的服务类。这个类就是一个普通的Java类我们称之为“Bean”。package com.example.service; public class MyService { private String message Hello from MyService!; public String getMessage() { return message; } public void setMessage(String message) { this.message message; } public void sayHello() { System.out.println(message); } }在Spring 3.0之后推荐使用基于Java的配置Configuration来代替传统的XML配置。我们在src/main/java下创建包com.example.config然后创建配置类package com.example.config; import com.example.service.MyService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration // 声明这是一个Spring配置类 public class AppConfig { Bean // 声明此方法返回的对象是一个Spring Bean由容器管理 public MyService myService() { MyService service new MyService(); service.setMessage(Hello, Spring Container!); return service; } }这个AppConfig类就相当于以前的applicationContext.xml。Bean注解告诉Spring“调用myService()方法把返回的MyService对象放到你的容器里名字就叫myService”。4.2 启动容器并获取Bean现在我们创建一个主类来启动Spring容器。在src/main/java下创建com.example包和MainApp类package com.example; import com.example.config.AppConfig; import com.example.service.MyService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MainApp { public static void main(String[] args) { // 1. 创建Spring容器传入配置类 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class); // 2. 从容器中获取Bean MyService service context.getBean(MyService.class); // 或者通过名字获取context.getBean(myService, MyService.class) // 3. 使用Bean service.sayHello(); // 输出: Hello, Spring Container! // 4. 关闭容器对于非Web应用这是个好习惯 context.close(); } }运行这个MainApp。如果一切配置正确你将在控制台看到输出Hello, Spring Container!。这个过程的意义在于MyService对象的创建和生命周期虽然这个例子简单不再由new关键字控制而是交给了AnnotationConfigApplicationContext这个Spring容器。容器根据AppConfig的指示创建了MyService的实例并把它管理起来。当我们需要时向容器“索取”getBean即可。这就是控制反转IoC最直观的体现。踩坑记录这里最容易出现的错误是NoSuchBeanDefinitionException意思是容器里找不到你要的Bean。排查步骤1. 检查配置类是否被Configuration注解。2. 检查Bean定义方法是否有Bean注解。3. 检查主类中AnnotationConfigApplicationContext构造器传入的配置类是否正确。4. 检查getBean时使用的类或名字是否与Bean方法名或显式指定的名字一致。5. 依赖注入实战让Bean之间产生联系单一个Bean没意思Spring的核心魅力在于管理Bean之间的关系即依赖注入DI。我们通过一个更经典的例子来演示。5.1 构造器注入更推荐的方式假设我们有一个数据访问层DAOBean和一个服务层ServiceBeanService需要依赖DAO。首先创建DAO接口及其实现package com.example.dao; public interface UserDao { String getUserName(); }package com.example.dao.impl; import com.example.dao.UserDao; import org.springframework.stereotype.Repository; Repository // 这是一个Spring的Stereotype注解标记为数据访问层Bean同时它本身也被Component注解会被组件扫描到。 public class UserDaoImpl implements UserDao { Override public String getUserName() { return 张三 (来自DAO); } }然后创建Service类并通过构造器注入DAOpackage com.example.service; import com.example.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; Service // 标记为服务层Bean public class UserService { private final UserDao userDao; // 构造器注入Spring会自动寻找容器中UserDao类型的Bean并注入进来 Autowired // 在Spring 4.3以后如果类只有一个构造器Autowired可以省略。但显式写出更清晰。 public UserService(UserDao userDao) { this.userDao userDao; } public void showUser() { System.out.println(User: userDao.getUserName()); } }5.2 启用组件扫描与运行测试现在我们需要修改配置类让它能自动扫描并注册带有Component,Service,Repository,Controller等注解的Bean。package com.example.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; Configuration ComponentScan(basePackages com.example) // 扫描com.example包及其子包下的所有组件 public class AppConfig { // 不再需要显式定义Bean因为UserDaoImpl和UserService已被注解标记会被自动扫描注册。 }更新主类进行测试package com.example; import com.example.config.AppConfig; import com.example.service.UserService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MainApp { public static void main(String[] args) { AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class); UserService userService context.getBean(UserService.class); userService.showUser(); // 输出: User: 张三 (来自DAO) context.close(); } }运行你会看到Service成功调用了DAO的方法。整个过程我们并没有在代码中new UserDaoImpl()也没有在UserService中new UserDaoImpl()。Spring容器在创建UserServiceBean时发现它的构造器需要一个UserDao类型的参数于是就在容器里寻找UserDao类型的Bean找到了UserDaoImpl然后自动创建UserDaoImpl实例并传递给UserService的构造器。这就是依赖注入的魔力。经验技巧关于Autowired的位置。除了构造器注入还有字段注入直接在字段上加Autowired和Setter方法注入。我强烈推荐使用构造器注入原因有三1. 明确依赖关系所有必需的依赖都在构造器中声明。2. 方便单元测试你可以直接通过构造器传入Mock对象。3. 保证依赖不可变字段用final修饰线程安全。字段注入虽然写起来简单但隐藏了依赖且让类变得不易测试。6. 配置文件与外部化配置告别硬编码在实际项目中数据库连接、服务器地址等配置信息绝不能硬编码在Java代码里。Spring提供了强大的外部化配置支持。6.1 使用PropertySource与Value假设我们有一个jdbc.properties文件放在src/main/resources目录下Maven标准资源目录db.urljdbc:mysql://localhost:3306/mydb db.usernameroot db.passwordsecret app.nameMySpringDemo修改配置类加载这个属性文件并定义一个Bean来演示注入package com.example.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; Configuration ComponentScan(basePackages com.example) PropertySource(classpath:jdbc.properties) // 加载类路径下的属性文件 public class AppConfig { // 使用Value将属性值注入到配置类的字段中 Value(${app.name}) private String appName; Bean public String appInfo() { return Application Name: appName; } // 也可以直接在Bean方法参数中使用Value Bean public DataSourceConfig dataSourceConfig(Value(${db.url}) String url, Value(${db.username}) String username, Value(${db.password}) String password) { DataSourceConfig config new DataSourceConfig(); config.setUrl(url); config.setUsername(username); config.setPassword(password); return config; } }这里DataSourceConfig是一个简单的POJO类用于存放配置。在实际项目中你可能会直接配置一个DataSourceBean。6.2 属性占位符解析器PropertySourcesPropertySource默认只能解析基本的属性文件。如果属性文件中包含如${}这样的占位符用于引用环境变量或其他属性需要注册一个PropertySourcesPlaceholderConfigurerBean。在Spring 4.3及以后如果你使用了PropertySource并且配置类上有ConfigurationSpring Boot会自动注册它。但在纯Spring项目中为了保险可以显式配置Configuration ComponentScan(basePackages com.example) PropertySource(classpath:jdbc.properties) public class AppConfig { // 确保属性占位符${...}能被解析 Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } // ... 其他配置 }注意这个Bean必须是static的因为它需要在Spring容器生命周期的早期被初始化。避坑指南属性文件找不到确保文件在src/main/resources下并且Maven在构建时会将这个目录下的文件复制到target/classes类路径。在IDEA中你可以将resources目录标记为Resources Root右键目录 - Mark Directory as - Resources Root。另外classpath:前缀表示从类路径根目录开始查找。如果属性文件在子目录如config/下则路径应为classpath:config/jdbc.properties。7. 整合Web与Spring MVC从控制台到浏览器前面的例子都是控制台应用但Spring更广泛的应用场景是Web。我们来快速搭建一个最简单的Spring MVC应用感受一下如何将Spring容器与Web服务器整合。7.1 添加Web依赖首先在pom.xml中添加Spring Web MVC和Servlet API依赖。注意我们这里构建的是一个传统的、需要部署到外部Servlet容器如Tomcat的Web应用而非Spring Boot内嵌容器的形式。dependencies !-- ... 之前的spring-context依赖 ... -- !-- Spring Web MVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- Servlet API (编译时需要运行时由Tomcat提供) -- dependency groupIdjakarta.servlet/groupId artifactIdjakarta.servlet-api/artifactId version6.0.0/version scopeprovided/scope /dependency /dependencies注意Servlet API的scopeprovided/scope意思是这个依赖在编译和测试时需要但在运行时由目标环境Tomcat提供不会被打进最终的WAR包。7.2 配置DispatcherServlet与根容器在纯Spring MVC中核心是配置DispatcherServlet。传统方式是在web.xml中配置现在我们更常用Java配置类来实现。首先我们需要一个类来实现WebApplicationInitializer接口这是Servlet 3.0的规范允许我们用代码代替web.xml。package com.example.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { // 指定根容器的配置类用于配置Service, Repository, DataSource等非Web组件 Override protected Class?[] getRootConfigClasses() { return new Class?[] { AppConfig.class }; // 我们之前写的核心配置类 } // 指定DispatcherServlet应用容器的配置类用于配置Controller, 视图解析器等Web组件 Override protected Class?[] getServletConfigClasses() { return new Class?[] { WebConfig.class }; // 即将创建的Web配置类 } // 指定DispatcherServlet的映射路径/表示处理所有请求 Override protected String[] getServletMappings() { return new String[] { / }; } }这个类在Web应用启动时会被Servlet容器自动检测并加载。它创建了两个Spring容器一个“根容器”Root WebApplicationContext由getRootConfigClasses配置和一个“Servlet容器”Servlet WebApplicationContext由getServletConfigClasses配置。根容器是Servlet容器的父容器通常用于共享的Bean如服务层、数据层Servlet容器则包含Web相关的Bean如控制器。这种分层设计是经典Spring MVC应用的标准模式。7.3 创建Web配置类与控制器创建WebConfig类启用MVC并配置视图解析器这里我们用最简单的内部资源视图解析器用于JSP。package com.example.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration EnableWebMvc // 启用Spring MVC ComponentScan(basePackages com.example.web) // 扫描Web层组件Controller public class WebConfig implements WebMvcConfigurer { // 配置JSP视图解析器 Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp(/WEB-INF/views/, .jsp); } }创建一个简单的控制器。在src/main/java下创建包com.example.web然后创建HomeControllerpackage com.example.web; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; Controller // 声明这是一个控制器 public class HomeController { GetMapping(/) // 处理对根路径的GET请求 public String home(Model model) { model.addAttribute(message, Hello, Spring MVC from IDEA Maven!); return home; // 返回逻辑视图名对应 /WEB-INF/views/home.jsp } }最后在src/main/webapp/WEB-INF/views/目录下创建home.jsp视图文件需要手动创建webapp,WEB-INF,views目录% page contentTypetext/html;charsetUTF-8 languagejava % html head titleSpring MVC Demo/title /head body h1${message}/h1 /body /html7.4 配置Tomcat并运行至此一个最基础的Spring MVC项目结构就完成了。为了运行它我们需要配置一个本地Tomcat服务器。在IDEA中点击右上角的“Add Configuration”添加一个Tomcat Server - Local。在Deployment选项卡中添加一个Artifact选择你的项目生成的war exploded例如spring-demo:war exploded。Application context可以设为/或/demo。配置好后启动Tomcat。打开浏览器访问http://localhost:8080/如果你的应用上下文是/就能看到页面上显示“Hello, Spring MVC from IDEA Maven!”。这个过程虽然步骤比Spring Boot多但它清晰地展示了Spring MVC应用的完整组装过程从WebApplicationInitializer启动到DispatcherServlet分发请求再到控制器处理并渲染视图。理解了这个你再去看Spring Boot那种“开箱即用”的体验就会明白它背后帮你自动配置了什么。个人体会从纯Spring核心到Spring MVC的整合是理解Spring生态的关键一步。很多初学者直接跳进Spring Boot虽然快速但容易对底层机制产生模糊。像这样手动搭一次你会对DispatcherServlet、父子容器、配置类扫描、视图解析等概念有更扎实的理解。以后无论遇到多复杂的Web项目或者Spring Boot自动配置出了问题你都有能力去排查和定制。