二、Spring框架中容器基本用法详解

1. Spring框架简介

Spring是一个开源的轻量级Java开发框架,最早由Rod Johnson在2002年创建,旨在解决企业级开发中的复杂性问题。Spring通过IoC(控制反转)和AOP(面向切面编程)两大核心思想,帮助开发者更容易地构建解耦、可测试、易维护的Java应用程序。

1.1 Spring的核心特性

  • IoC容器:统一管理Java对象生命周期与依赖关系。

  • AOP支持:支持事务管理、日志记录、安全控制等横切逻辑。

  • 事务管理:集成声明式事务管理,支持多种事务源。

  • 模块化架构:包括Spring Core、Spring Context、Spring AOP、Spring Data、Spring Web等模块,按需使用。

  • 与主流技术整合:如JPA、Hibernate、MyBatis、Kafka、RabbitMQ等。

1.2 Spring发展现状(2025版)

截至2025年,Spring Framework 6.x已成为主流版本,全面支持JDK 21及更高版本,原生支持编译时注解处理(Spring AOT)与GraalVM原生镜像构建能力,性能显著提升,特别适用于微服务架构与云原生应用开发。


2. Spring容器概述

2.1 容器的作用与本质

Spring容器是整个框架的核心,其主要职责是:

  • 创建、管理和销毁Bean的生命周期;

  • 管理对象之间的依赖关系(依赖注入);

  • 提供统一的资源访问方式。

简而言之,Spring容器就是一个高级的Java对象工厂,它根据配置(XML、注解或Java类)生成和维护我们需要的组件实例。

2.2 容器的核心接口结构

Spring提供了多种容器实现,主要有两个核心接口:

  • BeanFactory:容器的最底层接口,延迟加载。

  • ApplicationContext:继承BeanFactory,提供更丰富的容器功能,如国际化、事件发布、AOP支持等。

接口结构图如下(简化):

BeanFactory
   ↑
ApplicationContext
     ↑
ConfigurableApplicationContext

后续章节将详细讲解这两类容器的区别与使用方式。


3. 容器的类型

3.1 BeanFactory介绍

BeanFactory是Spring容器的最基本实现,适用于资源受限环境。它采用懒加载机制,只有在调用getBean()时才会真正创建对象。

示例:

// 1. 创建BeanFactory并加载XML配置
Resource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

// 2. 获取Bean(懒加载)
UserService userService = (UserService) factory.getBean("userService");

注意:从Spring 3.1开始,XmlBeanFactory已被废弃,推荐使用ApplicationContext

3.2 ApplicationContext详解

ApplicationContext是BeanFactory的超集,提供以下增强特性:

  • 国际化支持

  • 事件机制

  • 与Spring AOP、事务集成

  • Bean自动装配

  • Eager初始化(容器启动时加载全部单例Bean)

常用实现类:

  • ClassPathXmlApplicationContext:从类路径加载配置文件。

  • FileSystemXmlApplicationContext:从文件系统加载配置文件。

  • AnnotationConfigApplicationContext:用于注解驱动的配置类。

示例:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);

3.3 二者的区别与使用场景

对比项BeanFactoryApplicationContext
初始化方式懒加载饿加载
功能支持基础Bean管理支持AOP、国际化、事件等
推荐程度较少使用企业级开发首选

实际开发中,我们几乎总是使用ApplicationContext及其子类,除非在某些资源受限或特殊容器环境下才使用BeanFactory。

4. 容器的创建与配置方式

Spring提供了多种创建和配置容器的方法,主要包括以下四种:传统的XML配置、基于注解的配置、基于Java类的配置以及Spring Boot的自动装配。每种方式适用于不同的项目规模与开发场景。

4.1 基于XML的配置方式

这是Spring最早期支持的方式,通过XML文件显式声明Bean和依赖关系。

示例配置:

<bean id="userService" class="com.example.service.UserService">
    <property name="userRepository" ref="userRepository"/>
</bean>

<bean id="userRepository" class="com.example.repository.UserRepository"/>

容器加载代码:

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);

优点:

  • 配置集中,适合大型项目分模块管理

缺点:

  • 配置繁琐、易出错,不具备类型检查,开发效率低


4.2 基于注解的配置方式

从Spring 2.5开始支持,开发者可通过注解将类标记为Bean,并使用依赖注入。

示例代码:

@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;
}

@Repository
public class UserRepository {}

启用扫描:

@Configuration
@ComponentScan("com.example")
public class AppConfig {}

优点:

  • 减少XML配置,开发效率高

  • 更符合现代Java开发习惯


4.3 基于Java配置类的方式

从Spring 3起推荐使用,完全以Java代码代替XML配置,更加面向对象。

