Spring IoC 容器:Bean 管理与配置文件

一、Spring 容器的核心概念

Spring 容器的本质是一个IoC(控制反转)容器,它颠覆了传统 “开发者手动创建对象、管理依赖” 的模式,改为由容器统一管理对象的生命周期和依赖关系(这个过程也叫 DI 依赖注入)。

Spring 容器就是 Spring 框架的 “对象管家”,它帮你自动创建、管理、组装和销毁 Java 对象(也就是 Spring 里的 Bean),你不用手动 new 对象、处理对象依赖,只需要告诉它规则就行。

简单打个比方:

把 Spring 容器比作 家里的 “智能收纳柜”:

你(开发者)只需要把 “物品清单”(注解 / 配置文件)交给收纳柜,告诉它要收纳哪些东西(Bean)、哪些东西要放在一起(依赖关系)。

收纳柜会自动把物品分类放好(创建并注册 Bean),当你需要用某个物品时(获取 Bean),它直接递到你手上,不用你自己翻找;

当你不需要这些物品了(容器关闭),收纳柜还会自动把它们整理好收起来(销毁 Bean),全程不用你操心。

1. 容器的核心接口

Spring 容器有两个核心层级接口,你可以把它们理解为 “容器的不同版本”:

BeanFactory:最基础的容器接口,提供了 IoC 容器的最核心功能(如获取 Bean、Bean 生命周期管理),是 Spring 容器的底层实现,特点是懒加载

补:Spring 中的懒加载,是容器创建 Bean 时的一种加载策略,简单说就是 “什么时候用,什么时候才创建” 即 容器启动不创建,首次使用才创建,和默认的 “容器一启动就创建所有 Bean” 。

ApplicationContext:BeanFactory 的子接口,是更高级的容器实现,在 BeanFactory 基础上扩展了更多功能(如国际化、事件发布、资源加载),特点是饿汉式加载(容器启动时就创建所有单例 Bean),也是实际开发中最常用的容器。

常见的 ApplicationContext 实现类:

ClassPathXmlApplicationContext:从类路径加载 XML 配置文件创建容器。

AnnotationConfigApplicationContext:从注解配置类(@Configuration)创建容器(主流方式)。

FileSystemXmlApplicationContext:从文件系统路径加载 XML 配置文件。

二、Spring 容器的核心功能

1. Bean 的创建与注册

容器会根据配置(XML / 注解 / Java 配置)创建 Bean,并将其注册到容器中。

示例:注解方式创建容器并注册 Bean

// 1. 定义一个简单的 Bean
@Component // 标记为 Spring 管理的 Bean
public class UserService {
    public void sayHello() {
        System.out.println("Hello from Spring Container!");
    }
}

// 2. 配置类(告诉容器扫描哪些包下的 Bean)
@Configuration
@ComponentScan("com.example.demo") // 扫描指定包下的@Component注解类
public class SpringConfig {
}

// 3. 启动容器并获取 Bean
public class Main {
    public static void main(String[] args) {
        // 创建 ApplicationContext 容器(核心步骤)
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        
        // 从容器中获取 Bean
        UserService userService = context.getBean(UserService.class);
        
        // 使用 Bean
        userService.sayHello(); // 输出:Hello from Spring Container!
    }
}
2. Bean 的生命周期管理

Spring 容器会全程管理 Bean 的生命周期,核心流程:

  1. 实例化 Bean(创建对象)
  2. 设置 Bean 的属性(依赖注入)
  3. 调用 BeanNameAware/ApplicationContextAware 等接口方法
  4. 调用 BeanPostProcessor 的前置处理方法
  5. 调用 InitializingBeanafterPropertiesSet() 或自定义初始化方法(@PostConstruct)
  6. 调用 BeanPostProcessor 的后置处理方法
  7. Bean 就绪可使用
  8. 容器关闭时,调用 DisposableBeandestroy() 或自定义销毁方法(@PreDestroy)
  9. 销毁 Bean。

示例:自定义 Bean 生命周期方法

@Component
public class OrderService {
    // 初始化方法(Bean 创建完成后执行)
    @PostConstruct
    public void init() {
        System.out.println("OrderService 初始化");
    }

