七、Bean的实例化方式
Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)
- 第一种:通过构造方法实例化
- 第二种:通过简单工厂模式实例化
- 第三种:通过factory-bean实例化
- 第四种:通过FactoryBean接口实例化
7.1 通过构造方法实例化
我们之前一直使用的就是这种方式。默认情况下,会调用Bean的无参数构造方法。
package com.powernode.spring6.bean;public class User {public User() {System.out.println("User类的无参数构造方法执行。");}
}
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userBean" class="com.powernode.spring6.bean.User"/></beans>
package com.powernode.spring6.test;import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringInstantiationTest {@Testpublic void testConstructor(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");User user = applicationContext.getBean("userBean", User.class);System.out.println(user);}
}
执行结果:
- 控制台输出:User类的无参数构造方法执行。
- 输出User对象的地址信息
7.2 通过简单工厂模式实例化
第一步:定义一个Bean
package com.powernode.spring6.bean;public class Vip {
}
第二步:编写简单工厂模式当中的工厂类(简单工厂是使用静态方法)
package com.powernode.spring6.bean;public class VipFactory {public static Vip get(){return new Vip();}
}
第三步:在Spring配置文件中指定创建该Bean的方法(使用factory-method
属性指定)
<bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSimpleFactory(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Vip vip = applicationContext.getBean("vipBean", Vip.class);System.out.println(vip);
}
执行结果:
- 控制台输出Vip对象的地址信息
7.3 通过factory-bean实例化
这种方式本质上是:通过工厂方法模式进行实例化。
第一步:定义一个Bean
package com.powernode.spring6.bean;public class Order {
}
第二步:定义具体工厂类,工厂类中定义实例方法
package com.powernode.spring6.bean;public class OrderFactory {public Order get(){return new Order();}
}
第三步:在Spring配置文件中指定factory-bean
以及factory-method
<bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/>
<bean id="orderBean" factory-bean="orderFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSelfFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Order orderBean = applicationContext.getBean("orderBean", Order.class);System.out.println(orderBean);
}
执行结果:
- 控制台输出Order对象的地址信息
7.4 通过FactoryBean接口实例化
以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()
方法。
第一步:定义一个Bean
package com.powernode.spring6.bean;public class Person {
}
第二步:编写一个类实现FactoryBean
接口
package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;public class PersonFactoryBean implements FactoryBean<Person> {@Overridepublic Person getObject() throws Exception {// 这里负责对象的创建System.out.println("PersonFactoryBean's getObject executed.");return new Person();}@Overridepublic Class<?> getObjectType() {// 返回创建的Bean的类型return Person.class;}@Overridepublic boolean isSingleton() {// true表示单例// false表示原型 (每次调用 getBean() 都产生新对象)return true;}
}
第三步:在Spring配置文件中配置FactoryBean
<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>
测试程序:
@Test
public void testFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");// 通过Spring容器获取到的实际上是 getObject() 方法返回的对象Person personBean = applicationContext.getBean("personBean", Person.class);System.out.println(personBean);Person personBean2 = applicationContext.getBean("personBean", Person.class);System.out.println(personBean2);// 如果想获取 PersonFactoryBean 本身,需要在ID前加 '&'// Object factoryBean = applicationContext.getBean("&personBean");// System.out.println(factoryBean);
}
执行结果:
- 控制台输出:PersonFactoryBean’s getObject executed.
- 控制台输出两个Person对象的地址信息,由于isSingleton()返回true,两个对象地址相同
7.5 BeanFactory和FactoryBean的区别
7.5.1 BeanFactory
Spring IoC容器的顶级接口,BeanFactory
被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责管理(包括创建、配置、销毁等)Bean对象。
BeanFactory
是Spring IoC容器的核心,是真正的工厂。
7.5.2 FactoryBean
FactoryBean
:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个特殊的Bean。
在Spring中,Bean可以分为两类:
- 第一类:普通Bean (我们自己定义的业务类、组件等)
- 第二类:工厂Bean (实现了
FactoryBean
接口的Bean,用于创建其他Bean)
记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。当从容器中获取工厂Bean时(例如getBean("personBean")
),默认情况下获取到的是它getObject()
方法返回的对象,而不是工厂Bean本身。
7.6 使用FactoryBean注入自定义格式的Date
我们前面说过,java.util.Date
在Spring中虽然可以被当做简单类型,使用value
属性或<value>
标签注入字符串,但Spring默认的PropertyEditor
对日期字符串的格式要求非常严格,必须是toString()
方法的默认输出格式(例如:Mon Oct 10 14:30:26 CST 2022
)。其他自定义格式(如yyyy-MM-dd
)直接使用value
注入是无法识别的。
示例:定义Student类
package com.powernode.spring6.bean;import java.util.Date;public class Student {private Date birth;// Setter用于属性注入public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "Student{" +"birth=" + birth +'}';}
}
尝试使用标准格式注入:
<bean id="studentBean" class="com.powernode.spring6.bean.Student"><!-- Spring 默认可以识别这种格式 --><property name="birth" value="Mon Oct 10 14:30:26 CST 2002"/>
</bean>
测试:
@Test
public void testDateDefaultFormat(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Student studentBean = applicationContext.getBean("studentBean", Student.class);System.out.println(studentBean);
}
执行结果:
- 控制台输出Student对象信息,包含正确格式化的日期
Mon Oct 10 14:30:26 CST 2002
这种情况下,我们还可以使用FactoryBean来完成这个操作。
编写DateFactoryBean实现FactoryBean接口:
package com.powernode.spring6.bean;import org.springframework.beans.factory.FactoryBean;import java.text.SimpleDateFormat;
import java.util.Date;public class DateFactoryBean implements FactoryBean<Date> {// 可配置的日期格式private String strDate;public DateFactoryBean(String strDate) {this.strDate = strDate;}@Overridepublic Date getObject() throws Exception {// 格式化日期对象SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.parse(strDate);}@Overridepublic Class<?> getObjectType() {return null;}
}
在Spring配置文件中配置DateFactoryBean
并注入格式:
<!-- 配置 DateFactoryBean,并通过构造函数注入日期格式 -->
<bean id="dateFactory" class="com.powernode.spring6.bean.DateFactoryBean"><constructor-arg name="dateFormat" value="yyyy-MM-dd"/>
</bean><!-- 配置 Student Bean,并注入由 DateFactoryBean 创建的 Date 对象 -->
<bean id="studentBeanCustomDate" class="com.powernode.spring6.bean.Student"><!-- 注意这里 ref 指向的是 FactoryBean 的 ID --><!-- 通过工厂Bean:DateFactoryBean 来返回普通Bean:java.util.Date --><bean id="date" class="com.powernode.spring6.bean.DateFactoryBean"><constructor-arg name="strDate" value="2003-7-10"/></bean><bean id="student" class="com.powernode.spring.bean.Student"><property name="birth" ref="date"/></bean></bean>
</beans>
</bean>
编写测试程序:
@Test
public void testDateFactoryBean(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");Student studentBean = applicationContext.getBean("studentBeanCustomDate", Student.class);// 输出的日期会是当前日期,因为 DateFactoryBean 的 getObject 直接返回 new Date()// 但关键在于,可以通过 FactoryBean 控制创建过程和类型System.out.println(studentBean);
}
执行结果:
- 控制台输出Student对象信息,其birth属性将是由
DateFactoryBean
根据yyyy-MM-dd
格式(虽然示例中直接返回new Date()
,但原理是这样)创建的Date对象。
通过FactoryBean
,我们可以灵活地控制复杂对象的创建过程,例如根据配置的格式字符串来创建Date
对象,解决了Spring默认PropertyEditor
无法处理自定义日期格式的问题。
八、Bean的生命周期
8.1 什么是Bean的生命周期
Spring框架作为一个IoC容器,本质上是一个高级对象工厂,它完全接管了Bean对象的创建、配置、初始化、使用和销毁的完整生命周期。所谓Bean的生命周期,就是指一个Bean对象从被Spring容器实例化开始,到最终被销毁的整个过程。
在深入理解Spring的Bean生命周期时,需要关注以下关键问题:
- Bean对象何时被实例化?(对应构造方法的调用)
- 依赖注入在何时完成?(属性赋值的时机)
- 哪些初始化操作会在Bean就绪前执行?(初始化回调)
- Bean对象何时可以被应用程序使用?
- Bean对象何时被销毁?(容器关闭时机)
- 销毁前会执行哪些清理操作?(销毁回调)
8.2 为什么要了解Bean的生命周期
掌握Bean生命周期的核心意义在于:了解特定时间节点上Spring容器调用了哪些类的哪些方法,这些知识对开发者具有以下价值:
- 自定义初始化逻辑:在Bean初始化阶段执行资源加载、连接建立等操作
- 实现优雅关闭:在Bean销毁阶段释放资源、关闭连接
- 解决依赖问题:理解依赖注入的精确时机,解决复杂依赖关系
- 开发自定义扩展点:编写自定义的BeanPostProcessor等组件
- 调试和排错:理解Bean创建失败的原因和位置
8.3 Bean生命周期的5个基本阶段
根据Spring核心源码(AbstractAutowireCapableBeanFactory类的doCreateBean()方法实现),Bean的标准生命周期可以概括为5个主要阶段:
- 实例化Bean:调用构造方法创建对象实例,分配内存空间
- 属性注入:填充属性值,完成依赖注入(DI)
- 初始化Bean:执行自定义初始化方法,准备Bean投入使用
- 使用Bean:Bean可被应用程序正常使用
- 销毁Bean:容器关闭时执行自定义销毁方法,释放资源
示例代码
public class User {private String name;// 第一步:实例化Bean(构造方法)public User() {System.out.println("1.实例化Bean - 构造方法执行");}// 第二步:Bean属性赋值(依赖注入)public void setName(String name) {this.name = name;System.out.println("2.Bean属性赋值 - 依赖注入");}// 第三步:初始化Bean(初始化方法)public void initBean() {System.out.println("3.初始化Bean - 执行自定义初始化方法");}// 第五步:销毁Bean(销毁方法)public void destroyBean() {System.out.println("5.销毁Bean - 执行自定义销毁方法");}
}
XML配置详解
<bean id="userBean" class="com.powernode.spring6.bean.User" init-method="initBean" <!-- 指定初始化方法 -->destroy-method="destroyBean" <!-- 指定销毁方法 -->lazy-init="false"> <!-- 是否延迟初始化,默认false --><property name="name" value="zhangsan"/> <!-- 属性注入 -->
</bean>
测试代码与执行流程
public class BeanLifecycleTest {@Testpublic void testLifecycle() {// 创建并初始化Spring容器,加载配置,实例化并初始化BeanApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");// 从容器获取Bean实例(单例Bean在容器启动时已创建完毕)User userBean = applicationContext.getBean("userBean", User.class);// 第四步:使用BeanSystem.out.println("4.使用Bean - " + userBean.getName());// 关闭容器,触发Bean销毁过程// 注意:只有正常关闭spring容器才会执行销毁方法ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;context.close();}
}
关键注意事项
- 销毁方法的调用条件:只有通过
context.close()
或context.registerShutdownHook()
正常关闭Spring容器时,Bean的销毁方法才会被调用 - 容器关闭方法:只有
ConfigurableApplicationContext
的实现类(如ClassPathXmlApplicationContext
)才提供close()
方法 - 方法指定:在XML配置中通过
init-method
属性指定初始化方法,destroy-method
属性指定销毁方法 - 作用域影响:对于prototype作用域的Bean,Spring不会调用其销毁方法
8.4 Bean生命周期的7个阶段(加入Bean后处理器)
Spring提供了强大的扩展机制,通过实现BeanPostProcessor
接口,可以在Bean初始化前后插入自定义逻辑,将生命周期扩展为7个阶段:
- 实例化Bean:创建Bean实例
- 属性赋值:依赖注入
- BeanPostProcessor前置处理:执行
postProcessBeforeInitialization
方法 - 初始化Bean:执行初始化方法
- BeanPostProcessor后置处理:执行
postProcessAfterInitialization
方法 - 使用Bean:应用程序使用Bean
- 销毁Bean:执行销毁方法
Bean后处理器详解
/*** Bean后处理器示例 - 在初始化前后添加自定义逻辑*/
public class LogBeanPostProcessor implements BeanPostProcessor {/*** 在Bean初始化方法之前执行* @param bean 原始Bean实例* @param beanName Bean的名称* @return 处理后的Bean实例(可以是原始Bean或包装过的Bean)*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的before方法执行:" + beanName + " 即将初始化");// 这里可以修改Bean属性、添加通用逻辑等return bean; // 返回原始Bean或修改后的Bean}/*** 在Bean初始化方法之后执行* @param bean 已初始化的Bean实例* @param beanName Bean的名称* @return 最终的Bean实例(可能是原始Bean或包装过的Bean)*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后处理器的after方法执行:" + beanName + " 已完成初始化");// 这里可以创建代理、添加额外功能等return bean; // 返回原始Bean或包装后的Bean}
}
配置Bean后处理器
<!-- 配置Bean后处理器 - 无需指定id属性这个后处理器将作用于当前配置文件中所有的bean
-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
Bean后处理器的应用场景
- AOP实现:Spring AOP就是通过BeanPostProcessor和动态代理实现的
- 注解处理:处理Bean上的注解,如
@Autowired
、@Value
等 - 自定义检查:验证Bean的必要属性是否设置
- 包装增强:给Bean添加额外功能,如缓存、事务等
8.5 Bean生命周期的10个阶段(完整生命周期)
通过Spring源码分析,一个Bean的完整生命周期实际上可以更精细地划分为10个阶段:
- 实例化Bean:调用构造方法创建对象
- 属性赋值:进行依赖注入
- 执行Aware接口方法:如果Bean实现了相关Aware接口,调用对应方法
BeanNameAware.setBeanName()
:注入Bean的名称BeanClassLoaderAware.setBeanClassLoader()
:注入类加载器BeanFactoryAware.setBeanFactory()
:注入BeanFactory容器
- BeanPostProcessor前置处理:调用
postProcessBeforeInitialization
- InitializingBean初始化:如果Bean实现了
InitializingBean
接口,调用afterPropertiesSet()
方法 - 自定义初始化方法:调用
init-method
指定的方法 - BeanPostProcessor后置处理:调用
postProcessAfterInitialization
- 使用Bean:Bean可以被应用程序使用
- DisposableBean销毁:如果Bean实现了
DisposableBean
接口,调用destroy()
方法 - 自定义销毁方法:调用
destroy-method
指定的方法
实现10步生命周期的完整Bean
public class CompleteLifecycleBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {private String property;// 步骤1: 实例化Beanpublic CompleteLifecycleBean() {System.out.println("1. 实例化Bean: 构造方法执行");}// 步骤2: 属性赋值public void setProperty(String property) {this.property = property;System.out.println("2. 属性赋值: " + property);}// 步骤3: Aware方法 - 注入Bean名称@Overridepublic void setBeanName(String name) {System.out.println("3. Aware接口: 注入Bean名称 - " + name);}// 步骤3: Aware方法 - 注入ClassLoader@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {System.out.println("3. Aware接口: 注入ClassLoader");}// 步骤3: Aware方法 - 注入BeanFactory@Overridepublic void setBeanFactory(BeanFactory beanFactory) {System.out.println("3. Aware接口: 注入BeanFactory");}// 步骤5: InitializingBean接口方法@Overridepublic void afterPropertiesSet() {System.out.println("5. InitializingBean: 执行afterPropertiesSet方法");}// 步骤6: 自定义初始化方法public void customInit() {System.out.println("6. 自定义初始化: 执行init-method指定的方法");}// 步骤9: DisposableBean接口方法@Overridepublic void destroy() {System.out.println("9. DisposableBean: 执行destroy方法");}// 步骤10: 自定义销毁方法public void customDestroy() {System.out.println("10. 自定义销毁: 执行destroy-method指定的方法");}
}
执行顺序的关键发现
- 初始化顺序:
InitializingBean.afterPropertiesSet()
方法先执行,然后才是init-method
指定的自定义初始化方法 - 销毁顺序:
DisposableBean.destroy()
方法先执行,然后才是destroy-method
指定的自定义销毁方法 - Aware接口顺序:通常按照
BeanNameAware
→BeanClassLoaderAware
→BeanFactoryAware
的顺序执行
8.6 不同作用域Bean的生命周期管理差异
Spring对不同作用域的Bean采用不同的生命周期管理策略,这直接影响到开发者如何处理资源释放:
1. singleton作用域(默认)
-
特点:
- 每个Spring容器只创建一个Bean实例
- Spring容器负责完整的生命周期管理
- 容器启动时创建,容器关闭时销毁
-
生命周期管理:
- 完整经历从实例化到销毁的10个阶段
- 销毁方法在容器关闭时自动调用
- 适合共享状态且线程安全的对象
2. prototype作用域
-
特点:
- 每次请求都创建新的Bean实例
- Spring容器只负责创建,不负责销毁
- 创建后交由客户端代码管理
-
生命周期管理:
- 只经历从实例化到初始化后的前7个阶段
- 销毁方法不会被Spring容器调用
- 需要客户端代码手动清理资源
- 适合存储请求相关状态的对象
示例配置
<!-- 单例Bean配置(默认) -->
<bean id="singletonBean" class="com.example.SingletonBean"init-method="init" destroy-method="destroy">
</bean><!-- 原型Bean配置 -->
<bean id="prototypeBean" class="com.example.PrototypeBean"scope="prototype" init-method="init" destroy-method="destroy">
</bean>
原型Bean资源释放解决方案
对于prototype作用域的Bean,如果需要在不使用时释放资源,可以:
- 使用自定义的BeanPostProcessor跟踪创建的prototype Bean
- 实现类似Java中try-with-resources的模式自行管理资源
- 对于重量级资源,考虑改用单例模式并确保线程安全
8.7 手动注册Bean到Spring容器
在某些场景下,需要将手动创建的对象或第三方库的对象纳入Spring容器管理,可以使用Spring提供的Bean注册API:
/*** 手动将已创建的对象注册到Spring容器示例*/
public class ManualBeanRegistrationExample {@Testpublic void testManualBeanRegistration() {// 1. 手动创建对象User user = new User();user.setName("手动创建的用户");// 2. 创建可编程的BeanFactoryDefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 3. 将对象注册为单例Beanfactory.registerSingleton("userBean", user);// 4. 从Spring容器获取BeanUser retrievedBean = factory.getBean("userBean", User.class);// 5. 验证是同一个对象System.out.println("是否为同一对象: " + (user == retrievedBean));}
}
编程式Bean注册的应用场景
- 集成第三方库:将无法通过Spring自动管理的组件纳入容器
- 动态Bean创建:根据运行时条件创建不同的Bean实现
- 测试环境:在单元测试中注册Mock对象
- 插件系统:动态加载和注册插件组件
其他注册方式
除了registerSingleton
外,Spring还提供了多种注册Bean的方式:
// 注册Bean定义
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.example.MyBean");
factory.registerBeanDefinition("myBean", beanDefinition);// 通过FactoryBean注册
factory.registerSingleton("myFactoryBean", new MyFactoryBean());