Spring Framework 零基础入门:从概念到实践的深度解析 (约3000字)
亲爱的编程新手们,或者对 Spring Framework 感到好奇的朋友们:
欢迎来到 Spring 的世界!如果你曾听说过 Spring 的大名,知道它是 Java 生态中最流行、最强大的框架之一,但也因为它庞大的体系和各种眼花缭乱的概念而望而却步,那么这篇教程就是为你准备的。
我们将从零开始,不假设你有任何 Spring 使用经验,甚至只要求你具备基本的 Java 编程知识。我们将一起揭开 Spring 的神秘面纱,理解它解决的核心问题,掌握它最基础也是最重要的概念,并通过动手实践来巩固学习。
请记住,Spring Framework 本身是一个庞大而复杂的体系,它包含了众多模块(如 Core, AOP, Data Access, Web MVC, Security 等)。本教程作为零基础入门,会 专注于 Spring 最核心、最基础的部分:IoC(控制反转)和 DI(依赖注入)。这就像学习一门语言,首先要掌握字母和基本的词汇,才能构建句子,最终写出文章。掌握了 IoC 和 DI,你就拿到了通往 Spring 大门的钥匙。
准备好了吗?让我们开始这段激动人心的学习旅程!
第一部分:为什么是 Spring?传统 Java 开发的痛点
在深入了解 Spring 之前,我们先来看看在没有 Spring 这样的框架时,Java 开发可能会遇到哪些问题。理解了这些痛点,你才能真正体会到 Spring 的价值。
想象一下,你正在开发一个简单的用户管理系统。你需要一个 UserService
来处理用户业务逻辑,它需要依赖一个 UserRepository
来操作数据库。
在没有框架的情况下,代码可能会写成这样:
“`java
// 1. 数据访问层
public class UserRepository {
public void saveUser(User user) {
System.out.println(“Saving user: ” + user.getName());
// … 实际的数据库操作代码,例如 JDBC 连接
}
}
// 2. 业务逻辑层
public class UserService {
private UserRepository userRepository; // UserService 依赖 UserRepository
public UserService() {
// 问题在这里:UserService 自己创建了 UserRepository 的实例
this.userRepository = new UserRepository();
}
public void registerUser(User user) {
// ... 业务逻辑
userRepository.saveUser(user);
}
// ... 其他方法
}
// 3. 使用示例
public class MainApplication {
public static void main(String[] args) {
UserService userService = new UserService();
User newUser = new User(“Alice”);
userService.registerUser(newUser);
}
}
“`
这段代码看起来很简单,但隐藏着几个问题:
- 紧耦合 (Tight Coupling):
UserService
直接在自己的构造函数里new UserRepository()
。这意味着UserService
和UserRepository
高度绑定。如果UserRepository
的构造函数变了(比如需要传入一个数据库连接),UserService
的代码也必须修改。这种紧耦合使得代码难以维护和扩展。 - 难以测试 (Difficult Testing): 在单元测试
UserService
的registerUser
方法时,由于它内部创建并使用了真实的UserRepository
,测试会自动依赖真实的数据库操作。这使得单元测试变得困难(你需要配置数据库环境),而且测试速度慢、不够纯粹(我们只想测试UserService
的业务逻辑,而不是UserRepository
的数据库访问)。我们很难替换掉UserRepository
的实现(比如换成一个模拟的假对象来进行测试)。 - 大量的重复代码 (Boilerplate Code): 在更复杂的企业应用中,对象之间的依赖关系会非常复杂。你可能需要在很多地方手动创建和管理对象的实例,写大量的
new
语句,这会产生很多重复的“样板代码”。 - 对象生命周期管理 (Object Lifecycle Management): 在大型应用中,有些对象可能是单例(全局只有一个实例),有些可能是每次使用都创建新实例,有些可能需要在特定时机初始化或销毁。手动管理这些对象的创建、初始化、使用和销毁是非常繁琐且容易出错的。
Spring Framework 的诞生,很大程度上就是为了解决这些问题。
第二部分:什么是 Spring Framework?核心概念初探
简单来说,Spring Framework 是一个开源的、用于构建企业级 Java 应用的框架。它提供了全面的基础设施支持,帮助开发者更方便、更高效地构建应用程序。
Spring Framework 并非强制你使用它的所有功能,你可以根据需要选择性地使用它的某些模块。它的核心理念是 非侵入性(Non-invasiveness),即它尽量不强制你的代码必须继承或实现 Spring 特定的类或接口,让你的业务代码更加纯粹。
Spring 的强大之处在于它提供了一套统一的方式来管理应用程序中的各种对象及其相互关系。而这套方式的核心,就是我们即将深入探讨的 IoC(控制反转) 和 DI(依赖注入)。
第三部分:Spring 的灵魂:IoC 和 DI
这可能是 Spring 最重要也最容易让初学者感到困惑的概念。但请别担心,我们将用最通俗易懂的方式来解释它们。
3.1 IoC (Inversion of Control – 控制反转)
核心思想: 把对象的创建、组装、管理(控制)的权力,从对象本身或调用方,转移(反转)给一个外部的容器。
用一个不太严谨但生动的比喻来理解:
- 传统方式: 你去餐厅吃饭,你需要自己去找食材、自己做饭。所有的事情你都亲力亲为。这是你自己控制整个流程。
- IoC 方式: 你去餐厅吃饭,你只需要点餐,服务员会帮你把做好的食物端上来。你不再需要关心食材怎么来、饭是怎么做的,这些控制权交给了餐厅(容器)。你只负责“点餐”和“享用”。
在我们的 Java 例子中,传统方式下,UserService
需要谁(UserRepository
),就自己去 new
谁。这是 UserService
自己控制依赖的创建。
而使用了 IoC 后,UserService
不再自己创建 UserRepository
,它只是声明自己需要一个 UserRepository
。Spring 容器 会负责创建 UserRepository
的实例,并在适当的时候将这个实例提供给 UserService
。创建和管理 UserRepository
的控制权,从 UserService
反转给了 Spring 容器。
IoC 是一种思想,一种设计原则。那 Spring 是如何实现这种思想的呢?答案就是 依赖注入 (DI)。
3.2 DI (Dependency Injection – 依赖注入)
核心思想: IoC 的具体实现方式。容器负责创建对象,并通过某种方式(构造函数、Setter 方法、字段)将该对象所依赖的其他对象“注入”给它。
回到餐厅的比喻:你点了牛排(相当于 UserService
),牛排需要酱汁(相当于 UserRepository
)。餐厅(容器)做好牛排后,不是让牛排自己去拿酱汁,而是由服务员(注入机制)把酱汁直接倒在牛排上,或者单独配给你。这个过程就是“注入”。
在 Spring 中,容器会:
- 创建
UserRepository
的实例。 - 创建
UserService
的实例。 - 将
UserRepository
的实例“注入”到UserService
中,使得UserService
可以使用UserRepository
。
注入通常有三种方式:
-
构造函数注入 (Constructor Injection): 通过类的构造函数来注入依赖。这是 Spring 推荐的方式,因为它能确保依赖在对象创建时就存在,并明确表达了对象所需的依赖。
“`java
public class UserService {
private final UserRepository userRepository; // 依赖是 final 的,不可变// 通过构造函数注入 UserRepository public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public void registerUser(User user) { userRepository.saveUser(user); }
}
“`
* Setter 方法注入 (Setter Injection): 通过类的 Setter 方法来注入依赖。允许对象在创建后修改依赖,适用于可选依赖或循环依赖的场景(尽管循环依赖应尽量避免)。“`java
public class UserService {
private UserRepository userRepository;// 提供一个 Setter 方法用于注入 public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } public void registerUser(User user) { // 需要在使用前确保 userRepository 已被注入 if (userRepository != null) { userRepository.saveUser(user); } else { throw new IllegalStateException("UserRepository not set!"); } }
}
“`
* 字段注入 (Field Injection): 直接在字段上标记,让容器通过反射将依赖注入到字段中。代码简洁,但不够直观,且难以进行单元测试(因为测试时你无法直接通过构造函数或 Setter 传入 Mock 对象)。通常不推荐在大规模应用中滥用,尤其是在构造函数注入可行的场景下。“`java
public class UserService {
// 直接在字段上标记,Spring 会注入
// 在使用注解配置时常见
private UserRepository userRepository; // Spring 会通过反射设置这个字段public void registerUser(User user) { userRepository.saveUser(user); }
}
“`
DI 的好处:
- 解耦 (Decoupling): 对象不再负责创建其依赖,只声明需要什么。这使得对象之间的依赖关系变得松散,一个对象的改变不容易影响到它的依赖方。
- 易于测试 (Easy Testing): 在进行单元测试时,我们可以轻松地用模拟对象(Mock Object)替换真实的依赖,只测试当前对象的逻辑,而不用担心依赖的实际行为。例如,测试
UserService
时,可以注入一个假的UserRepository
,记录saveUser
方法是否被调用,而不需要真的去碰数据库。 - 提高可维护性和可扩展性 (Maintainability & Extensibility): 由于解耦,修改或替换某个组件的实现变得容易,只需在配置中改变注入的对象即可,而不需要修改使用该对象的代码。
- 降低复杂性 (Reduced Complexity): 容器统一管理对象的创建和依赖关系,开发者无需手动处理这些复杂的相互创建过程。
总结: IoC 是一种设计思想(控制权反转),DI 是实现 IoC 的一种具体手段(通过注入方式将依赖提供给对象)。在 Spring 中,IoC 和 DI 几乎是同义词,通常提到 IoC/DI,指的就是 Spring 容器通过 DI 的方式来管理对象的依赖关系。
第四部分:Spring 容器和 Bean
理解了 IoC/DI 的概念后,我们需要知道 Spring 是如何实现它的。这就引入了 Spring 容器 (Spring Container) 和 Bean (Bean) 的概念。
4.1 Spring 容器
Spring 容器是负责创建、配置和管理 Bean 的核心组件。 它读取配置信息(告诉它哪些类需要被管理,它们之间的依赖关系是什么),然后根据这些信息创建对象,并将它们组装起来。
Spring 提供了不同类型的容器实现,最常用的是 ApplicationContext
。ApplicationContext
是 BeanFactory
的子接口,它在 BeanFactory
的基础上提供了更多企业级的功能,如国际化支持、事件发布、资源加载等。对于绝大多数应用来说,使用 ApplicationContext
是推荐的选择。
你可以把 Spring 容器想象成一个超级工厂或者一个对象仓库,你告诉它你需要什么对象(通过配置),它会帮你把对象创建好,并处理好它们之间的依赖关系,然后你就可以直接从容器中获取并使用这些对象了。
4.2 Bean
Bean 是指被 Spring 容器管理的对象。 任何一个 Java 类,只要它被 Spring 容器创建、配置和管理,就可以被称为一个 Spring Bean。
在我们的例子中,如果我们配置让 Spring 容器来管理 UserRepository
和 UserService
的实例,那么 UserRepository
和 UserService
的实例就都成为了 Spring Bean。
Spring 容器会根据配置信息来决定如何创建 Bean(例如,是使用默认构造函数,还是使用特定的构造函数,或者通过工厂方法),以及如何将它们之间的依赖关系注入进去。
第五部分:如何告诉 Spring 容器创建和管理 Bean?配置方式
Spring 容器需要知道哪些类应该成为 Bean,以及它们之间的依赖关系是什么。这就是通过 配置 来完成的。Spring 提供了多种配置方式:
- XML 配置: 传统的配置方式,通过 XML 文件来定义 Bean 及其依赖。虽然现代开发中已经较少直接使用,但理解它有助于理解 Spring 的底层工作原理。
- 基于 Annotation (注解) 的配置: 通过在 Java 类中添加注解来告诉 Spring 如何管理 Bean。这是目前最常用、最简洁的配置方式。
- 基于 Java Code (JavaConfig) 的配置: 通过编写 Java 类和方法来定义 Bean。这种方式类型安全,比 XML 更灵活,也是现代 Spring 开发中常用的方式。
我们将逐一介绍这三种方式,并以我们的 UserService
和 UserRepository
例子为例。
假设我们有这两个类:
“`java
// UserRepository.java
public class UserRepository {
public void saveUser(User user) {
System.out.println(“Saving user: ” + user.getName() + ” via Repository”);
}
}
// UserService.java (使用构造函数注入)
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
System.out.println("UserService created with UserRepository.");
}
public void registerUser(User user) {
System.out.println("Registering user: " + user.getName() + " via UserService");
userRepository.saveUser(user);
}
}
// User.java (简单的模型类)
public class User {
private String name;
public User(String name) { this.name = name; }
public String getName() { return name; }
}
“`
5.1 XML 配置示例
创建一个 applicationContext.xml
文件:
“`xml
<!-- 定义 UserRepository Bean -->
<bean id="userRepository" class="com.example.UserRepository"/>
<!-- 定义 UserService Bean -->
<!-- 通过 constructor-arg 标签将 userRepository Bean 注入到 UserService 的构造函数中 -->
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository"/>
</bean>
“`
<bean>
标签用于定义一个 Bean。id
: Bean 的唯一标识符,你可以通过它从容器中获取 Bean。class
: Bean 对应的类的全限定名。<constructor-arg>
: 用于指定构造函数参数。ref="userRepository"
表示将 id 为userRepository
的 Bean 引用作为参数传入。
如何使用 XML 配置启动容器并获取 Bean:
“`java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.UserService;
import com.example.User;
public class MainApplicationXml {
public static void main(String[] args) {
// 1. 创建 Spring 容器,加载 XML 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);
System.out.println(“Spring container started.”);
// 2. 从容器中获取 Bean (根据 id 获取)
UserService userService = (UserService) context.getBean("userService");
System.out.println("UserService bean obtained.");
// 3. 使用获取到的 Bean
User newUser = new User("Bob");
userService.registerUser(newUser);
// 4. 关闭容器 (对于 standalone 应用是可选的,但好习惯)
((ClassPathXmlApplicationContext) context).close();
}
}
“`
运行 MainApplicationXml
,你会看到 Spring 容器启动,创建了 UserRepository
和 UserService
的实例,并将前者注入到后者,最后成功调用了方法。
5.2 Annotation (注解) 配置示例
Spring 提供了许多注解来简化配置。首先,我们需要在类上标记它们的角色:
“`java
// UserRepository.java
import org.springframework.stereotype.Repository;
@Repository // 标记这是一个数据访问组件
public class UserRepository {
public void saveUser(User user) {
System.out.println(“Saving user: ” + user.getName() + ” via Repository (Annotation)”);
}
}
// UserService.java
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service // 标记这是一个业务逻辑组件
public class UserService {
private final UserRepository userRepository;
// 使用 @Autowired 注解进行构造函数注入
// 如果只有一个构造函数,@Autowired 可以省略
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
System.out.println("UserService created with UserRepository (Annotation).");
}
public void registerUser(User user) {
System.out.println("Registering user: " + user.getName() + " via UserService (Annotation)");
userRepository.saveUser(user);
}
}
“`
@Repository
,@Service
,@Component
,@Controller
等是 Spring 的stereotype (刻板印象) 注解,用于标记不同层次的组件。被这些注解标记的类,在Spring进行组件扫描时会被自动识别为 Bean。@Component
是通用的,其他是其特化,提供了更明确的角色语义。@Autowired
: 用于自动注入依赖。Spring 会在容器中查找类型匹配的 Bean,并将其注入到被标记的位置(构造函数参数、Setter 方法、字段)。
要让 Spring 容器识别这些注解,我们需要配置组件扫描 (Component Scanning)。这可以通过 XML 或 JavaConfig 实现。
使用 XML 开启注解和组件扫描:
在 applicationContext.xml
中添加:
“`xml
<!-- 开启注解驱动 -->
<context:annotation-config/>
<!-- 扫描指定包下的类,将带有 @Component, @Service, @Repository, @Controller 等注解的类注册为 Bean -->
<context:component-scan base-package="com.example"/>
“`
<context:annotation-config/>
: 启用 Spring 对常用注解(如@Autowired
,@Value
,@PostConstruct
等)的处理。<context:component-scan base-package="com.example"/>
: 告诉 Spring 扫描com.example
包及其子包,查找带有组件注解的类,并将它们注册为 Bean。
使用方法与 XML 配置类似,仍然是加载这个 XML 文件:
“`java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.example.UserService;
import com.example.User;
public class MainApplicationAnnotationXml {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext-annotation.xml”); // 假设保存为不同的文件名
System.out.println(“Spring container started (Annotation/XML).”);
UserService userService = context.getBean(UserService.class); // 可以根据类型获取 Bean
System.out.println("UserService bean obtained.");
User newUser = new User("Charlie");
userService.registerUser(newUser);
((ClassPathXmlApplicationContext) context).close();
}
}
“`
使用注意事项: @Autowired
默认是按类型查找依赖。如果同一类型的 Bean 有多个,可能会报错或需要结合 @Qualifier
注解指定 Bean 的名称。
5.3 Java Code (JavaConfig) 配置示例
JavaConfig 是通过编写 Java 类来定义配置,通常比 XML 更灵活,更类型安全,且更容易重构。
创建一个配置类:
“`java
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.UserRepository;
import com.example.UserService;
@Configuration // 标记这是一个配置类
public class AppConfig {
@Bean // 标记这个方法返回的对象是一个 Bean
public UserRepository userRepository() {
System.out.println("Creating UserRepository Bean via JavaConfig.");
return new UserRepository(); // 方法名 userRepository 默认就是 Bean 的名称
}
@Bean // 标记这个方法返回的对象是一个 Bean
public UserService userService() {
System.out.println("Creating UserService Bean via JavaConfig.");
// 在 @Configuration 类中,调用其他 @Bean 方法可以直接获取对应的 Bean 引用
UserService userService = new UserService(userRepository());
return userService;
}
}
“`
@Configuration
: 标记一个类作为 Spring 配置类。@Bean
: 标记一个方法,该方法的返回值将被注册为 Spring Bean。方法名默认就是 Bean 的名称。在@Configuration
类中,调用其他@Bean
方法时,Spring 会拦截这个调用,并返回容器中已经存在的相应 Bean 实例,而不是再次执行方法创建新对象(这是 Spring CGLIB 代理的特性,确保@Configuration
类中的 Bean 方法调用是获取 Bean 引用的方式)。
如何使用 JavaConfig 启动容器并获取 Bean:
“`java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.example.UserService;
import com.example.User;
import com.example.config.AppConfig; // 导入配置类
public class MainApplicationJavaConfig {
public static void main(String[] args) {
// 1. 创建 Spring 容器,加载 JavaConfig 配置类
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(“Spring container started (JavaConfig).”);
// 2. 从容器中获取 Bean
UserService userService = context.getBean(UserService.class); // 通常根据类型获取
System.out.println("UserService bean obtained.");
// 3. 使用获取到的 Bean
User newUser = new User("David");
userService.registerUser(newUser);
// 4. 关闭容器
((AnnotationConfigApplicationContext) context).close();
}
}
“`
JavaConfig + 注解的混合使用: 实际开发中,最常用的是 JavaConfig 结合注解。@Configuration
类负责开启组件扫描、配置第三方库的 Bean 等,而业务类则通过注解 (@Service
, @Repository
, @Autowired
) 自动注册为 Bean 并处理依赖注入。
“`java
// AppConfig.java (用于开启组件扫描)
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,比如第三方库的对象
}
// UserRepository.java (同Annotation示例)
@Repository
public class UserRepository { … }
// UserService.java (同Annotation示例)
@Service
public class UserService {
@Autowired // 或构造函数注入
private UserRepository userRepository;
…
}
// MainApplicationCombined.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.example.UserService;
import com.example.User;
import com.example.config.AppConfig;
public class MainApplicationCombined {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(“Spring container started (Combined).”);
UserService userService = context.getBean(UserService.class);
System.out.println("UserService bean obtained.");
User newUser = new User("Eve");
userService.registerUser(newUser);
((AnnotationConfigApplicationContext) context).close();
}
}
“`
这种混合方式是现代 Spring Boot 应用的基础,理解了它,你就离 Spring Boot 不远了。
第六部分:Spring 的其他核心特性(简单了解)
除了 IoC 和 DI,Spring Framework 还提供了很多强大的功能。作为零基础入门,你只需要知道它们的存在以及解决什么问题即可。
-
AOP (Aspect-Oriented Programming – 面向切面编程):
- 解决什么问题: 许多功能(如日志记录、事务管理、安全检查)会分散在应用程序的多个模块中,形成“横切关注点”(Cross-cutting Concerns)。将这些代码直接写在业务逻辑里会造成大量重复。
- AOP 如何解决: AOP 允许你将这些横切关注点从业务逻辑中分离出来,封装到“切面”(Aspect)中。然后通过配置,让 Spring 在特定的执行点(Join Point,如方法调用前、调用后)自动执行这些切面中的代码,而无需修改业务逻辑本身。
- 类比: 想象你在看电影,AOP 就像是电影中的背景音乐、字幕或特效,它们不是故事情节本身,但会贯穿整个电影,并且可以在不修改电影内容的情况下被添加或修改。
-
数据访问支持 (Data Access):
- Spring 提供了强大的数据访问集成层,简化了数据库操作。它支持各种数据访问技术,如 JDBC、ORM 框架(Hibernate, JPA)、NoSQL 数据库等。
- Spring 的数据访问模块提供了统一的异常处理机制,将不同数据访问技术的特定异常转换为 Spring 统一的异常体系,方便处理。
-
事务管理 (Transaction Management):
- Spring 提供了声明式事务管理的功能。你只需要通过注解 (
@Transactional
) 或 XML 配置,就可以控制方法是否应该在一个事务中运行,而无需手动编写事务的开启、提交、回滚代码。这极大地简化了事务的管理。
- Spring 提供了声明式事务管理的功能。你只需要通过注解 (
-
Web 开发支持 (Spring MVC):
- Spring MVC 是 Spring 官方提供的 Web 框架,基于 MVC (Model-View-Controller) 模式。它提供了DispatcherServlet、Controller、View Resolver 等组件,帮助你构建灵活、解耦的 Web 应用程序。
-
Spring Boot:
- 划重点: 对于初学者来说,理解了核心 Spring(IoC/DI)后,下一步强烈推荐学习 Spring Boot。
- Spring Boot 是什么: Spring Boot 并不是一个全新的框架,它是基于 Spring Framework 之上的一个项目,旨在简化 Spring 应用的搭建和开发过程。
- Spring Boot 如何简化:
- 约定大于配置: Spring Boot 提供了很多默认配置,减少了开发者手动配置的工作量。
- 内嵌服务器: 可以直接打包成可执行的 JAR 文件,内嵌 Tomcat, Jetty 或 Undertow 等服务器,无需单独部署 WAR 包。
- 起步依赖 (Starter Dependencies): 提供了很多预定义的依赖组合,简化了 Maven/Gradle 配置。
- 自动化配置 (Auto-configuration): 根据你添加的依赖,自动配置 Spring 组件。
简单来说,Spring Boot 让你可以“just run”你的 Spring 应用,大大提高了开发效率。
第七部分:搭建你的第一个 Spring 项目
理论知识讲了不少,现在让我们动手实践,搭建一个最简单的 Spring 项目(使用 JavaConfig + 注解的方式,这是现代开发的常用组合)。
前提条件:
- 安装 JDK (Java Development Kit) 8 或更高版本。
- 安装一个集成开发环境 (IDE),如 IntelliJ IDEA, Eclipse 或 VS Code。推荐使用 IntelliJ IDEA (社区版或旗舰版)。
- 安装 Maven 或 Gradle 构建工具。大多数现代项目使用 Maven 或 Gradle 管理依赖。这里以 Maven 为例。
步骤:
- 创建一个 Maven 项目:
在 IDE 中创建一个新的 Maven 项目。选择maven-archetype-quickstart
或直接创建一个空的 Maven 项目。 -
添加 Spring 依赖:
打开项目的pom.xml
文件,添加 Spring Core 和 Spring Context 的依赖。“`xml
4.0.0 <groupId>com.yourcompany</groupId> <artifactId>spring-zero-basis</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <!-- 定义 Spring 版本,这里使用较新的版本 --> <spring.version>6.1.0</spring.version> </properties> <dependencies> <!-- Spring Core 依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Context 依赖 (包含 IoC 容器) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Beans 依赖 (通常会被 spring-context 传递依赖引入,但显式写出来也无妨) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <!-- JUnit 依赖,可选,用于测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
``
pom.xml`,IDE 或 Maven 会自动下载所需的依赖。
保存 -
创建业务类 (
User
,UserRepository
,UserService
):
在src/main/java
目录下创建包结构 (例如com.example
),并在其中创建User.java
,UserRepository.java
,UserService.java
,代码如 5.2 注解配置示例 中所示(带有@Repository
,@Service
,@Autowired
注解)。 -
创建 Java 配置类 (
AppConfig
):
在src/main/java
目录下创建包结构 (例如com.example.config
),并在其中创建AppConfig.java
,代码如 5.3 JavaConfig + 注解混合使用 中所示(带有@Configuration
,@ComponentScan
)。 -
创建主应用程序类 (
MainApplicationCombined
):
在src/main/java
目录下创建包结构 (例如com.example
),并在其中创建MainApplicationCombined.java
,代码如 5.3 JavaConfig + 注解混合使用 中所示。 -
运行应用程序:
找到MainApplicationCombined.java
文件中的main
方法,右键点击运行。
如果一切顺利,你应该能在控制台看到类似以下的输出:
Spring container started (Combined).
Creating UserRepository Bean via Annotation. <- @Repository 注解扫描并创建
Creating UserService Bean via Annotation. <- @Service 注解扫描并创建
UserService created with UserRepository (Annotation). <- UserService 构造函数执行,DI 完成
UserService bean obtained.
Registering user: Eve via UserService (Annotation)
Saving user: Eve via Repository (Annotation)
恭喜你!你已经成功搭建并运行了第一个使用 Spring IoC/DI 的应用程序。通过 context.getBean(UserService.class)
,你从 Spring 容器中获取到了 UserService
的实例。而这个 UserService
实例所依赖的 UserRepository
实例,是 Spring 容器自动创建并注入给它的,你没有写一行 new UserRepository()
的代码!这就是 IoC 和 DI 的魔力!
第八部分:Spring 的优势总结
通过前面的学习,我们可以总结出 Spring 框架的主要优势:
- 降低耦合度: IoC/DI 机制使得组件之间的依赖关系更加松散,提高了系统的灵活性和可维护性。
- 提高可测试性: 依赖注入使得单元测试时替换 Mock 对象变得容易,提高了测试效率和质量。
- 简化开发: Spring 提供了大量的基础设施支持(如事务管理、数据访问模板),减少了重复的样板代码。
- 强大的生态系统: Spring 不仅仅是 Core 模块,还有 Spring MVC、Spring Data、Spring Security、Spring Cloud 等众多模块,覆盖了企业应用开发的方方面面,并且拥有庞大的社区支持。
- 非侵入性: 大部分情况下,Spring 不强制你的业务类实现特定的接口或继承特定的基类,使得你的业务代码更加纯净。
第九部分:下一步学习建议
掌握了 IoC 和 DI,你已经迈出了坚实的第一步。Spring Framework 博大精深,接下来的学习路径可以考虑:
- 深入理解 Bean 的生命周期、作用域 (Scope): Bean 在容器中的创建、初始化、销毁过程,以及单例 (Singleton)、原型 (Prototype) 等不同的作用域。
- 学习 Spring Boot: 如前所述,Spring Boot 是现代 Spring 开发的主流,它能极大地简化你的开发流程。学习如何使用 Spring Boot 快速构建 Web 应用、RESTful API、微服务等。
- 学习 Spring 其他常用模块:
- Spring Data: 简化数据库访问(JPA, JDBC, MongoDB 等)。
- Spring MVC: 如果你想开发传统的 Web 应用或 RESTful API。
- Spring Security: 强大的安全框架,处理认证和授权。
- Spring AOP: 深入理解面向切面编程。
- 阅读官方文档: Spring 官方文档是最好的学习资源,虽然初看可能觉得复杂,但随着你实践的深入,回过头来看会有新的理解。
- 多动手实践: 理论结合实践是最好的学习方式。尝试用 Spring 重构你以前写的小项目,或者跟着教程做一些更复杂的例子。
结语
从零开始学习 Spring Framework 可能会让你感到有些挑战,但只要抓住 IoC 和 DI 这两个核心概念,并结合实际动手练习,你会发现 Spring 并没有想象中那么可怕。它提供了一套优雅的方式来构建健壮、可维护的 Java 应用,是每个 Java 后端开发者都值得投入时间学习的技能。
希望这篇教程能为你打开 Spring 世界的大门。祝你在 Spring 的学习旅程中一切顺利,享受编程的乐趣!
加油!