    // 销毁方法(容器关闭时执行)
    @PreDestroy
    public void destroy() {
        System.out.println("OrderService 销毁");
    }
}
3. 依赖注入(DI)

容器会自动将 Bean 依赖的其他对象注入进来,无需手动 new,常见方式:

字段注入(最简洁,推荐简单场景):

@Component
public class UserController {
    @Autowired // 自动注入 UserService 实例
    private UserService userService;
}

构造器注入(推荐复杂场景,更符合依赖不可变原则):

@Component
public class UserController {
    private final UserService userService;
    
    // 容器自动调用构造器注入
    public UserController(UserService userService) {
        this.userService = userService;
    }
}

补:若用 Spring Boot 2.6+,建议配合 @RequiredArgsConstructor 替代纯字段注入(兼顾简洁和规范)开发常用的方式:

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor // Lombok注解,自动生成final字段的构造器
public class UserController {
    private final UserService userService; // final + 构造器注入,极简又规范
}

可选依赖 / 动态变更依赖(推荐:Setter 注入):

适用场景:

依赖可选存在(有则注入,无则用默认值),或运行中可能更换实现的场景。

例如:订单服务(OrderService)可选依赖短信通知(SmsService),没有则用邮件通知兜底;或需要动态切换支付渠道(AlipayService/WechatPayService)。

示例:

@Component
public class OrderService {
    private SmsService smsService; // 可选依赖,非final
    
    // Setter注入,支持可选依赖
    @Autowired(required = false) // required=false 表示可选
    public void setSmsService(SmsService smsService) {
        this.smsService = smsService;
    }
    
    // 业务方法:有短信服务则发通知,无则跳过
    public void createOrder(Order order) {
        // 业务逻辑...
        if (smsService != null) {
            smsService.sendOrderNotice(order);
        }
    }
}

补:Setter 方法可在运行时被调用,支持动态更换依赖实现(比如从支付宝切换到微信支付)。

多实现类的依赖注入(需配合限定符)

适用场景:

一个接口有多个实现类,需要指定具体注入哪个(比如 PaymentService 有 AlipayPayment、WechatPayment 两个实现)。

方案:

构造器 / 字段 / Setter 注入都可,关键是配合 @Qualifier(指定 Bean 名称)或 @Resource(按名称注入)。

示例(构造器 + @Qualiier):

// 接口
public interface PaymentService {
    void pay(Order order);
}

// 实现类1
@Component("alipay") // 指定Bean名称
public class AlipayPayment implements PaymentService {
    @Override
    public void pay(Order order) {
        System.out.println("支付宝支付");
    }
}

// 实现类2
@Component("wechat")
public class WechatPayment implements PaymentService {
    @Override
    public void pay(Order order) {
        System.out.println("微信支付");
    }
}

// 注入时指定实现类
@Component
public class OrderService {
    private final PaymentService paymentService;
    
    // 构造器注入 + @Qualifier 指定Bean名称
    public OrderService(@Qualifier("alipay") PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}
注入方式选择原则

优先选构造器注入:这是 Spring 官方推荐的方式,符合 “依赖注入” 的设计初衷,代码更健壮、易测试;

字段注入只用于简单场景:Controller 层可酌情使用,核心业务层尽量不用;

Setter注入仅用于可选依赖:不要把 Setter 注入当成 “替代构造器” 的方式,避免依赖可变带来的风险;

多实现类必加限定符:用 @Qualifier@Resource 明确指定 Bean,避免 “NoUniqueBeanDefinitionException” 异常。

4. Bean 的作用域管理

Spring 支持为 Bean 设置不同的作用域,控制 Bean 的实例数量,常用:

singleton(默认):容器中只有一个 Bean 实例,全局共享。

prototype:每次获取 Bean 都创建新实例。

request:Web 环境下,每个 HTTP 请求创建一个实例。

session:Web 环境下,每个 HTTP 会话创建一个实例。

示例:设置 Bean 作用域

@Component
@Scope("prototype") // 原型作用域,每次getBean都新建
public class CartService {
}

三、容器的启动与关闭

启动:创建 ApplicationContext 实例时,容器会完成 Bean 的扫描、注册、实例化(单例 Bean)、依赖注入等操作。

关闭:非 Web 环境下需手动关闭容器(释放资源),Web 环境下由容器(如 Tomcat)自动关闭。

示例:手动关闭容器

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 使用容器...
        
