当前位置: 代码网 > it编程>编程语言>Java > Spring IoC容器与Bean管理全解析

Spring IoC容器与Bean管理全解析

2026年07月21日 Java 我要评论
一、bean的存储在上一节的案列中,如果需要把某个对象交给ioc容器管理,需要在类上添加一个注解:@component,而spring框架为了更好的服务web应用程序,提供了更丰富的注解类注解:@co

一、bean的存储

在上一节的案列中,如果需要把某个对象交给ioc容器管理,需要在类上添加一个注解:@component,而spring框架为了更好的服务web应用程序,提供了更丰富的注解

类注解:@controller、@service、@repository、@component、@configuration

方法注解:@bean

1.1 @controller(控制器存储)

使用@controller存储bean

从spring容器中获取对象,

上述代码是根据类型来查找的对象,但如果一个spring容器中,同一个类型存在多个bean的话,怎么获取呢,(bean的命名上一小节有分享过,这里就不赘述了)

1.根据bean名称获取bean

object getbean(string var1)throws beansexception;

2.根据bean名称和类型获取bean

<t> t getbean(string var1,class<t> var2)throws beansexception;

3.根据类型获取bean

<t> t getbean(class<t> var1)throws beansexception;

4. 按bean名称和构造函数动态创建bean,只适用于具有原型(prototype)作用域的bean

object getbean(string var1,class<t> var2)throws beansexception;

5.按bean类型和构造函数动态创建bean,只适用于具有原型(prototype)作用域的bean

<t> t getbean(string var1,class<t> var2)throws beansexception;

因为实际应用中1 2 3频率最高,就只展示这三种,后续几个注解也是

查看是否是同一个对象

可以看出,地址一致,获取的bean的对象是同一个

1.2 @service(服务存储)

使用@service存储bean

读取bean

1.3 @repository(仓库存储)

使用@repository存储bean

读取bean

1.4 @component(组件存储)

使用@component存储bean

读取bean

1.5 @configuration(配置存储)

使用@cofiguration存储bean

读取bean

1.6 这些注解的用途

  • @controller:控制层,接受请求,对请求进行处理,并经行响应
  • @service:业务逻辑层,处理具体的业务逻辑
  • @repository:数据访问层,也称为持久层,负责数据访问操作
  • @configuration:配置层,处理项目中的一些配置信息
  • @component:通用组件注解,不属于上面分层的普通业务类、工具类使用,标记后会被 spring 自动扫描存入 ioc 容器

@controller@service@repository@configuration 都是@component的衍生注解。除了@controller不可以和@responsebody直接等同,其余衍生注解仅做分层语义区分,底层功能一致,作用都是将类交给 spring ioc 容器管理。

比如:杯子有喝水的杯子,刷牙的杯子。虽然它们都是杯子,但我们日常更倾向于用刷牙的杯子刷牙,喝水的杯子喝水。

1.7 applicationcontext vs beanfactory

  • 继承关系和功能方面来说spring容器有两个顶级的接口:beanfactory和applicationcontext。其中beanfactory提供了基础的访问容器的能力,而applicationcontext属于beanfactory的子类,它除了基础beanfactory的所有功能之外,还拥有独特的特性,添加了国际化支持、资源访问支持、以及事件传播等方面的支持
  • 从性能方面来说:applicationcontext是一次性加载并初始化所有的bean对象,而beanfactory是需要哪个才去加载那个,因此更加轻量

二、方法注解@bean

上面我们介绍了五大类注解,但存在两个问题:

1,使用外部包里的类,没办法添加类注解

2,一个类,需要多个对象,如多个数据源

基于这两个问题,我们就需要使用方法注解@bean,@bean注解也需搭配五大注解使用

2.1 搭配类注解的使用

在spring框架的设计中,方法注解@bean要配合类注解才能将对象正常存储到spring容器中

如果注掉@component

2.2 定义多个对象

2.3 重命名bean

2.3.1 方法一:最完整的写法

