《SpringBoot 3:入门与应用实战》第 3 章 IOC 容器中的 Bean 阅读笔记 1
2026/8/14 1:46:51 网站建设 项目流程

《SpringBoot 3:入门与应用实战》第 3 章 IOC 容器中的 Bean 阅读笔记 1

IOC 容器中存放和管理的大多数是与程序运行相关的 Bean,在 Spring Framework 中对象被统称为 Bean(组件)​,即能被 Spring Framework 的 IOC 容器创建或管理的对象都可以称为 Bean(组件)​。
通过编写 XML 配置文件或注解配置类,将第三方框架的核心 API 注册为 IOC 容器中的 Bean,这些核心 API 对象会在适当的位置发挥其作用,以支撑项目的正常运行。
一个 Bean 的定义中可以包含以下因素:

  • Bean 的 Name​
  • Bean 的所属类/接口信息
  • Bean的作用域,单实例或多实例。
  • Bean 内部的依赖关系,即 Bean 的内部注入了哪些属性和其他 Bean。
  • Bean 的实例化方式,即由构造方法创建,还是由工厂方法创建。
  • Bean 的生命周期,是否包含对象创建后的初始化动作和对象被回收前的销毁动作。

Bean 和对象有什么区别,
在学习 Spring 时经常遇见 Bean 的概念,Bean 和 Java 中的对象有什么区别。
Bean 其实就是 Java 中的对象,在 Spring Framework 中,为了跟 Java new 出的对象区分,把注入到 IOC 容器中的 Java 对象称为 Bean。
Spring Framework 的核心特性之一就是 IOC 控制反转,
把创建和管理对象交给 Spring Framework 的 IOC 的容器,我们直接使用 IOC 容器,不用自己去创建和管理对象。

3.1 Bean的类型

在 Spring Framework 中,Bean 有两种类型:普通 Bean 和工厂 Bean (FactoryBean)。

3.1.1 普通 Bean

普通 Bean 的定义非常好理解,第 2 章中的所有 Bean 都是普通 Bean,包括使用 @Component 注解及其派生注解标注的 Bean,以及使用 标签、@Bean 注解定义的 Bean。

3.1.2 FactoryBean

除了普通的 Bean,Spring Framework 还考虑到特殊情况:某些 Bean 的创建过程比较复杂,使用 XML 配置文件或者注解声明较难甚至无法完成。因而引入了 FactoryBean 机制,借助工厂方法创建 Bean 对象。

1.理解 FactoryBean

FactoryBean 是一个接口,如果某个类实现了 FactoryBean 接口,它将不再是一个普通 Bean,而是一个创建对象的工厂。

2.FactoryBean 的使用

通过简单示例演示 FactoryBean 的使用。假设,小朋友要买玩具,由玩具工厂给小朋友制造玩具。
(1)声明小朋友+玩具
首先声明小朋友类 Child,Child类中添加一个 wantToy 的属性,指代小朋友想买的玩具类别。之后我们再创建一个玩具的抽象类 Toy 和两个具体的子类,球类 Ball 和玩具汽车类 Car。

