Spring之核心容器IoC/DI/基本操作详解

    • 一、核心概念:IoC与DI的本质
      • 1.1 IoC(Inversion of Control,控制反转)
        • 传统开发模式(无IoC)
        • IoC模式(Spring容器管理)
      • 1.2 DI(Dependency Injection,依赖注入)
        • DI的三种实现方式
      • 1.3 IoC容器的核心作用
    • 二、Spring容器的核心接口与实现类
      • 2.1 核心接口关系
      • 2.2 常用容器实现类
    • 三、Bean的定义与依赖注入(DI)实战
      • 3.1 环境准备
      • 3.2 基于XML的Bean定义与注入
        • 3.2.1 定义Bean(XML配置)
        • 3.2.2 目标类(UserDao、UserService)
        • 3.2.3 启动容器并使用Bean
      • 3.3 基于注解的Bean定义与注入(推荐)
        • 3.3.1 核心注解
        • 3.3.2 注解配置实战
        • 3.3.3 启动容器(基于注解配置)
      • 3.4 三种依赖注入方式对比
        • 3.4.1 构造器注入(推荐)
        • 3.4.2 Setter注入
        • 3.4.3 字段注入(简洁但不推荐)
    • 四、Spring容器的基本操作
      • 4.1 容器的创建与关闭
        • 创建容器
        • 关闭容器
      • 4.2 获取Bean的三种方式
      • 4.3 Bean的作用域(Scope)
      • 4.4 Bean的生命周期
        • 生命周期示例
    • 五、常见问题与避坑指南
      • 5.1 Bean的命名冲突
      • 5.2 循环依赖问题
      • 5.3 单实例Bean的线程安全问题

Spring框架的核心是IoC容器,它通过控制反转(IoC)和依赖注入(DI)实现对象的管理与依赖解耦,是Spring所有功能的基础。

一、核心概念:IoC与DI的本质

1.1 IoC(Inversion of Control,控制反转)

IoC是一种设计思想,核心是将对象的创建权由开发者转移给容器,实现“谁用谁创建”到“容器创建后注入”的转变。

传统开发模式(无IoC)
// 传统方式:开发者手动创建对象
public class UserService {// 依赖UserDao,手动创建private UserDao userDao = new UserDaoImpl();public void addUser() {userDao.insert(); // 调用依赖对象的方法}
}

问题

  • 依赖硬编码(new UserDaoImpl()),若更换实现类(如UserDaoMybatisImpl),需修改UserService源码;
  • 对象创建与业务逻辑耦合,难以测试和扩展。
IoC模式(Spring容器管理)
// IoC方式:容器创建对象,开发者仅声明依赖
public class UserService {// 依赖UserDao,由容器注入(无需手动new)@Autowiredprivate UserDao userDao;public void addUser() {userDao.insert();}
}

核心变化

  • 对象创建权转移:UserDao的实例由Spring容器创建,而非UserService手动创建;
  • 依赖解耦:UserService仅依赖UserDao接口,不依赖具体实现,更换实现类无需修改源码。

1.2 DI(Dependency Injection,依赖注入)

DI是IoC的具体实现方式,指容器在创建对象时,自动将依赖的对象注入到当前对象中。简单说:IoC是思想,DI是手段。

DI的三种实现方式
  1. 构造器注入:通过构造方法传入依赖对象;
  2. Setter注入:通过Setter方法设置依赖对象;
  3. 字段注入:通过注解直接标记字段(如@Autowired)。

后续会通过代码示例详细讲解这三种方式。

1.3 IoC容器的核心作用

Spring的IoC容器(如ApplicationContext)本质是一个“对象工厂”,核心功能:

  1. 对象管理:创建、存储、销毁Bean(Spring对对象的称呼);
  2. 依赖注入:自动将依赖的Bean注入到目标对象;
  3. 生命周期管理:控制Bean的初始化、销毁等生命周期节点;
  4. 配置解析:读取XML、注解等配置,解析Bean的定义。

二、Spring容器的核心接口与实现类

Spring提供了两套核心容器接口:BeanFactoryApplicationContext,后者是前者的增强版,实际开发中优先使用ApplicationContext

2.1 核心接口关系

BeanFactory(基础容器)└── ApplicationContext(高级容器,继承BeanFactory)├── ClassPathXmlApplicationContext(XML配置,类路径加载)├── FileSystemXmlApplicationContext(XML配置,文件系统加载)├── AnnotationConfigApplicationContext(注解配置)└── WebApplicationContext(Web环境专用)