示例配置类:

@Configuration
public class AppConfig {

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService() {
        return new UserService(userRepository());
    }
}

容器创建方式:

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);

优点:

  • 类型安全,支持重构

  • 结构清晰,适合IDE支持


4.4 Spring Boot中的容器自动装配

Spring Boot 提供“零配置”的开发体验,通过自动装配机制将容器管理推向极致简化。

入口类示例:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

等效于组合注解:

  • @Configuration

  • @EnableAutoConfiguration

  • @ComponentScan

特性:

  • 根据classpath依赖自动配置所需Bean

  • 配置默认值,开发者可自定义覆盖

适用场景:

  • 快速原型开发

  • 微服务与云原生架构

⚠ 尽管Spring Boot自动装配强大,建议开发者理解Spring容器的本质,避免形成“配置黑盒”。

5. Bean的定义与管理

在Spring框架中,Bean 指的是由Spring容器实例化、组装和管理的对象。Spring通过容器自动完成Bean的创建与依赖注入,这使得我们能够专注于业务逻辑而不是对象之间的组织结构。

5.1 什么是Bean

在Spring中,任何被容器管理的对象都可以称为一个Bean。Bean一般由我们自己编写的类(如Service、DAO、工具类等)组成,通过Spring的配置方式(XML、注解、Java类)被注册到容器中。

Bean并不需要实现任何特定接口或继承任何特定类,只需被容器识别即可。

常见Bean示例:

public class UserService {
    public void register(String username) {
        // 注册逻辑
    }
}

5.2 @Component、@Service、@Repository 注解的区别与使用

Spring提供了一系列注解用于标识Bean。这些注解的作用本质上都是将类注册为Bean,但语义上有所区别,增强了代码的可读性与层次划分。

注解名所属层描述与适用场景
@Component通用组件最基础的注解,用于标识任意组件
@Service业务逻辑层标识服务组件,语义上代表Service类
@Repository数据访问层标识DAO类,同时提供异常转化支持
@Controller控制层Spring MVC 控制器,配合@RequestMapping使用

示例代码:

@Service
public class UserService {
    public void registerUser(String username) {
        System.out.println(\"注册用户: \" + username);
    }
}

@Repository
public class UserRepository {
    public void save(String username) {
        System.out.println(\"保存用户: \" + username);
    }
}

Spring在扫描这些注解时,会自动将类注册为容器Bean。


5.3 Bean的命名与别名配置

默认情况下,Spring使用类名首字母小写作为Bean的ID。

默认命名示例:

上述类在容器中的名称为orderService


自定义Bean名称:

@Component(\"myOrderService\")  // 指定名称
public class OrderService {}

配置别名:

在XML中可以通过name属性为一个Bean配置多个别名:

<bean id=\"mainService\" class=\"com.example.UserService\" name=\"alias1,alias2\"/>

注解方式中没有直接支持别名配置,但可以通过@Qualifier精确指定注入哪个Bean。

示例:

@Autowired
@Qualifier(\"alias1\") // 注入别名为 alias1 的Bean
private UserService userService;

说明:当存在多个相同类型的Bean时,应结合@Qualifier@Primary来指定注入对象,避免歧义。

6. 依赖注入的实现方式

Spring框架的核心思想之一就是依赖注入(Dependency Injection,简称DI)。它使得对象之间的依赖关系不再由代码主动创建,而是由Spring容器负责装配,从而实现了对象间的解耦。

Spring支持多种依赖注入方式,主要包括:

  • 构造函数注入

  • Setter方法注入

  • 字段注入(不推荐)

此外,还可以通过@Qualifier@Primary等注解解决注入冲突。

6.1 构造函数注入

构造函数注入是一种强制依赖的注入方式,适用于所有依赖都是必需的情况。推荐在类的依赖项较少时使用。

示例:

@Component
public class OrderService {
    private final UserRepository userRepository;

    // 构造函数注入
    @Autowired
    public OrderService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Spring 4.3之后,如果类中只有一个构造函数,可以省略@Autowired注解。

6.2 Setter方法注入

适合依赖可选、或依赖之间有循环引用的情况。

示例:

@Component
public class EmailService {
    private NotificationSender sender;

    @Autowired
    public void setSender(NotificationSender sender) {
        this.sender = sender;
    }
}

6.3 字段注入(不推荐使用)

字段注入使用最方便,但可测试性差、难以维护。

示例:

@Component
public class UserController {
    @Autowired
    private UserService userService;
}

建议优先使用构造函数注入,其次是Setter方法注入。字段注入不利于单元测试与依赖明确性。

6.4 使用@Qualifier和@Primary解决冲突

当容器中有多个相同类型的Bean时,Spring无法确定该注入哪个实例,这时可以通过@Primary@Qualifier来指定:

@Primary

标记某个Bean为默认注入对象。

@Bean
@Primary
public NotificationSender emailSender() {
    return new EmailSender();
}
@Qualifier

用于精确指定注入哪个Bean。

@Autowired
@Qualifier("smsSender")
private NotificationSender sender;

结合使用 @Primary@Qualifier 是实际项目中常见的解决方案。


7. Bean的作用域

Spring容器默认以单例方式创建和管理Bean,但在某些业务场景中,我们可能希望每次请求都返回不同的Bean实例,或者根据用户Session来隔离数据。这就涉及到Bean的作用域(Scope)

Spring支持的主要作用域包括:

  • singleton(单例,默认)

  • prototype(原型)

  • request(Web请求级)

  • session(Web会话级)

  • application(Web应用级)

  • websocket(WebSocket会话级)

7.1 Singleton作用域

**默认作用域。**整个Spring容器中只有一个该Bean的实例,所有注入该Bean的地方都引用同一个对象。

@Component
@Scope("singleton")
public class ConfigService {}

**注意:**单例Bean在容器初始化时立即创建(非延迟加载)。

7.2 Prototype作用域

每次调用getBean()都会返回一个新的Bean实例。适用于状态不共享的Bean,如线程对象、会话缓存。

@Component
@Scope("prototype")
public class UserSessionCache {}

prototype作用域的Bean不受Spring容器的生命周期管理,如销毁方法不会被自动调用。

7.3 Request、Session、Application作用域(仅Web环境)

这类作用域通常用于Spring Web MVC或Spring Boot Web应用中:

Request作用域

每次HTTP请求都会创建一个新的Bean。

@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestInfo {}
Session作用域

在同一用户会话中共享Bean,不同会话之间互不影响。

@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserContext {}
Application作用域

整个Web应用范围内只有一个Bean实例。

@Component
@Scope(value = WebApplicationContext.SCOPE_APPLICATION)
public class GlobalCache {}

使用代理模式(proxyMode)

由于非单例Bean可能注入到单例Bean中,Spring使用动态代理方式确保作用域隔离。

@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)

如果不使用代理模式,注入的作用域Bean可能会失效或抛出异常。


8. Bean的生命周期

在Spring中,Bean的生命周期是指一个Bean从创建到销毁的整个过程。理解Bean生命周期,有助于我们在适当的时机进行初始化、资源释放等操作。

Spring容器会对Bean生命周期的多个阶段进行管理,包括实例化、依赖注入、初始化、使用、销毁等。

8.1 生命周期流程总览

下面是一个典型的Spring Bean生命周期流程:

  1. 实例化Bean(Constructor)

  2. 设置属性(依赖注入)

  3. 如果实现了 BeanNameAwareBeanFactoryAwareApplicationContextAware 接口,Spring将调用对应方法

  4. 如果实现了 BeanPostProcessor,则会调用postProcessBeforeInitialization()

  5. 调用初始化方法(如实现InitializingBeanafterPropertiesSet()或自定义init-method)

  6. 调用postProcessAfterInitialization()(来自BeanPostProcessor)

  7. Bean准备就绪,可被使用

  8. 容器关闭时,调用销毁方法(如DisposableBeandestroy()或自定义destroy-method)

8.2 初始化与销毁方法

Spring允许我们通过以下三种方式定义Bean的初始化与销毁逻辑:

1. 使用注解 @PostConstruct 和 @PreDestroy

推荐使用,简洁清晰。

@Component
public class CacheManager {

    @PostConstruct
    public void init() {
        // 初始化缓存
        System.out.println("CacheManager 初始化");
    }

    @PreDestroy
    public void destroy() {
        // 清理资源
        System.out.println("CacheManager 销毁");
    }
}

注意:@PostConstruct 和 @PreDestroy 依赖于JSR-250,需要在模块中添加相关依赖(如 Jakarta annotations)。

2. 实现 InitializingBean 和 DisposableBean 接口
@Component
public class FileWriter implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("FileWriter 初始化");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("FileWriter 销毁");
    }
}
3. 在配置中指定 init-method 和 destroy-method

适用于 XML 或 Java 配置方式。

@Bean(initMethod = "start", destroyMethod = "close")
public TaskExecutor taskExecutor() {
    return new TaskExecutor();
}

8.3 BeanPostProcessor 的使用

BeanPostProcessor 是Spring容器中用于扩展Bean生命周期行为的机制,常用于AOP、属性填充、日志注入等。

  • postProcessBeforeInitialization():在初始化方法(@PostConstruct等)之前执行