packagecom.yangjunbo.bean.examplea;publicclassChild{// 当前小朋友想玩球privateStringwantToy="ball";publicStringgetWantToy(){returnwantToy;}}

packagecom.yangjunbo.bean.examplea;publicabstractclassToy{privateStringname;publicToy(Stringname){this.name=name;}}
packagecom.yangjunbo.bean.examplea;publicclassBallextendsToy{// 球publicBall(Stringname){super(name);}}
packagecom.yangjunbo.bean.examplea;publicclassCarextendsToy{// 玩具汽车publicCar(Stringname){super(name);}}

(2)声明玩具工厂
接下来声明一个玩具工厂类 ToyFactoryBean,使其实现 FactoryBean 接口并指定泛型为 Toy。
由需求分析可知,玩具工厂只有知道小朋友想买什么玩具才能对应地生产,从代码逻辑上讲,玩具工厂应当依赖小朋友,由小朋友为它提供玩具类别,由此可知 ToyFactoryBean 中应当注入 Child 对象。
ToyFactoryBean 实现 FactoryBean 接口时需要至少实现两个方法,包含生产目标对象的类型,以及生产的逻辑。

packagecom.yangjunbo.bean.examplea;importorg.springframework.beans.factory.FactoryBean;publicclassToyFactoryBeanimplementsFactoryBean<Toy>{privateChildchild;@OverridepublicToygetObject()throwsException{switch(child.getWantToy()){case"ball":returnnewBall("ball");case"car":returnnewCar("car");default:// Spring Framework 2.0开始允许返回null,之前的1.x版本不允许returnnull;}}@OverridepublicClass<Toy>getObjectType(){returnToy.class;}publicvoidsetChild(Childchild){this.child=child;}}

(3)注册玩具工厂
使用注解配置类 BeanTypeConfiguration 将上述的 Child 和 ToyFactoryBean 注入到 IOC 容器中。

packagecom.yangjunbo.bean.examplea;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassBeanTypeConfiguration{@BeanpublicChildchild(){returnnewChild();}@BeanpublicToyFactoryBeantoyFactory(){ToyFactoryBeantoyFactory=newToyFactoryBean();toyFactory.setChild(child());returntoyFactory;}}

(4)测试效果
编写测试启动类 BeanTypeAnnoApplication,使用 BeanTypeConfiguration 配置类驱动 IOC 容器,并直接从容器中获取 Toy 对象。由于ToyFactoryBean 实现了 FactoryBean 接口,在获取 Toy 对象时会触发 getObject 方法,进而生成一个 Toy 对象。运行 main 方法之后,控制台可以正确打印出 “ball”​,证明 ToyFactoryBean 已经创建出了 Ball 对象。

packagecom.yangjunbo.bean.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassBeanTypeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanTypeConfiguration.class);Toytoy=ctx.getBean(Toy.class);System.out.println(toy);}}

3.FactoryBean 与 Bean 同时存在

如果 IOC 容器中同时存在 FactoryBean 与对应目标类型的 Bean,则最终效果如何?
为了验证该效果,我们在 BeanTypeConfiguration 中使用 @Bean 注解手动注册一个 Ball 对象。接下来不修改 BeanTypeAnnoApplication 启动类,直接运行 main 方法,可以发现控制台抛出 NoUniqueBeanDefinitionException 异常,提示 IOC 容器中有两个 Toy 对象。
由此得出一个结论:FactoryBean 创建的 bean 对象会直接放在 IOC 容器中。
如果修改一下 BeanTypeAnnoApplication 中 main 方法的代码,将获取单个 Toy 对象改为获取全部 Toy 对象。重新运行 main 方法,可以发现控制台打印了两个 toy,通过 ToyFactoryBean 创建出来的 Ball 对象的名称叫 toyFactory。

packagecom.yangjunbo.bean.examplea;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassBeanTypeConfiguration{@BeanpublicChildchild(){returnnewChild();}@BeanpublicToyFactoryBeantoyFactory(){ToyFactoryBeantoyFactory=newToyFactoryBean();toyFactory.setChild(child());returntoyFactory;}@BeanpublicBallball(){returnnewBall("ball");}}


packagecom.yangjunbo.bean.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importjava.util.Map;publicclassBeanTypeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanTypeConfiguration.class);//Toy toy = ctx.getBean(Toy.class);//System.out.println(toy);Map<String,Toy>toys=ctx.getBeansOfType(Toy.class);toys.forEach((name,toy)->{System.out.println("toy name : "+name+", "+toy.toString());});}}

4.取出 FactoryBean 本体

取出 FactoryBean 本体有两种方式:
第一种是使用 FactoryBean 的类型获取,即 ctx.getBean(ToyFactoryBean.class);
第二种是在 Bean 的 id 前面加一个 & 符号,即 ctx.getBean(“&toyFactory”)。

packagecom.yangjunbo.bean.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importjava.util.Map;publicclassBeanTypeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanTypeConfiguration.class);//Toy toy = ctx.getBean(Toy.class);//System.out.println(toy);Map<String,Toy>toys=ctx.getBeansOfType(Toy.class);toys.forEach((name,toy)->{System.out.println("toy name : "+name+", "+toy.toString());});System.out.println(ctx.getBean(ToyFactoryBean.class));System.out.println(ctx.getBean("&toyFactory"));}}

5.FactoryBean 创建 Bean 的实例数

FactoryBean 接口中有一个默认方法 isSingleton,返回值默认为 true,代表默认是单实例的。连续两次获取 Toy 对象并对比内存地址,可以发现是同一个对象。如果在 ToyFactoryBean 中重写 isSingleton 方法并返回 false,再连续两次获取 Toy 对象,内存地址则不相同。

packagecom.yangjunbo.bean.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importjava.util.Map;publicclassBeanTypeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanTypeConfiguration.class);//Toy toy = ctx.getBean(Toy.class);//System.out.println(toy);Map<String,Toy>toys=ctx.getBeansOfType(Toy.class);toys.forEach((name,toy)->{System.out.println("toy name : "+name+", "+toy.toString());});System.out.println(ctx.getBean(ToyFactoryBean.class));System.out.println(ctx.getBean("&toyFactory"));System.out.println(ctx.getBean("toyFactory"));System.out.println(ctx.getBean("toyFactory"));}}

packagecom.yangjunbo.bean.examplea;importorg.springframework.beans.factory.FactoryBean;publicclassToyFactoryBeanimplementsFactoryBean<Toy>{privateChildchild;@OverridepublicToygetObject()throwsException{switch(child.getWantToy()){case"ball":returnnewBall("ball");case"car":returnnewCar("car");default:// Spring Framework 2.0开始允许返回null,之前的1.x版本不允许returnnull;}}@OverridepublicClass<Toy>getObjectType(){returnToy.class;}publicvoidsetChild(Childchild){this.child=child;}@OverridepublicbooleanisSingleton(){returnfalse;}}

6.FactoryBean 创建 Bean 的时机

IOC 容器中的普通 Bean 都是随 IOC 容器的初始化而一并创建的,那么 FactoryBean 与创建的目标对象是否同时创建?我们可以在 Toy 和ToyFactoryBean 的内部声明无参构造方法,并添加控制台打印。
创建一个新的启动类 BeanTypeAnnoApplication,main 方法中只有创建 ApplicationContext 一行代码,运行main方法,可以发现控制台只打印了 ToyFactoryBean 的初始化文字,而 Toy 的初始化文字并没有被打印,由此得出结论:FactoryBean 本身的加载是随 IOC 容器的初始化时机一并创建的。
控制台并没有打印 “生产了一个” + name,说明 FactoryBean 中要创建的 Bean 还没有被加载,**由此也就得出另一个结论:FactoryBean 生产Bean 的机制是延迟生产。**如果在初始化 ApplicationContext 后再获取 Toy 对象,此时会打印“生产了一个 ball”​,印证了刚才的结论。



packagecom.yangjunbo.bean.exampleb;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassBeanTypeAnnoApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(BeanTypeConfiguration.class);ctx.getBean(Toy.class);}}

7.BeanFactory 与 FactoryBean 的区别

BeanFactory:Spring Framework 中实现 IOC 的底层容器​。
FactoryBean:用于创建 Bean 的工厂 Bean,适合用来创建一些初始化流程比较复杂的 Bean。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询