一、bean的生命周期
所谓生命周期,就是指对象从创建到销毁的整个过程。Spring容器可以管理bean的生命周期,当一个bean被加载到Spring容器中,它就具有了生命,并且Spring在确保bean被使用之前,进行一些其他的操作。
bean的生命周期分为以下几个过程:
(1)通过构造器创建bean实例(无参构造)。
(2)为bean的属性设置值和对其他bean的引用(调用set方法)。
(3)调用bean的初始化方法(需要配置初始化方法——init-method属性)。
(4)使用bean(获取到对象)。
(5)当容器关闭的时候,调用bean的销毁方法(需要配置销毁的方法——destory-method属性)。
接下来,用代码的方式来对上述流程进行验证。
(1)创建Student类。
1)为验证第一步和第二步,需要在无参构造和set方法中加入输出语句。
2)分别定义initMethod()和destoryMethod()方法,并加入输出语句。
public class Student {
private String name;
public Student(){
System.out.println("第一步:调用无参构造创建对象");
}
public void setName(String name) {
System.out.println("第二步:调用set方法注入属性值");
this.name = name;
}
public void initMethod(){
System.out.println("第三步:调用初始化方法");
}
public void destoryMethod(){
System.out.println("第五步:执行完毕,调用销毁方法");
}
}
(2)在bean.xml中进行配置。
<!--
属性说明:
init-method:实体类中初始化的方法。
destroy-method:实体类中销毁的方法
-->
<bean id="student" class="com.yht.example3.entity.Student" init-method="initMethod" destroy-method="destoryMethod">
<property name="name" value="YHT"></property>
</bean>
(3)进行测试。
@Test
public void testLive(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = context.getBean("student", Student.class);
System.out.println("第四步:获取到对象实例 \t" + student);
// ApplicationContext中没有close()方法,而是在子类ClassPathXmlApplicationContext中
context.close();
}
执行结果如下:
以上就是Spring中bean的生命周期的五个过程,除此之外,Spring中bean在初始化前后还有两个过程,我们通过bean的后置处理器来完成。
(1)定义MyBeanPost,实现BeanPostProcessor接口,并覆写其中的方法。
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在初始化之前执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在初始化之后执行");
return bean;
}
}
(2)在bean.xml中配置myBeanPost。
<bean id="myBeanPost" class="com.yht.example3.posthandler.MyBeanPost"></bean>
(3)进行单元测试,得到结果如下:
由此可以得到bean的生命周期的七个步骤:
(1)通过构造器创建bean实例(无参构造)。
(2)为bean的属性设置值和对其他bean的引用(调用set方法)。
(3)在初始化之前执行的操作。
(4)调用bean的初始化方法(需要配置初始化方法——init-method属性)。
(5)在初始化之后执行的操作。
(6)使用bean(获取到对象)。
(7)当容器关闭的时候,调用bean的销毁方法(需要配置销毁的方法——destory-method属性)。
二、bean的作用域
Spring在初始化一个bean的实例时,可以指定该实例的作用域。在Spring中定义了五种作用域,我们常用的是singleton(单例)和prototype(多例),具体介绍如下:
属性 | 说明 |
---|---|
singleton | 单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域 |
prototype | 原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例 |
request | 在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效 |
session | 在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效 |
global session | 在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效 |
1、singleton
singleton 是 Spring 容器默认的作用域,当一个 Bean 的作用域为 singleton 时,Spring 容器中只会存在一个共享的 Bean 实例,并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,就只会返回 Bean 的同一个实例。
在 Spring 配置文件中,可以使用 <bean> 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:
<bean id="teacher" class="com.yht.example3.entity.Teacher" scope="singleton"></bean>
用下面程序发现两次获取得teacher地址一致,这说明此时的teacher就是单例的,全局唯一。
@Test
public void testTeacher(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher1 = context.getBean("teacher", Teacher.class);
Teacher teacher2 = context.getBean("teacher", Teacher.class);
System.out.println("teacher1的地址 : " + teacher1);
System.out.println("teacher2的地址 : " + teacher2);
}
2、prototype
使用 prototype 作用域的 Bean 会在每次请求该 Bean 时都会创建一个新的 Bean 实例。因此对需要保持会话状态的 Bean(如 Structs 2的 Action 类)应该使用 prototype 作用域。将上述例子中scope属性改为prototype,并进行测试,会发现两次得到的结果不一致,这说明对象是多例的。
<bean id="teacher" class="com.yht.example3.entity.Teacher" scope="prototype"></bean>
执行结果如下:
3、singleton和prototype的区别
区别一:singleton是单例,prototype是多例。
区别二:当socpe设置为singleton时,加载Spring配置文件时就会创建单例对象。当socpe设置为prototype时,是在调用getBean时才会创建对象。
三、bean的三种实例化方式
在Spring中,要想使用容器中的Bean,也需要实例化Bean。实例化Bean有三种方式,分别是:构造器实例化、静态工厂实例化、实例工厂方式实例化。构造器实例化是最常用的一种方式,其他方式了解即可。
1、构造器实例化
构造器实例化是指Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean。
(1)创建Teacher类
public class Teacher {
private String name;
private String major;
public void setName(String name) {
this.name = name;
}
public void setMajor(String major) {
this.major = major;
}
public void showMessage(){
System.out.println("教师:" + this.name + ",方向:" + this.major);
}
}
(2)在bean.xml中配置。
<bean id="teacher1" class="com.yht.example3.entity.Teacher">
<property name="name" value="赵茜"></property>
<property name="major" value="网络"></property>
</bean>
(3)进行单元测试。
@Test
public void testCommon(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher = context.getBean("teacher1", Teacher.class);
teacher.showMessage();//教师:赵茜,方向:网络
}
2、静态工厂实例化
(1)创建一个MyStaticFactory类,并创建一个静态方法createObject()来返回Teacher实例。
public class MyStaticFactory {
public static Teacher createObject(){
return new Teacher();
}
}
(2)在bean.xml中进行配置。
<!-- 静态工厂实例化 -->
<!--
class:静态工厂的类。
factory-method:静态工厂中的方法
-->
<bean id="teacher2" class="com.yht.example3.factory.MyStaticFactory" factory-method="createObject">
<property name="name" value="孙飞"></property>
<property name="major" value="人工智能"></property>
</bean>
(3)进行的单元测试。
@Test
public void testStatic(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher = context.getBean("teacher2", Teacher.class);
teacher.showMessage();//教师:孙飞,方向:人工智能
}
3、实例工厂实例化
(1)创建一个MyCommonFactory类,并创建一个方法createObject()来返回Teacher实例。
public class MyCommonFactory {
public Teacher createObject(){
return new Teacher();
}
}
(2)在bean.xml中进行配置。
<!-- 实例工厂实例化 -->
<!--
factory-bean :指向配置的实例工厂
factory-method:静态工厂中的方法
-->
<bean id="teacher3" factory-bean="teacherFactory" factory-method="createObject">
<property name="name" value="郭安"></property>
<property name="major" value="车联网"></property>
</bean>
<!-- 配置工厂 -->
<bean id="teacherFactory" class="com.yht.example3.factory.MyCommonFactory"></bean>
(3)进行的单元测试。
@Test
public void testInstance(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Teacher teacher = context.getBean("teacher3", Teacher.class);
teacher.showMessage();//教师:郭安,方向:车联网
}
4、三种实例化方式的区别
构造器实例化:通过无参构造的方法实例化Bean,其实质是将Bean对应的类交给Spring自带的工厂(BeanFactory)管理,由Spring自带的工厂模式帮我们创建和维护这个类。
静态工厂方式实例化:通过静态工厂创建并返回Bean。其实质是将Bean对应的类交给我们自己的静态工厂管理。Spring只是帮我们调用了静态工厂创建实例的方法。运用:很多时候我们在使用第三方jar包提供给我们的类时,由于这个类没有构造方法,是通过第三方包提供的静态工厂创建的,如果我们想把第三方jar包里面的这个类交由Spring来管理的话,就可以使用Spring提供的静态工厂创建实例的配置。
实例工厂方式实例化:通过实例工厂创建并返回Bean,其实质就是把创建实例的工厂类和调用工厂类的方法创建实例的方法的过程也交由Spring管理,创建实例的这个过程也是由我们自己配置的实例工厂内部实现的。运用:如Spring整合Hibernate就是通过这种方式实现的。但对于没有与Spring整合过的工厂类,我们一般都是自己用代码来管理的。