  • postProcessAfterInitialization():在初始化方法之后执行

示例:

@Component
public class AuditLoggerPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("[BeforeInit] " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("[AfterInit] " + beanName);
        return bean;
    }
}

BeanPostProcessor 可以注册多个,Spring 会按顺序依次执行。

8.4 使用@PreDestroy注意事项

  • 只对单例Bean生效;prototype作用域的Bean需要手动销毁。

  • 适用于容器关闭前自动释放资源,如关闭线程池、数据库连接等。

9. Spring容器的使用最佳实践

在企业级项目开发中,合理使用Spring容器能显著提升项目的可维护性、可测试性和可扩展性。本章将结合实际经验与Spring官方推荐,总结一些高质量的使用建议和最佳实践。

9.1 容器配置建议

9.1.1 避免XML配置的过度依赖

虽然Spring仍然支持XML配置,但从Spring 4开始,官方已推荐采用注解与Java配置类(@Configuration)方式,配合Spring Boot的自动装配特性,将配置最小化。

@Configuration
@ComponentScan(basePackages = "com.example.service")
public class AppConfig {}
9.1.2 合理拆分配置类

一个大型系统应将配置类模块化拆分,如数据库、MVC、任务调度等功能分别独立配置:

@Configuration
@Import({DatasourceConfig.class, WebMvcConfig.class, SchedulerConfig.class})
public class RootConfig {}

9.2 如何编写高内聚低耦合的Bean

9.2.1 倾向构造函数注入

构造函数注入让依赖一目了然,且便于单元测试与mock,不容易出现空指针问题。

@Service
public class OrderService {
    private final UserRepository userRepository;

    public OrderService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
9.2.2 避免在Bean中直接调用new

在Spring容器托管之外手动创建对象,会绕过容器管理,破坏依赖注入机制,应使用依赖注入或工厂模式:

// 错误示范
EmailSender sender = new EmailSender();

// 正确做法
@Autowired
private EmailSender sender;
9.2.3 使用接口进行依赖声明

使用接口编程可提升代码灵活性和扩展性:

public interface PaymentStrategy {
    void pay(Order order);
}

@Component("wechatPayment")
public class WeChatPayment implements PaymentStrategy { ... }

9.3 如何排查Bean注入失败的问题

  • 检查是否声明为Spring组件(如@Component, @Service, @Repository)。

  • 检查包扫描路径是否覆盖目标类所在包。

  • 查看是否存在多个同类型Bean未指定@Qualifier@Primary

  • 使用@PostConstruct进行初始化验证。

  • 利用Spring Boot的ApplicationContextRunnerSpringBootTest编写单元测试。

9.4 结合单元测试进行容器验证

Spring支持JUnit 5与Mockito,借助@SpringBootTest@ContextConfiguration可轻松进行容器集成测试:

@SpringBootTest
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    void testCreateOrder() {
        Order order = orderService.createOrder("user1");
        assertNotNull(order);
    }
}

对于轻量级测试,也可通过构造注入+Mockito进行Mock测试,避免启动整个容器。

10. 总结与展望

通过本文,我们系统性地回顾并实践了Spring容器的基本用法。从核心概念、容器类型、Bean管理与依赖注入方式,到生命周期控制与使用中的最佳实践,每一个环节都是Spring框架高内聚低耦合设计理念的具体体现。

回顾核心要点:

  • Spring容器是Spring框架的核心,负责管理所有Bean对象及其生命周期。

  • ApplicationContext是最常用的容器实现,功能完备,推荐用于绝大多数场景。

  • 通过注解与Java配置类替代XML配置是现代Spring开发的主流方式。

  • 依赖注入建议优先使用构造函数方式,搭配@Primary与@Qualifier解决冲突。

  • 理解Bean作用域与生命周期有助于避免常见问题,尤其在Web和多线程环境中。

Spring的设计目标一直是提升开发效率、促进架构解耦。在2025年Spring Framework最新版中,借助原生支持GraalVM、AOT编译以及对现代JDK的支持,Spring容器的启动效率和运行性能都有了进一步的提升。

无论是开发企业级系统、构建微服务应用,还是参与云原生架构设计,Spring容器的运用都贯穿始终。掌握其使用原理与最佳实践,是每一位Java开发者的必修课。


后续学习建议

本文旨在为读者建立完整的Spring容器知识体系,但更细致的内容(如高级生命周期扩展、自定义注入逻辑、BeanFactoryPostProcessor用法、Spring Context的事件机制等)将在本专栏后续文章中逐一展开。

📌 建议读者关注本专栏,按照文章顺序持续学习,你将逐步掌握Spring框架最深层的运行机制与设计哲学。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

探索java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值