一、Bean组件的周期与作用域
1.1 Bean组件的生命周期
什么是Bean的周期方法
- 我们可以在组件类中定义方法,然后当IoC容器实例化和销毁组件对象的时候进行调用!这两个方法我们成为生命周期方法!
- 类似于Servlet的init/ service/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
- Bean只有两种周期,init与destory,即初始化和销毁。
1.2 Bean生命周期的代码实现
创建组件类:
package com.alphamilk.Ioc5; public class JavaBean { /* 注意:使用周期方法时候,对应的方法必须满足以下几点 1.无参 2.返回值类型为void */ public void init(){ System.out.println("javaBean被初始化"); } public void destory(){ System.out.println("JavaBean被正常销毁"); } }
创建对应的配置文件xml
<?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"> <!-- 组件创建前面都是一样的,但是需要加上对应的周期方法: 1.init-method 初始化组件方法 2.destroy-method 销毁组件方法 --> <bean id="javaBean" class="com.alphamilk.Ioc5.JavaBean" init-method="init" destroy-method="destory"/> </beans>
测试:
package com.alphamilk.Ioc; import org.junit.jupiter.api.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class newTest { @Test public void test(){ // 创建容器对象 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springioc-5.xml"); /* 注意,如果没有使用close函数,则会出现destroy方法无法调用的情况 因为没有close下,ioc容器会意外死亡,就不会调用destroy方法 */ // 正常销毁组件对象 applicationContext.close(); } }
编辑
2.1 Bean标签的作用域
什么是Bean标签的作用域
一般情况下Bean的作用域对应两种形式,单例和多例模式,以下具体讲解单例与多例。
- 单例:一般创建Bean中默认为单例模式singleton,其意思是在测试类中getBean的时候如果只有一个对应Bean标签,那么哪怕进行多次getBean返回,返回对象都是同一个。
- 多例:指的是在测试类中getBean的时候,在使用getBean方法的时候,虽然只有一个标签,但是每一次调用都会产生一个新的Bean组件对象。
具体创建多少个Bean的实例对象,由Bean的作用域Scope属性指定!一般都推荐用单例模式
2.2 Scope作用域可选值
取值 | 含义 | 创建对象的时机 | 默认值 |
singleton | 在 IOC 容器中,这个 bean 的对象始终为单实例 | IOC 容器初始化时 | 是 |
prototype | 这个 bean 在 IOC 容器中有多个实例 | 获取 bean 时 | 否 |
在WebApplicationContext中较为特殊,其scope多了两个取值
取值 | 含义 | 创建对象的时机 | 默认值 |
request | 请求范围内有效的实例 | 每次请求 | 否 |
session | 会话范围内有效的实例 | 每次会话 | 否 |
2.3 案例代码
创建组件类对象
package com.alphamilk.Ioc5; public class JavaBeanScope { public void doWork(){ System.out.println("JavaBean is Working"); } }
配置对应单例与多例情况下
在默认情况下不需要配置scpoe
编辑
测试代码:
package com.alphamilk.Ioc; import com.alphamilk.Ioc5.JavaBeanScope; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class newTest { @Test public void test(){ // 创建Ioc容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springioc-5.xml"); // 通过容器获取Bean对象 JavaBeanScope bean = applicationContext.getBean(JavaBeanScope.class); JavaBeanScope bean2 = applicationContext.getBean(JavaBeanScope.class); // 结果为False,因为采用多例模式下,每一次getBean,Ioc都会new一个新的JavaBeanScope对象 System.out.println(bean==bean2); } }
编辑
二、FactoryBean接口
1.1 什么是FactoryBean
- 在我们前面文章中提到,实现SpringIoc有两个接口,四个实现类。Factory就是两个接口中的父接口。
- 虽然Ioc容器会自动new一个对象,但是并不是所有的组件都是可以随便就new出来的,比如Mybatis。其最终是要构造Session操作数据库对象,而获取该对象需要先获取配置信息getResource(配置文件),然后构造SessionFactoryBuilder,再调用SessionFactoryBuilder函数Builder,最后获取到的SessionFactory还需要调用方法openSession。这种复杂的创建对象的过程一般的Ioc容器就无法实现。所以我们直接从其根源FactoryBean来设置该如何实例化对象。
修改复杂实例化思路:
编辑
1.2 FactoryBean接口的常用方法
方法名 | 方法描述 |
T getObject() |
返回由工厂创建的对象实例。该返回值将被存储到IoC容器中。 |
boolean isSingleton() |
如果此 FactoryBean 返回单例对象,则返回 true ,否则返回 false 。默认实现返回 true (注意,使用lombok插件可能会影响效果)。 |
Class<?> getObjectType() |
返回 getObject() 方法返回的对象类型。如果在创建之前不知道对象类型,则返回 null 。 |
1.3 实现FactoryBean案例代码
1.创建JavaBean需要实例化的子类
package com.alphamilk.Ioc6; public class JavaBean { private String name; private int age; // 组件方法 public void PrintInfo(){ System.out.println(name+"的年龄是"+age); } }
2.创建JavaBean需要实例化的工厂类
package com.alphamilk.Ioc6; import org.springframework.beans.factory.FactoryBean; //泛型内容<>内填写返回的类型 public class JavaBeanFactory implements FactoryBean<JavaBean> { // 如果子例对象需要DI(依赖注入)则需要进行桥梁连接 private String name; public void setName(String name) { this.name = name; } private int age; public void setAge(int age) { this.age = age; } // 覆写如何实现实例化子类过程 @Override public JavaBean getObject() throws Exception { JavaBean javaBean = new JavaBean(); javaBean.setAge(age); javaBean.setName(name); return javaBean; } // 设置返回的类全定符 @Override public Class<?> getObjectType() { return JavaBean.class; } }
3.配置XML
<?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="javaBeanFactory" class="com.alphamilk.Ioc6.JavaBeanFactory"> <!-- 由于是setter注入,所以用对应的注入方法--> <property name="name" value="黄飞宏"/> <property name="age" value="99"/> </bean> </beans>
4.测试代码
package com.alphamilk.Ioc; import com.alphamilk.Ioc5.JavaBeanScope; import com.alphamilk.Ioc6.JavaBean; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class newTest { @Test public void test(){ // 创建Ioc容器 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springioc-6.xml"); // 获取组件Bean JavaBean bean = (JavaBean) context.getBean("javaBeanFactory"); // 调用Bean方法测试是否有效 bean.PrintInfo(); } }
编辑
总结一下FactoryBean的使用场景:
- 代理类的创建
- 第三方框架整合
- 复杂对象实例化等
容易混淆--面试题 FactoryBean和BeanFactory区别
- **FactoryBean **是 Spring 中一种特殊的 bean,可以在 getObject() 工厂方法自定义的逻辑创建Bean!是一种能够生产其他 Bean 的 Bean。FactoryBean 在容器启动时被创建,而在实际使用时则是通过调用 getObject() 方法来得到其所生产的 Bean。因此,FactoryBean 可以自定义任何所需的初始化逻辑,生产出一些定制化的 bean。
- 一般情况下,整合第三方框架,都是通过定义FactoryBean实现!!!
- BeanFactory 是 Spring 框架的基础,其作为一个顶级接口定义了容器的基本行为,例如管理 bean 的生命周期、配置文件的加载和解析、bean 的装配和依赖注入等。BeanFactory 接口提供了访问 bean 的方式,例如 getBean() 方法获取指定的 bean 实例。它可以从不同的来源(例如 Mysql 数据库、XML 文件、Java 配置类等)获取 bean 定义,并将其转换为 bean 实例。同时,BeanFactory 还包含很多子类(例如,ApplicationContext 接口)提供了额外的强大功能。
- 总的来说,FactoryBean 和 BeanFactory 的区别主要在于前者是用于创建 bean 的接口,它提供了更加灵活的初始化定制功能,而后者是用于管理 bean 的框架基础接口,提供了基本的容器功能和 bean 生命周期管理。