一、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 方法。


十、valueref 的区别

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 项目中的对象创建、依赖关系、分层结构和通用功能变得更加清晰、统一、可维护。

更多推荐