Spring Bean是什么?
在 Spring 中,Bean 是一个由 Spring IoC(控制反转)容器管理的对象。Bean 通常通过在 XML 配置文件、Java 配置类或基于注解的方式定义,并由容器负责创建和配置。Spring 容器负责管理 Bean 的整个生命周期,从创建、配置到销毁。这种管理包括依赖注入、生命周期回调等。
Bean别名
<bean id="bookDao" class="space.mora.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service service2" class="space.mora.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/> <!--配置依赖 name是指class中的属性名称,ref是指依赖bean的id -->
</bean>
<bean>
标签的name
属性可以用来指定bean的别名,别名之间可以使用逗号、分号、空格进行分割,定义的别名可以用于API获取bean,以及XML中其他地方对该bean进行引用。
Bean非单例(配置控制范围)
Spring通过<bean>
标签声明的bean默认为单例,即只创建一个对象。
- 为什么Bean默认为单例?
答:限制Bean创建实例的数量。并且单例作用域下,Bean可以在ApplicationContext被创建之后就进行预创建,此时依赖或声明配置异常可以提前抛出,而不是等程序运行一段时间之后,再在创建Bean对象时抛出。(有点类似于fast-fail的理念) - 适合交给容器进行管理的bean
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
- 不适合交给容器管理的bean
- 封装实体的域对象
如何创建以非单例的形式来创建bean?
<bean>
标签的scope
属性,设置为prototype
(默认为singleton
),若如此,创建的bean每次getBean时拿到的是不同的对象。
Bean对象创建时机
默认情况下,所有bean在IOC容器启动之后即被创建。
但是,当<bean>
的属性lazy-init
被设置为true
(启用懒加载),或者scope
被设置为prototype
时(非单例),这些对象只有被使用到的时候才会创建ctx.getBean(...)
。
Bean实例化的三种形式
-
使用无参构造
直接使用普通的<bean>
标签创建时,使用无参构造函数创建类实例。且无论该无参构造的访问控制属性,即便是private也可成功调用,这是因为底层实现借助了反射
机制。请注意必须提供无参构造,否则抛BeanCreationException
。 -
使用静态工厂
<bean>
标签的class属性指向静态工厂类,额外配置factory-method
属性说明工厂方法名即可。
<bean id="orderDao2" class="space.mora.factory.OrderDaoFactory" factory-method="getOrderDao"/>
- 使用实例工厂
在配置中,先创建实例工厂Bean,再创建目标Bean实例。
<bean id="userDaoFactory" class="space.mora.factory.UserDaoFactory"/>
<bean id="userDao1" factory-bean="userDaoFactory" factory-method="getUserDao"/>
- 实例工厂模式简化
工厂类实现FactoryBean
接口,可一步到位完成配置。该接口配置如下:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception; // 创建对象并返回
@Nullable
Class<?> getObjectType(); // 返回被创建对象的类class
default boolean isSingleton() {
return true; // 控制是否单例
}
}
Tips
: 容器只负责管理 FactoryBean 实例的生命周期,而不负责管理由 FactoryBean 创建的对象的生命周期。因此,不会自动调用公开的 bean 对象(例如 Closeable.close ())上的销毁方法。相反,FactoryBean 应该实现 DisposableBean,并将任何此类关闭调用委托给底层对象。
使用FactoryBean
后,XML中的<bean>
标签仅需用class指定实现的FactoryBean
非抽象类即可。
<bean id="userDao2" class="space.mora.factory.UserDaoFactoryBean"/>
注意:使用普通实例工厂,Bean实例化默认在IOC容器启动时;而使用FactoryBean
实例化Bean时,默认在ctx.getBean
进而调用FactoryBean#getObject()
时,即属于懒加载模式。
Tips
: 当然具体看你FactoryBean
接口怎么实现,如果你在接口类中声明静态实例,在getObject时返回该静态实例,那么此种情况下,也相当于容器启动时创建。
Bean生命周期控制
bean生命周期如下:
-
- 创建对象(分配内存)
-
- 执行构造方法
-
- 执行依赖注入(set操作)
-
- 执行生命周期回调中的
init-method
- 执行生命周期回调中的
-
- 当容器被关闭或销毁时,执行
destory-method
- 当容器被关闭或销毁时,执行
我们可以借助Bean的生命周期回调来控制Bean在属性设置完成以及销毁前的行为。主要有以下两种形式:
- XML配置
通过<bean>
标签的init-method
属性和destory-method
属性,指定初始化回调和销毁回调。需要注意的是,销毁回调函数只有当ClassPathXmlApplicationContext
的close
方法被调用时,才会进行回调。而通常情况下,我们引用容器实例的ApplicationContext
属于上层接口,没有close
方法。
因此,想要销毁回调正常被调用的话,首先需要用ClassPathXmlApplicationContext
去引用容器实例,然后
- 要么在最后调用
close
关闭容器 - 要么在创建容器之后,调用
registerShutdownHook
方法注册销毁回调钩子
。
- 实现Spring提供的接口
除了使用钩子外,还可以实现Spring的生命周期回调接口中的方法配置回调。
实例化回调可以实现InitializingBean
接口中的afterPropertiesSet
方法。
销毁回调可以实现DisposableBean
接口中的destroy
方法。
当然,这种方式配置的回调仍然需要关闭容器或注册销毁钩子。
Tips
: Spring官方文档并不建议用这种方式,因为这会破坏Spring的非侵入特性。