IOC 操作 Bean 管理(FactoryBean)
1、Spring 有两种类型的 bean ,一种普通 bean,另外一种工厂 bean(FactoryBean)。
2、普通 bean:在配置文件中,定义bean 类型就是返回值类型
class 定义的是什么类型,返回的就是什么类型。比如:
<!-- 定义的是 Book 类型,返回的就是 Book 类型 -->
<bean id="book" class="com.tt.stu.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
3、工厂 bean:在配置文件中,定义的 bean 类型可以和返回类型不一样。
a. 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
public class MyBean implements FactoryBean<Course> {
// 定义返回 bean 类型
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCourseName("abc");
// 返回改成了 Course 类型对象,而不是 MyBean 类型对象了
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
b. 配置文件
<bean id="myBean" class="com.tt.stu.factorybean.MyBean"></bean>
c.使用
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");
Course myBean = context.getBean("myBean", Course.class); // 传入的是要返回类型的 Class
System.out.println(myBean);
}
控制台输出结果:
Course{courseName='abc'}
工厂 Bean 和工厂模式相似,在上面这个案例中,我们为了得到 Course 对象并不是直接通过配置文件配置 Course 对象来创建对象。而是通过一个类间接的返回 Course 对象。
Bean 作用域
1、在 Spring 里面,可以设置 bean 实例是单实例还是多实例。
2、在 Sprng 里面,默认情况下,创建的 bean 是单实例对象。
在之前的案例中,我们都是用的单实例的,只创建一个对象,且只有一个实例的对象,我们不妨来验证一下。
修改之前的 Book 类的测试代码,如下:
@Test
public void testCollection2() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");
// 我们通过 context 获取两次对象,通过观察对象的哈希值,看看这两个对象是否是同一个对象。
Book book1 = context.getBean("book", Book.class);
Book book2 = context.getBean("book", Book.class);
System.out.println(book1.hashCode());
System.out.println(book2.hashCode());
}
控制台打印结果如下:
1824872646
1824872646
所以两次获取的是同一个对象。
3、如何设置单实例还是多实例。
(1)在 spring 配置文件 bean 标签里面有一个属性(scope)用于设置单实例还是多实例。
(2)scope 属性值
- singleton (默认值):表示是单实例对象。
- prototype:表示是多实例对象。
我们来修改一下 bean 里面的这个属性,看看修改后,获取的 book 是否为同一个对象,修改如下:
<bean id="book" class="com.tt.stu.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
修改后再次执行代码,控制台打印如下:
1824872646
359742806
可以发现这个时候对象不是同一个对象了。
(3)singleton 和 prototype 的区别
第一:singleton 单实例, prototype 多实例
第二:设置 scope 的值为 singleton 的时候,加载 spring 配置文件时就会创建单实例对象。
设置 scope 的值为 prototype 的时候,不是在加载 spring 配置文件的时候创建对象,而是在调用 getBean 方法时候创建多实例对象。
另外,scope 的值还有两个,一个是 request,一个是 session,request 和 session 是 JavaWeb 里面的域对象,如果设置为他们就会把该对象放到相应的域对象中。
Bean 生命周期
1、生命周期
(1)从对象创建到对象销毁的过程
2、bean 的生命周期
(1)通过构造器创建 bean 实例(无参构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用相应的 setter 方法)
(3)调用 bean 的初始化的方法(需要进行配置)
(4)bean 可以使用了(获取对象,使用方法)
(5)当容器关闭的时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)。
3、演示 bean 生命周期
a. 创建类,并且在类中声明各个声明周期的方法。
public class Orders {
// 无参构造
public Orders() {
System.out.println("第一步 执行无参数构造创建 bean 实例");
}
private String orderName;
public void setOrderName(String orderName) {
this.orderName = orderName;
System.out.println("第二步 调用 setter 方法设置属性值");
}
// 创建执行的初始化的方法 ,需要自己定义并且在配置文件中配置
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
// 创建执行的销毁的方法,需要自己创建,并在配置文件配置
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
@Override
public String toString() {
return "Orders{" +
"orderName='" + orderName + '\'' +
'}';
}
}
b. 配置配置文件
<!-- bean9.xml -->
<bean id="orders" class="com.tt.stu.bean.Orders"
init-method="initMethod" <!-- 设置初始化方法 -->
destroy-method="destroyMethod"> <!-- 设置销毁方法 -->
<property name="orderName" value="手机"></property>
</bean>
c. 使用
@Test
public void testBean3() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean9.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步获取创建 bean 实例对象:" + orders);
// 手动让 bean 实例销毁,使其调用销毁方法
// ClassPathXmlApplicationContext 中有 close 方法可以销毁 bean
((ClassPathXmlApplicationContext) context).close();
// 这里也可也直接使用 ClassPathXmlApplicationContext 接收 context,就不用强转了。
}
控制台打印结果如下:
第一步 执行无参数构造创建 bean 实例
第二步 调用 setter 方法设置属性值
第三步 执行初始化的方法
第四步获取创建 bean 实例对象:Orders{orderName='手机'}
第五步 执行销毁的方法
可以看出来每个生命周期是在什么地方执行。
4、bean 的后置处理器,bean 的生命周期变成 7 步
(1)通过构造器创建 bean 实例(无参构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用相应的 setter 方法)
(3)把 bean 实例传递给 bean 后置处理器的方法。 — postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置)
(5)把 bean 实例传递给 bean 后置处理器的方法 — postProcessAfterInitialization
(6)bean 可以使用了(获取对象,使用方法)
(7)当容器关闭的时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)。
5、演示添加后置处理器效果
(1)创建类,实现接口 BeanPostProcessor,创建后置处理器
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
(2)配置文件配置,我们将后置处理器加在上面 Orders 案例配置里,让 Orders 拥有后置处理器。
<!-- ... -->
<!-- bean9.xml -->
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.tt.stu.bean.MyBeanPost"></bean>
(3)使用,我们再次执行上面的测试代码,观察输出变化
控制台输出结果如下:
第一步 执行无参数构造创建 bean 实例
第二步x 调用 setter 方法设置属性值
在初始化之前执行的方法
第三步 执行初始化的方法
在初始化之后执行的方法
第四步获取创建 bean 实例对象:Orders{orderName='手机'}
第五步 执行销毁的方法
很明显在第三步前后多了两个方法。