2.2 常用容器实现类

容器实现类特点适用场景
ClassPathXmlApplicationContext从类路径加载XML配置文件非Web项目,配置文件在src/main/resources
AnnotationConfigApplicationContext基于注解配置(如@Configuration注解驱动开发,无XML配置

三、Bean的定义与依赖注入(DI)实战

3.1 环境准备

创建Maven项目,添加Spring核心依赖:

<dependencies><!-- Spring核心容器 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.20</version></dependency>
</dependencies>

3.2 基于XML的Bean定义与注入

3.2.1 定义Bean(XML配置)

创建src/main/resources/spring.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义UserDao的Bean(id:唯一标识,class:全类名) --><bean id="userDao" class="com.example.dao.UserDaoImpl"/><!-- 定义UserService的Bean,并注入UserDao --><bean id="userService" class="com.example.service.UserService"><!-- Setter注入:通过setUserDao方法注入userDao --><property name="userDao" ref="userDao"/></bean>
</beans>
3.2.2 目标类(UserDao、UserService)
// UserDao接口
public interface UserDao {void insert();
}// UserDao实现类
public class UserDaoImpl implements UserDao {@Overridepublic void insert() {System.out.println("UserDaoImpl:插入用户");}
}// UserService(需要注入UserDao)
public class UserService {private UserDao userDao;// Setter方法(用于Setter注入,方法名需对应XML中的property name)public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void addUser() {userDao.insert(); // 调用注入的UserDao}
}
3.2.3 启动容器并使用Bean
public class Main {public static void main(String[] args) {// 1. 加载Spring配置文件,创建容器(ApplicationContext是IoC容器的核心接口)ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2. 从容器中获取UserService(参数为XML中定义的id)UserService userService = context.getBean("userService", UserService.class);// 3. 调用方法(依赖的UserDao已被容器注入)userService.addUser(); // 输出:UserDaoImpl:插入用户}
}

3.3 基于注解的Bean定义与注入(推荐)

注解配置比XML更简洁,是现代Spring开发的主流方式。

3.3.1 核心注解
注解作用
@Component标记类为Bean(通用注解)
@Repository标记DAO层Bean(@Component的特例)
@Service标记Service层Bean(@Component的特例)
@Controller标记Controller层Bean(Web环境)
@Autowired自动注入依赖(默认按类型匹配)
@Configuration标记配置类(替代XML配置文件)
@ComponentScan扫描指定包下的注解Bean
3.3.2 注解配置实战
// 1. 配置类(替代XML,扫描com.example包下的注解Bean)
@Configuration
@ComponentScan("com.example")
public class SpringConfig {// 无需手动定义Bean,通过@Component等注解自动扫描
}// 2. UserDaoImpl(用@Repository标记为Bean)
@Repository // 等价于<bean id="userDaoImpl" class="..."/>
public class UserDaoImpl implements UserDao {@Overridepublic void insert() {System.out.println("UserDaoImpl:插入用户");}
}// 3. UserService(用@Service标记,并通过@Autowired注入UserDao)
@Service // 等价于<bean id="userService" class="..."/>
public class UserService {// 字段注入:直接在字段上标记@Autowired(无需Setter或构造器)@Autowiredprivate UserDao userDao;public void addUser() {userDao.insert();}
}
3.3.3 启动容器(基于注解配置)
public class Main {public static void main(String[] args) {// 加载注解配置类,创建容器ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);// 获取UserService(Bean id默认是类名首字母小写:userService)UserService userService = context.getBean("userService", UserService.class);userService.addUser(); // 输出:UserDaoImpl:插入用户}
}

3.4 三种依赖注入方式对比

3.4.1 构造器注入(推荐)

通过构造方法注入依赖,确保对象创建时依赖已初始化:

@Service
public class UserService {private final UserDao userDao;// 构造器注入(@Autowired可省略,Spring 4.3+支持单构造器自动注入)@Autowiredpublic UserService(UserDao userDao) {this.userDao = userDao;}
}

优势

  • 依赖不可变(final修饰),避免后续被修改;
  • 强制初始化依赖,防止null异常。
3.4.2 Setter注入

通过Setter方法注入,灵活性高(可在对象创建后修改依赖):

@Service
public class UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}
}

优势:适合可选依赖(可设置默认值)。

3.4.3 字段注入(简洁但不推荐)

直接在字段上注入,代码简洁但存在缺陷:

@Service
public class UserService {@Autowiredprivate UserDao userDao; // 字段注入
}

缺陷

  • 无法注入final字段(构造器注入可以);
  • 依赖隐藏在字段中,不通过构造器或方法暴露,可读性差;
  • 不利于单元测试(难以手动注入模拟对象)。

四、Spring容器的基本操作

4.1 容器的创建与关闭

创建容器
// 1. 基于XML(类路径)
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2. 基于XML(文件系统路径)
ApplicationContext context = new FileSystemXmlApplicationContext("D:/spring.xml");// 3. 基于注解配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
关闭容器

ApplicationContext无直接关闭方法,需通过ConfigurableApplicationContext

ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 关闭容器(触发Bean的销毁方法)
context.close();

4.2 获取Bean的三种方式

// 1. 通过id获取(返回Object,需强转)
UserService userService1 = (UserService) context.getBean("userService");// 2. 通过id+类型获取(推荐,无需强转)
UserService userService2 = context.getBean("userService", UserService.class);// 3. 通过类型获取(适合单实例Bean,存在多个同类型Bean时报错)
UserService userService3 = context.getBean(UserService.class);

4.3 Bean的作用域(Scope)

Spring默认创建的Bean是单实例(singleton),可通过@Scope指定作用域:

@Service
@Scope("prototype") // 多实例:每次获取Bean时创建新对象
public class UserService { ... }

常用作用域:

作用域说明适用场景
singleton单实例(默认),容器启动时创建无状态Bean(如Service、Dao)
prototype多实例,每次获取时创建有状态Bean(如Model、View)
request每个HTTP请求创建一个实例(Web环境)Web应用请求相关Bean
session每个会话创建一个实例(Web环境)Web应用会话相关Bean

4.4 Bean的生命周期

Spring容器管理Bean的完整生命周期:

  1. 实例化:创建Bean对象(调用构造方法);
  2. 属性注入:注入依赖的Bean;
  3. 初始化:执行初始化方法(如@PostConstruct);
  4. 使用:Bean可被容器获取并使用;
  5. 销毁:容器关闭时执行销毁方法(如@PreDestroy)。
生命周期示例
@Service
public class UserService {// 1. 实例化(构造方法)public UserService() {System.out.println("UserService:构造方法(实例化)");}// 2. 属性注入(@Autowired)@Autowiredprivate UserDao userDao;// 3. 初始化方法(@PostConstruct标记)@PostConstructpublic void init() {System.out.println("UserService:初始化");}// 5. 销毁方法(@PreDestroy标记)@PreDestroypublic void destroy() {System.out.println("UserService:销毁");}
}

执行结果

UserService:构造方法(实例化)
UserService:初始化  // 容器启动时执行
// 使用Bean...
UserService:销毁    // 容器关闭时执行

五、常见问题与避坑指南

5.1 Bean的命名冲突

当容器中存在多个同类型Bean时,注入会报错NoUniqueBeanDefinitionException

// 两个UserDao实现类
@Repository
public class UserDaoImpl1 implements UserDao { ... }@Repository
public class UserDaoImpl2 implements UserDao { ... }// 注入时冲突
@Service
public class UserService {@Autowired // 报错:存在两个UserDao Beanprivate UserDao userDao;
}

解决方案

  1. @Qualifier指定Bean的id:
@Autowired
@Qualifier("userDaoImpl1") // 指定注入id为userDaoImpl1的Bean
private UserDao userDao;
  1. @Primary标记优先注入的Bean:
@Repository
@Primary // 优先注入
public class UserDaoImpl1 implements UserDao { ... }

5.2 循环依赖问题

两个Bean互相依赖(A依赖B,B依赖A)会导致循环依赖:

@Service
public class AService {@Autowiredprivate BService bService;
}@Service
public class BService {@Autowiredprivate AService aService;
}

解决方案

  1. @Lazy延迟注入(打破即时依赖):
@Service
public class AService {@Autowired@Lazy // 延迟注入BServiceprivate BService bService;
}
  1. 改用Setter注入(构造器注入无法解决循环依赖)。

5.3 单实例Bean的线程安全问题

单实例Bean(默认)在多线程环境下,若存在共享状态(如成员变量),会有线程安全问题:

@Service
public class UserService {// 共享状态(多线程访问会冲突)private int count = 0;public void increment() {count++; // 线程不安全操作}
}

解决方案

  1. 避免共享状态(推荐):单实例Bean设计为无状态(不定义成员变量);
  2. 改用prototype作用域(不推荐,性能差);
  3. 使用线程安全容器(如ThreadLocal)。

总结:Spring核心容器通过IoC和DI实现了对象的“按需创建”和“自动注入”:

  1. 依赖解耦:对象之间仅依赖接口,不依赖具体实现,降低耦合度;
  2. 简化开发:开发者无需关注对象创建和依赖管理,专注业务逻辑;
  3. 可扩展性:通过配置或注解轻松更换Bean实现,无需修改业务代码;
  4. 生命周期管理:容器统一管理Bean的创建、初始化、销毁,便于资源控制。
    掌握Spring容器的核心是理解“容器是对象的管理者”:它创建对象、注入依赖、控制生命周期,是整个Spring生态的基础。后续学习Spring的AOP、事务等功能,都需要以容器为基础。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91403.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91403.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/91403.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文阅读】基于注意力机制的冥想脑电分类识别研究(2025)

基于注意力机制的冥想脑电分类识别研究&#x1f4a1; Meta DataTitle基于注意力机制的冥想脑电分类识别研究Authors周梓涵Pub. date2025&#x1f4dc; Research Background & Objective背景&#xff1a; 现代生活压力导致心理问题日益突出&#xff0c;冥想作为一种有效的心…

GitHub 上 Star 数量前 8 的开源 Web 应用项目

原文链接&#xff1a;https://www.nocobase.com/cn/blog/github-open-source-web-applications。 近期&#xff0c;我们发布了多篇「Top GitHub Star 开源项目推荐」系列文章&#xff0c;受到了大量点赞与收藏&#xff0c;很多开发者留言表示希望能看到更多不同领域的开源工具推…

FATFS文件系统原理及其移植详解

一、FATFS简介 FATFS 是一个完全免费开源的 FAT/exFAT 文件系统模块&#xff0c;专门为小型的嵌入式系统而设计。它完全用标准 C 语言&#xff08;ANSI C C89&#xff09;编写&#xff0c;所以具有良好的硬件平台独立性&#xff0c;只需做简单的修改就可以移植到 8051、PIC、A…

KubeRay 和 Ray

KubeRay 和 Ray 不是替代关系&#xff0c;而是互补的协作关系。两者在分布式计算生态中扮演不同角色&#xff0c;共同构成完整的云原生 AI 解决方案。以下是具体分析&#xff1a;&#x1f527; 1. 核心定位差异Ray 是分布式计算引擎&#xff0c;提供底层 API&#xff08;如 ray…

破解轮胎仓储高密度与柔性管理难题

轮胎作为特殊的大件异形工业品&#xff0c;其仓储管理长期面临多重挑战&#xff1a;规格型号繁杂导致SKU数量庞大&#xff0c;重型载重对货架承重提出极高要求&#xff0c;橡胶材质对防压变形、避光防老化等存储环境存在严苛标准。传统平置堆垛或普通货架方案不仅空间利用率不足…

EVA series系列(上)

目录 一、EVA 1、概述 2、方法 二、EVA-02 1、概述 2、架构 三、EVA-CLIP 1、概述 2、方法 四、EMU 1、概述 2、架构 3、训练细节 4、评估 一、EVA 1、概述 为探寻大规模表征学习任务的MIM预训练任务在ViT基础上扩展到1B参数量规模&#xff0c;结合10M级别&am…

ABP VNext + EF Core 二级缓存:提升查询性能

ABP VNext EF Core 二级缓存&#xff1a;提升查询性能 &#x1f680; &#x1f4da; 目录ABP VNext EF Core 二级缓存&#xff1a;提升查询性能 &#x1f680;引言 &#x1f680;一、环境与依赖 &#x1f6e0;️二、集成步骤 ⚙️2.1 安装 NuGet 包2.2 注册缓存服务与拦截器2…

3.1k star!推荐一款开源基于AI实现的浏览器自动化插件工具 !

大家好&#xff01;今天&#xff0c;我要给大家介绍一款超实用的开源工具——Chrome MCP Server&#xff01;这款工具不仅能大幅提升我们的工作效率&#xff0c;还能让AI助手&#xff08;如Claude&#xff09;直接操控浏览器&#xff0c;实现自动化操作、内容分析等强大功能。 …

关于 OpenAI 的反思

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Python爬虫库性能与选型对比

Python常用爬虫库的优势对比。这是一个非常实用的问题&#xff0c;很多Python开发者都会面临选择合适爬虫工具的困惑。我根据网络很多搜索结果&#xff0c;整理出这些信息&#xff0c;为用户提供一个全面且清晰的对比分析。以下是Python中常用爬虫库的核心优势对比及选型建议&a…

NAT作业

拓扑图 实验要求 1.按照图示配置IP地址&#xff0c;公网地址100.1.1.1/24..较网“说过?,使“掩入到互联网&#xff0c;私服究的不到公的&#xff0c;使阳接入无三。.私网A通过NAPT&#xff0c;使R1接入到互联网&#xff0c;私网B通过EASY,IP&#xff0c;使R3接入到互联网实验思…

JAVA进阶--JVM

一.JVM的概述java语言有跨平台特点, 写一次java程序,可以在不同的平台上运行.(JVM虚拟机的作用)前提条件: 在不同的平台上安装不同的虚拟机(虚拟机就是一个翻译).java--->.class--->不同的虚拟机--->机器码1.jvm作用:负责将字节码翻译为机器码, 管理运行时内存2.jvm的…

基于Alpine构建MySQL镜像

文章目录基于Alpine构建MySQL镜像一、基础镜像选择与初始化1. 基础镜像选型2. 系统初始化二、核心配置构建1. 目录与权限配置2. 配置文件优化三、安全增强配置1. 密码策略强化2. 非root运行四、数据持久化与启动配置1. 数据卷声明2. 入口脚本优化五、完整Dockerfile示例六、关键…

Alamofire 网络请求全流解析,通俗易懂

Alamofire 网络请求全流程解析&#xff1a;从发起请求到处理响应 一、请求发起阶段&#xff1a;准备你的"快递" 1. 你告诉Alamofire要发什么"快递" // 就像告诉快递员&#xff1a;"我要寄一个包裹给https://api.example.com" AF.request("h…

链路聚合技术

链路聚合技术 链路聚合概述及应用场景 概述 链路聚合是把多条物理链路聚合在一起&#xff0c;形成一条逻辑链路。应用在交换机、路由器、服务器间链路&#xff0c;注意了&#xff0c;主机上面不能用链路聚合技术分为三层链路聚合和二层链路聚合链路聚合的作用 增加链路带宽提供…

SpringCloud之Zuul

SpringCloud之Zuul 推荐参考&#xff1a;https://www.springcloud.cc/spring-cloud-dalston.html#_router_and_filter_zuul 1. 什么是Zuul Spring Cloud Zuul 是 Netflix 提供的微服务网关核心组件&#xff0c;作为统一的 API 入口&#xff0c;承担请求路由、过滤、安全控制等…

低精度定时器 (timer_list) 和 高精度定时器 (hrtimer)

Linux 内核提供了两种主要类型的定时器&#xff0c;以满足不同的时间精度需求&#xff1a;低精度定时器 (timer_list) 和 高精度定时器 (hrtimer)。它们各有特点和适用场景。下面&#xff0c;我将分别提供它们在内核代码中的简化使用示例。1. 低精度定时器 (timer_list) 示例ti…

虚拟机VMware的使用方法

虚拟机VMware的使用方法VMware是全球领先的虚拟化技术提供商&#xff0c;其产品&#xff08;如VMware Workstation Pro&#xff09;允许用户在单一物理机上运行多个操作系统&#xff08;OS&#xff09;&#xff0c;实现资源高效利用、隔离测试和灵活部署。本文将详细介绍VMware…

冰岛人(map)

#include<bits/stdc.h> using namespace std; struct people { string fat; int sex; }; map<string,people>mp; int pan(string s,string m) { string s1; int i0; while(s!“”) { int y0; s1m; while(s1!“”) { if(s1s&&(i<4||y<4)) return 0; s…

MS Azure Eventhub 发送 AD log 到cribl

1: 首先说一下,Cribl 提供了很多第三方的接口: 先看一下cribl 提供的接口界面: 注意到,上面提供的link 地址是 xxxxx:9093, 不鼠标放到撒谎给你吗的? 上面,就可以看到了。所以要开的port 一定要把9093 开了,关于全部开的port: What ports do I need to open on the f…