2.3.2 方法二:省略“name={ }”

2.3.3 方法三:当只有一个名称时,{}也可省略

三、扫描路径

q:使用前面学习的五大注解声明的 bean,一定会生效吗?

a:不一定生效。原因在于:bean 想要生效,还需要被 spring 扫描到。

下面我们通过修改项目工程的目录结构,来测试 bean 对象是否生效:

再运行代码:

@springbootapplication
public class springiocdemoapplication {
    public static void main(string[] args) {
        // 获取 spring 上下文对象
        applicationcontext context = 
            springapplication.run(springiocdemoapplication.class, args);
        // 从 spring 上下文中获取对象
        user u1 = (user) context.getbean("u1");
        // 使用对象
        system.out.println(u1);
    }
}

运行结果:

解释:没有找到名称为 "u1" 的 bean。

为什么没有找到 bean 对象呢?

使用五大注解声明的 bean,要想生效,还需要配置扫描路径,让 spring 扫描到这些注解。也就是通过 @componentscan 来配置扫描路径。

@componentscan({"com.example.demo"})
@springbootapplication
public class springiocdemoapplication {
    public static void main(string[] args) {
        // 获取 spring 上下文对象
        applicationcontext context = 
            springapplication.run(springiocdemoapplication.class, args);
        // 从 spring 上下文中获取对象
        user u1 = (user) context.getbean("u1");
        // 使用对象
        system.out.println(u1);
    }
}

{} 里可以配置多个包路径,例如:@componentscan({"com.example.demo", "com.example.service"})

注意:这种做法仅做了解,不推荐使用。

那为什么前面没有配置 @componentscan 注解也可以呢?

@componentscan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解 @springbootapplication 中了。默认扫描的范围是 spring boot 启动类所在包及其子包。

在配置类上添加 @componentscan 注解,该注解默认会扫描该类所在的包下所有的配置类。

推荐做法:把启动类放在我们希望扫描的包的路径下,这样我们定义的 bean 就都可以被扫描到。

扫描路径配置总结

  1. 默认扫描:spring boot 项目默认扫描启动类所在包及其所有子包
  2. 自定义扫描:使用 @componentscan 注解可以指定要扫描的包路径
  3. 多包扫描:可以通过数组形式指定多个包路径
  4. 最佳实践:合理组织项目结构,将需要被扫描的类放在启动类的子包中

通过合理配置扫描路径,可以确保 spring 能够正确发现和管理所有的 bean 组件,这是 spring ioc 容器正常工作的基础。

四、di详解

上面所分享都是控制反转ioc的 细节,下面我们来看看di吧

依赖注入是一个过程,是指ioc容器在创建bean时, 去提供运行时所依赖的资源,而资源指的就是对象.

关于依赖注入,spring也给我们提供了三种方式:

  1. 属性注入
  2. 构造方法注入
  3. setter注入

4.1 属性注入 @autowired

代码实现

运行展示

4.2 构造方法注入

代码实现

运行展示

如果只有一个构造方法@autowired可省

如果有多个构造方式,默认的构造方法为无参构造方法

可以通过@autowired来指定默认构造方法

4.3 setter注入

代码实现

运行展示

setter注入和属性的setter方法实现类似,只不过在设置set方法的时候需要加上@autowired注解

4.4 三种注入的优缺点分析

spring 提供了三种依赖注入方式,每种方式都有其适用场景和优缺点。了解这些差异有助于我们在实际开发中选择最合适的注入方式。

1. 属性注入(field injection)

优点:

  • 简洁直观:代码量最少,直接在字段上添加 @autowired 注解即可完成注入
  • 使用方便:无需编写额外的构造方法或 setter 方法
  • 可读性好:依赖关系一目了然,便于快速理解类的依赖项

