Spring 入门详解:从 XML 配置到注解开发,彻底理解 IoC、DI、Bean 和 Spring 容器
一、Spring 是什么?
Spring 是 Java 生态中非常重要的一个开发框架。
很多初学者刚接触 Spring 时,会觉得它很抽象:
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
Student student = applicationContext.getBean("student", Student.class);
看起来好像只是换了一种方式创建对象。
原来我们写 Java 是这样:
Student student = new Student();
现在用了 Spring 之后变成:
Student student = applicationContext.getBean("student", Student.class);
那么问题来了:
为什么不直接 new?为什么要从 Spring 容器里面拿?Spring 到底帮我们做了什么?
一句话概括:
Spring 是一个帮助 Java 程序管理对象、组织对象关系、简化企业级开发的大型框架。
更具体地说,Spring 最核心的能力是:
1. 帮你创建对象
2. 帮你管理对象
3. 帮你维护对象之间的依赖关系
4. 帮你统一处理事务、日志、权限等通用逻辑
5. 帮你更方便地开发 Web 后端项目
对于初学者来说,最先要理解的是 Spring 的两个核心概念:
IoC:控制反转
DI:依赖注入
它们是 Spring 的地基。
二、没有 Spring 时,对象是怎么创建的?
假设我们有一个 Student 类:
package pojo;
public class Student {
private String name;
private int age;
public Student() {
System.out.println("Student 对象被创建了");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("setName 方法被调用");
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
System.out.println("setAge 方法被调用");
this.age = age;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
传统 Java 写法是:
public class TestStudent {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setAge(18);
System.out.println(student);
}
}
这段代码的逻辑很直接:
1. 程序员自己 new Student()
2. 程序员自己调用 setName()
3. 程序员自己调用 setAge()
4. 程序员自己管理这个对象
也就是说,对象创建权在程序员手里。
项目小的时候这样写没问题。
但是项目变大以后,对象和对象之间会产生大量依赖。
比如一个学生管理系统中,可能有:
StudentController
StudentService
StudentDao
DataSource
它们之间的关系可能是:
StudentController 需要 StudentService
StudentService 需要 StudentDao
StudentDao 需要 DataSource
传统写法可能变成这样:
DataSource dataSource = new DataSource();
StudentDao studentDao = new StudentDao();
studentDao.setDataSource(dataSource);
StudentService studentService = new StudentService();
studentService.setStudentDao(studentDao);
StudentController studentController = new StudentController();
studentController.setStudentService(studentService);
对象越来越多时,代码会变得非常混乱。
这就是 Spring 要解决的问题。
三、Spring 的核心思想:把对象交给容器管理
Spring 的核心不是让代码变“玄学”,而是把原来程序员手动管理对象的过程,交给 Spring 容器。
原来:
Student student = new Student();
现在:
Student student = applicationContext.getBean("student", Student.class);
表面上只是写法变了,底层思想却变了。
原来的对象创建流程是:
程序员主动 new 对象
用了 Spring 之后变成:
Spring 容器创建对象
程序员从 Spring 容器中获取对象
这就是 Spring 的核心思想:
对象的创建权、管理权从程序员手中转移给 Spring 容器。
这个思想就叫 IoC。
四、IoC 是什么?
IoC 的全称是:
Inversion of Control
翻译成中文叫:
控制反转
这个名字很抽象,但意思其实很简单。
1. 什么叫控制?
这里的“控制”,主要指的是:
对象的创建权
对象的管理权
对象之间关系的装配权
比如:
Student student = new Student();
这就是程序员在控制对象的创建。
2. 什么叫反转?
原来是程序员控制对象:
程序员 → 创建对象
现在是 Spring 控制对象:
Spring 容器 → 创建对象
对象控制权发生了转移,所以叫“控制反转”。
3. IoC 的本质
IoC 的本质可以用一句话理解:
不再由程序员主动 new 对象,而是由 Spring 容器统一创建和管理对象。
五、DI 是什么?
DI 的全称是:
Dependency Injection
翻译成中文叫:
依赖注入
DI 是 IoC 的具体实现方式。
比如 StudentService 需要 StudentDao。
传统写法:
public class StudentService {
private StudentDao studentDao = new StudentDao();
}
这表示 StudentService 自己创建了 StudentDao。
用了 Spring 之后,一般会这样写:
public class StudentService {
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
}
然后让 Spring 把 StudentDao 注入进来。
XML 配置:
<bean id="studentDao" class="dao.StudentDao"/>
<bean id="studentService" class="service.StudentService">
<property name="studentDao" ref="studentDao"/>
</bean>
这里的:
<property name="studentDao" ref="studentDao"/>
意思是:
把 id 为 studentDao 的 Bean 注入到 studentService 对象的 studentDao 属性中
这就叫依赖注入。
六、Bean 是什么?
在 Spring 中,经常会看到一个词:
Bean
很多人刚开始会觉得 Bean 很高级,其实可以先粗暴理解为:
被 Spring 容器管理的 Java 对象,就叫 Bean。
例如普通对象:
Student student = new Student();
这是程序员自己创建的对象。
Spring Bean:
<bean id="student" class="pojo.Student"/>
这是交给 Spring 创建和管理的对象。
所以:
普通对象:程序员自己 new 出来的对象
Spring Bean:Spring 容器帮你创建并管理的对象
七、XML 方式使用 Spring
早期 Spring 常用 XML 配置管理 Bean。
假设我们有一个 Student 类:
package pojo;
public class Student {
private String name;
private int age;
public Student() {
System.out.println("Student 无参构造方法被调用");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("setName 被调用");
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
System.out.println("setAge 被调用");
this.age = age;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
然后在 src/main/resources 目录下创建 application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="pojo.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</beans>
这个 XML 的意思是:
让 Spring 创建一个 pojo.Student 对象;
这个对象在 Spring 容器中的名字叫 student;
给它的 name 属性赋值为 张三;
给它的 age 属性赋值为 18。
八、XML 中的 <bean> 是什么意思?
核心语法如下:
<bean id="student" class="pojo.Student">
</bean>
其中:
id="student"
表示这个 Bean 在 Spring 容器中的名字。
class="pojo.Student"
表示 Spring 要创建哪个类的对象。
注意:class 里面要写完整类名,也就是:
包名.类名
例如:
pojo.Student
service.StudentService
dao.StudentDao
controller.StudentController
九、XML 中的 <property> 是什么意思?
例如:
<property name="name" value="张三"/>
<property name="age" value="18"/>
这表示给对象属性赋值。
它底层大致等价于:
student.setName("张三");
student.setAge(18);
所以 XML 中的:
<property name="name" value="张三"/>
不是直接访问 name 属性,而是调用对应的 setter 方法:
setName("张三");
因此,如果使用 property 注入,类中通常需要提供对应的 setter 方法。
十、value 和 ref 的区别
Spring XML 里面最常见的两个属性是:
value
ref
它们的区别非常重要。
1. value:注入普通值
<property name="name" value="张三"/>
<property name="age" value="18"/>
value 用来注入普通数据,例如:
字符串
数字
布尔值
2. ref:注入另一个 Bean
假设有两个类:
package dao;
public class StudentDao {
public void save() {
System.out.println("保存学生信息");
}
}
package service;
import dao.StudentDao;
public class StudentService {
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
public void addStudent() {
studentDao.save();
}
}
XML 配置:
<bean id="studentDao" class="dao.StudentDao"/>
<bean id="studentService" class="service.StudentService">
<property name="studentDao" ref="studentDao"/>
</bean>
这里:
ref="studentDao"
意思是:
把 id 为 studentDao 的 Bean 注入到 studentService 中
它不是字符串 "studentDao",而是引用另一个对象。
所以:
value:注入普通值
ref:注入另一个 Bean 对象
十一、ApplicationContext 是什么?
测试代码通常这样写:
package tool;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Student;
public class TestStudent {
public static void main(String[] args) {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
Student student = applicationContext.getBean("student", Student.class);
System.out.println(student);
}
}
其中:
ApplicationContext applicationContext
表示 Spring 容器。
可以把 ApplicationContext 理解成:
Spring 对象仓库
Spring 容器
Bean 管理中心
它里面保存着 Spring 创建出来的对象。
十二、new ClassPathXmlApplicationContext("application.xml") 是什么意思?
这行代码:
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
表示:
从 classpath 路径下加载 application.xml 配置文件;
根据 XML 配置创建 Spring 容器;
解析 <bean> 标签;
实例化配置中的 Bean;
把这些 Bean 放进 Spring 容器中。
其中 ClassPathXmlApplicationContext 可以拆开理解:
ClassPath:从 classpath 中找配置文件
Xml:配置文件是 XML 格式
ApplicationContext:Spring 容器
所以整体意思就是:
根据 classpath 下的 XML 配置文件创建 Spring 容器。
十三、classpath 是什么?
classpath 是 Java 程序运行时查找类和资源文件的路径。
在 Maven 项目中,一般有两个重要目录:
src/main/java
src/main/resources
其中:
src/main/java:放 Java 源码
src/main/resources:放配置文件
如果 application.xml 放在:
src/main/resources/application.xml
那么就可以这样读取:
new ClassPathXmlApplicationContext("application.xml");
如果放在:
src/main/resources/spring/application.xml
那么应该这样写:
new ClassPathXmlApplicationContext("spring/application.xml");
十四、getBean 是什么?
这行代码:
Student student = applicationContext.getBean("student", Student.class);
意思是:
从 Spring 容器中取出名字叫 student 的 Bean;
并且要求它的类型是 Student。
常见写法有三种。
1. 根据 id 获取 Bean
Student student = applicationContext.getBean("student", Student.class);
2. 根据类型获取 Bean
Student student = applicationContext.getBean(Student.class);
这种方式要求容器中只能有一个 Student 类型的 Bean。
如果有两个:
<bean id="student1" class="pojo.Student"/>
<bean id="student2" class="pojo.Student"/>
再写:
applicationContext.getBean(Student.class);
Spring 就不知道你要哪个,会报错。
3. 先获取 Object,再强制转换
Student student = (Student) applicationContext.getBean("student");
这种写法比较老,不如下面这种清晰:
Student student = applicationContext.getBean("student", Student.class);
十五、XML 配置方式有什么问题?
XML 方式虽然直观,但有一个明显问题:
如果每个类都要在 XML 里面手动注册,项目大了以后会非常麻烦。
例如项目中有:
StudentController
TeacherController
CourseController
StudentService
TeacherService
CourseService
StudentDao
TeacherDao
CourseDao
如果都用 XML,就要写:
<bean id="studentController" class="controller.StudentController"/>
<bean id="teacherController" class="controller.TeacherController"/>
<bean id="courseController" class="controller.CourseController"/>
<bean id="studentService" class="service.StudentService"/>
<bean id="teacherService" class="service.TeacherService"/>
<bean id="courseService" class="service.CourseService"/>
<bean id="studentDao" class="dao.StudentDao"/>
<bean id="teacherDao" class="dao.TeacherDao"/>
<bean id="courseDao" class="dao.CourseDao"/>
如果项目中有几百个、几千个类,XML 配置会变得非常臃肿。
所以现代 Spring 更常用注解开发。
十六、注解方式管理 Bean
注解方式的核心思想是:
在类上加注解,让 Spring 自动扫描并注册 Bean。
以前 XML 写法:
<bean id="studentService" class="service.StudentService"/>
现在注解写法:
@Service
public class StudentService {
}
然后开启组件扫描:
<context:component-scan base-package="service"/>
Spring 就会自动扫描 service 包,发现带有 @Service 的类,然后自动创建对象并放入容器。
十七、Spring 常见组件注解
Spring 中常见的组件注解有:
@Component
@Service
@Repository
@Controller
它们的共同作用是:
把当前类交给 Spring 容器管理,让它成为一个 Bean。
但是它们的语义不同。
| 注解 | 常用位置 | 作用 |
|---|---|---|
@Component | 普通组件 | 通用组件,不明确属于哪一层 |
@Service | 业务层 | 处理业务逻辑 |
@Repository | 数据访问层 | 操作数据库、持久化数据 |
@Controller | 控制层 | 接收 Web 请求,调用业务层 |
一句话记忆:
@Controller:接请求
@Service:写业务
@Repository:查数据库
@Component:普通组件
十八、@Component 是什么?
@Component 是最通用的组件注解。
例如:
package util;
import org.springframework.stereotype.Component;
@Component
public class TimeUtil {
public String getNow() {
return "2026-06-17";
}
}
意思是:
这个类是一个普通组件,请 Spring 管理它。
如果某个类不明显属于 Controller、Service、Repository,可以使用 @Component。
十九、@Service 是什么?
@Service 一般用于业务层。
例如:
package service;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
public void addStudent() {
System.out.println("添加学生业务逻辑");
}
}
业务层负责处理系统规则,例如:
判断学生信息是否合法
判断课程是否已满
判断用户是否有权限
处理订单支付流程
处理请假审批逻辑
所以 @Service 一般写在 Service 类上。
二十、@Repository 是什么?
@Repository 一般用于数据访问层。
例如:
package dao;
import org.springframework.stereotype.Repository;
@Repository
public class StudentDao {
public void save() {
System.out.println("保存学生到数据库");
}
}
数据访问层主要负责:
执行 SQL
查询数据库
保存数据
删除数据
修改数据
在 MyBatis 项目中,也常见 @Mapper,它和 @Repository 都经常出现在数据访问层中。
二十一、@Controller 是什么?
@Controller 一般用于 Web 控制层。
例如:
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class StudentController {
@RequestMapping("/student/list")
public String list() {
return "student-list";
}
}
Controller 主要负责:
接收用户请求
调用 Service 处理业务
返回页面或数据
如果是前后端分离项目,更常用:
@RestController
@RestController 可以简单理解为:
@Controller + @ResponseBody
它通常直接返回 JSON 数据或字符串。
二十二、典型三层架构
Spring 项目中经常采用三层结构:
Controller 层
Service 层
Repository / Dao 层
它们之间的调用关系如下:
flowchart TD
A[浏览器 / 前端请求] --> B[Controller 控制层]
B --> C[Service 业务层]
C --> D[Repository / Dao 数据访问层]
D --> E[(数据库)]
E --> D
D --> C
C --> B
B --> A
举个学生管理系统的例子:
用户点击添加学生
↓
StudentController 接收请求
↓
StudentService 判断学生信息是否合法
↓
StudentDao 执行 SQL 保存学生
↓
数据库保存成功
二十三、@Autowired 是什么?
有了注解之后,对象之间的依赖也可以让 Spring 自动注入。
例如:
package dao;
import org.springframework.stereotype.Repository;
@Repository
public class StudentDao {
public void save() {
System.out.println("保存学生信息");
}
}
package service;
import dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
@Autowired
private StudentDao studentDao;
public void addStudent() {
studentDao.save();
}
}
这里:
@Autowired
private StudentDao studentDao;
意思是:
Spring 自动从容器中找到 StudentDao 类型的 Bean;
然后注入到 studentDao 属性中。
这样就不用手动写:
StudentDao studentDao = new StudentDao();
也不用在 XML 中写:
<property name="studentDao" ref="studentDao"/>
二十四、完整注解开发示例
项目结构:
src/main/java
├─ controller
│ └─ StudentController.java
├─ service
│ └─ StudentService.java
├─ dao
│ └─ StudentDao.java
└─ tool
└─ TestSpring.java
src/main/resources
└─ application.xml
1. StudentDao.java
package dao;
import org.springframework.stereotype.Repository;
@Repository
public class StudentDao {
public void save() {
System.out.println("执行 SQL:保存学生信息");
}
}
2. StudentService.java
package service;
import dao.StudentDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
@Autowired
private StudentDao studentDao;
public void addStudent() {
System.out.println("处理添加学生的业务逻辑");
studentDao.save();
}
}
3. StudentController.java
package controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import service.StudentService;
@Controller
public class StudentController {
@Autowired
private StudentService studentService;
public void addStudent() {
System.out.println("Controller 接收到添加学生请求");
studentService.addStudent();
}
}
4. application.xml
使用注解扫描时,需要引入 context 命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="controller"/>
<context:component-scan base-package="service"/>
<context:component-scan base-package="dao"/>
</beans>
也可以把这些包放到统一父包下,例如:
com.example.controller
com.example.service
com.example.dao
然后只写:
<context:component-scan base-package="com.example"/>
5. TestSpring.java
package tool;
import controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
StudentController controller =
context.getBean(StudentController.class);
controller.addStudent();
}
}
运行结果:
Controller 接收到添加学生请求
处理添加学生的业务逻辑
执行 SQL:保存学生信息
这就说明:
StudentController 被 Spring 创建了
StudentService 被 Spring 创建了
StudentDao 被 Spring 创建了
Spring 自动把 Service 注入到了 Controller
Spring 自动把 Dao 注入到了 Service
这就是 Spring 的依赖注入。
二十五、XML 和注解方式对比
| 方式 | 特点 | 适合场景 |
|---|---|---|
| XML 配置 | 直观,但配置繁琐 | 学习 Spring 原理、老项目维护 |
| 注解配置 | 简洁,开发效率高 | 现代 Spring 项目 |
| Spring Boot 自动配置 | 更简洁,开箱即用 | Web 后端、企业项目、微服务 |
XML 方式:
<bean id="studentService" class="service.StudentService"/>
注解方式:
@Service
public class StudentService {
}
Spring Boot 方式:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Spring 的发展趋势就是:
从 XML 配置
到注解配置
再到 Spring Boot 自动配置
二十六、Spring Boot 和 Spring 的关系
很多人学到这里会问:
Spring 和 Spring Boot 是一个东西吗?
不是一个东西,但关系很密切。
可以这样理解:
Spring 是底层框架
Spring Boot 是对 Spring 的进一步封装和简化
Spring 提供了:
IoC
DI
AOP
事务管理
Bean 管理
Web 支持
Spring Boot 在 Spring 的基础上提供了:
自动配置
内置服务器
快速启动
简化依赖管理
减少 XML 配置
所以:
Spring 是地基
Spring Boot 是更好用的脚手架
二十七、Spring 的执行流程总结
以 XML 方式为例:
ApplicationContext context =
new ClassPathXmlApplicationContext("application.xml");
这行代码执行后,Spring 大致会做这些事情:
1. 找到 application.xml
2. 读取 XML 配置
3. 解析 <bean> 标签
4. 根据 class 属性找到对应的 Java 类
5. 通过反射创建对象
6. 根据 property 标签注入属性
7. 把创建好的对象放入 Spring 容器
8. 程序通过 getBean 获取对象
流程图如下:
flowchart TD
A[启动程序] --> B[加载 application.xml]
B --> C[解析 bean 配置]
C --> D[通过反射创建对象]
D --> E[注入属性或依赖]
E --> F[把对象放入 Spring 容器]
F --> G[getBean 获取 Bean]
G --> H[调用对象方法]
二十八、Spring 的本质到底是什么?
学完上面的内容,可以发现 Spring 并不神秘。
Spring 的底层思想就是:
1. 把对象交给容器管理
2. 把对象之间的依赖关系交给容器装配
3. 通过配置或注解告诉 Spring 哪些类需要被管理
4. 程序运行时从容器中获取对象,或者让 Spring 自动注入对象
一句话总结:
Spring 的本质是一个管理 Java 对象及其依赖关系的容器框架。
二十九、初学 Spring 最容易混淆的几个点
1. Bean 不是特殊语法
Bean 本质上就是 Java 对象,只不过它被 Spring 管理了。
普通对象:自己 new
Bean:Spring 创建和管理
2. IoC 不是某个类
IoC 是一种思想,不是某个具体类。
对象创建权从程序员手里转移给 Spring 容器
3. DI 是 IoC 的实现方式
DI 负责把一个对象需要的依赖注入进去。
StudentService 需要 StudentDao
Spring 自动把 StudentDao 注入给 StudentService
4. getBean 不是正式开发的主要写法
学习阶段经常用:
applicationContext.getBean(...)
但正式开发中更多使用:
@Autowired
private StudentService studentService;
或者构造器注入。
5. 注解不是魔法
例如:
@Service
public class StudentService {
}
这不是魔法,它的本质是:
Spring 扫描到这个类
发现它有 @Service
于是创建它的对象
并放进 Spring 容器
三十、总结
Spring 是 Java 后端开发中非常重要的框架。
它最核心的内容包括:
IoC:控制反转,把对象创建权交给 Spring
DI:依赖注入,让 Spring 自动装配对象之间的关系
Bean:被 Spring 管理的 Java 对象
ApplicationContext:Spring 容器
XML 配置:早期手动注册 Bean 的方式
注解开发:现代 Spring 常用方式
Spring Boot:进一步简化 Spring 开发
最开始学习 Spring,可以先记住这几句话:
1. Spring 是一个管理对象的容器框架。
2. 被 Spring 管理的对象叫 Bean。
3. IoC 表示对象不再由程序员自己 new,而是交给 Spring 创建。
4. DI 表示 Spring 会把一个对象需要的其他对象自动注入进去。
5. XML 可以注册 Bean,但项目大了会很麻烦。
6. @Component、@Service、@Repository、@Controller 可以让 Spring 自动扫描并管理类。
7. 正式开发中,更多使用注解和 Spring Boot,而不是大量手写 XML。
最后用一句话收尾:
Spring 的价值不是让代码看起来更复杂,而是让大型 Java 项目中的对象创建、依赖关系、分层结构和通用功能变得更加清晰、统一、可维护。
更多推荐
所有评论(0)