        // 手动关闭容器(触发 Bean 的销毁方法)
        context.close();
    }
}

四、配置文件

1、文件类型

Spring 配置文件主要分两类,核心都是 XML 格式(这是配置文件的主流形式):

核心配置文件(Bean 定义)

最基础的配置文件,用于注册 Bean、配置依赖注入、指定扫描规则等,文件名通常是 applicationContext.xml(约定俗成,也可自定义)。

环境配置文件(属性配置)

用于存放常量(如数据库连接信息、端口号),常见格式:

.properties(键值对格式,最传统)

.yml/.yaml(层级格式,Spring Boot 主流,也算广义的配置文件)

2、核心配置文件详解(XML)

完整示例

<?xml version="1.0" encoding="UTF-8"?>
<!-- 根标签 + 命名空间声明(核心:beans,扩展:context、tx等) -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1. 扫描指定包下的注解Bean(XML + 注解混合配置) -->
    <context:component-scan base-package="com.example.demo"/>

    <!-- 2. 加载外部属性配置文件(比如db.properties) -->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- 3. 手动注册Bean(id=Bean名称,class=全类名) -->
    <bean id="userDao" class="com.example.demo.dao.UserDaoImpl"/>

    <!-- 4. 注册Bean并注入依赖(构造器注入) -->
    <bean id="userService" class="com.example.demo.service.UserServiceImpl">
        <!-- ref 引用另一个Bean的id -->
        <constructor-arg ref="userDao"/>
    </bean>

    <!-- 5. 注册Bean并设置属性(Setter注入 + 读取外部配置) -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- value 直接赋值,${} 读取外部properties中的值 -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

</beans>

标签说明

标签 / 配置 作用
<beans> 根标签,所有配置都包裹在里面,声明命名空间和 xsd 约束
<bean> 注册单个 Bean,id是 Bean 唯一标识,class是类的全限定名
<constructor-arg> 构造器注入依赖,ref引用其他 Bean,value赋值基本类型
<property> Setter 注入依赖,name对应 Setter 方法名(如 setUrl→url),value/ref赋值
<context:component-scan> 开启注解扫描,base-package指定扫描的包路径(混合配置核心)
<context:property-placeholder> 加载外部.properties 文件,支持${key}读取值

加载配置文件

// 方式1:从类路径加载(最常用)
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// 方式2:从文件系统绝对路径加载
// ApplicationContext context = new FileSystemXmlApplicationContext("D:/project/applicationContext.xml");

// 获取Bean
UserService userService = context.getBean("userService", UserService.class);
3、属性配置文件(.properties/.yml)

这类文件用于存放可配置的常量,解耦代码中的硬编码,常见于数据库连接、服务端口等配置。

properties 格式(传统)

文件名:db.properties

# 数据库连接配置
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.cj.jdbc.Driver

yml 格式(Spring Boot 主流)

文件名:application.yml(层级结构,更简洁)

# 服务器配置
server:
  port: 8080
  servlet:
    context-path: /demo

# 数据库配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

加载属性配置文件

XML 中:通过 <context:property-placeholder> 加载(见上文 XML 示例);

Spring Boot 中:自动加载 resources 下的 application.properties/application.yml,无需手动配置。

4、使用场景
配置文件类型 优点 缺点 适用场景
XML 核心配置 配置与代码分离、老项目兼容 繁琐、编译期不校验、维护成本高 Spring 老项目、需要跨团队共享配置
.properties 简单易上手、兼容性好 层级结构不清晰、重复配置多 简单配置、老项目
.yml 层级清晰、简洁、支持复杂结构 对格式敏感(空格 / 缩进) Spring Boot 新项目、复杂配置

总结

  1. Spring 容器的核心是 IoC 容器,核心接口为 BeanFactory(基础)和 ApplicationContext(常用),负责统一管理 Bean 的生命周期。
  2. 容器的核心能力包括:Bean 的创建 / 注册、依赖注入(DI)、生命周期管理、作用域控制。
  3. 实际开发中优先使用 AnnotationConfigApplicationContext(注解配置),Bean 的注入推荐构造器注入,作用域默认使用 singleton,需多实例时用 prototype。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