缺点:

  • 仅适用于 ioc 容器:如果脱离 spring 容器,这种方式无法工作
  • 空指针风险:只有在使用的时候才会出现 npe(空指针异常),编译期无法发现
  • 无法注入 final 字段:不能注入被 final 修饰的属性
  • 测试困难:在单元测试中需要依赖 spring 容器或使用反射设置字段
  • 违反单一职责原则:容易导致类依赖过多,职责不清晰

2. 构造方法注入(constructor injection)

优点:

  • 可以注入 final 字段:支持注入被 final 修饰的属性,确保依赖不可变
  • 依赖不可变:注入的对象在构造完成后不会被修改,保证了对象状态的一致性
  • 完全初始化:依赖在使用前一定会被完全初始化,因为构造方法在类加载阶段就会执行
  • 通用性好:构造方法是 jdk 支持的标准特性,不依赖特定框架,更换框架时仍然适用
  • 便于测试:可以在单元测试中直接通过构造方法传入依赖的模拟对象
  • 强制依赖:明确类的必需依赖,避免部分依赖缺失的情况

缺点:

  • 代码略显繁琐:当需要注入多个依赖时,构造方法的参数列表会较长
  • 循环依赖问题:如果存在循环依赖,构造方法注入会直接报错

注意事项:如果类只有一个构造方法,那么 @autowired 注解可以省略;如果类中有多个构造方法,需要添加 @autowired 来明确指定使用哪个构造方法。

3. setter 注入(setter injection)

优点:

  • 灵活性高:方便在类实例化之后,重新对该对象进行配置或注入
  • 可选依赖:适合注入非必需的依赖,可以有默认值或为空
  • 便于继承:子类可以通过重写 setter 方法来改变注入行为
  • 解决循环依赖:在某些情况下可以解决构造方法注入无法处理的循环依赖问题

缺点:

  • 无法注入 final 字段:不能注入被 final 修饰的属性
  • 依赖可能被改变:setter 方法可能会被多次调用,存在被修改的风险
  • 对象状态不稳定:在 setter 方法被调用前,依赖可能处于未初始化状态
  • 时序问题:需要确保在对象使用前所有必要的 setter 方法都被调用

三种注入方式对比总结

特性属性注入构造方法注入setter 注入
代码简洁性★★★★★★★★☆☆★★★★☆
不可变性★☆☆☆☆★★★★★★★☆☆☆
测试友好性★★☆☆☆★★★★★★★★★☆
框架通用性★☆☆☆☆★★★★★★★★★☆
循环依赖处理★★★★☆★☆☆☆☆★★★★☆
spring 官方推荐spring 4.x 之前spring 4.x 之后spring 3.x 推荐

选择建议

  1. 强制依赖:使用构造方法注入,确保依赖在对象创建时就被正确设置
  2. 可选依赖:使用 setter 注入,提供更大的灵活性
  3. 快速原型:可以使用属性注入快速搭建原型,但生产环境建议使用构造方法注入
  4. 不可变对象:如果需要创建不可变对象,必须使用构造方法注入
  5. 测试驱动开发:优先考虑构造方法注入,便于编写单元测试

在实际开发中,spring 官方从 4.x 版本开始推荐使用构造方法注入,因为它能保证依赖的不可变性和完全初始化,同时提高代码的可测试性。但具体选择哪种方式,还需要根据项目的实际需求和团队的编码规范来决定。

4.5 @autowired存在问题

当同一个类型存在多个bean时,使用@autowired会产生问题

为了解决上述问题,spring提供了三种解决方案

  • @primary
  • @qualifier
  • @resource

4.5.1 @primary

4.5.2 @qualifier

4.5.3 @resource

五、小结

感觉这几天有点昼夜颠倒了,好困好困好累好累,怀疑是不是日常没什么运动量导致身体虚虚的。想报个游泳班,但是都好贵啊,最后还是决定不报了,等后面有机会了在报吧。明天吃完火锅后,就开始减肥控糖,一定要瘦瘦瘦

到此这篇关于spring ioc容器与bean管理全解析的文章就介绍到这了,更多相关spring ioc容器与bean管理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com