目录
一、BeanFactory
BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂,我们可以通过它获取工厂管理的对象。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。它定义了getBean()、containsBean()等管理Bean的通用方法。但BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 :
- DefaultListableBeanFactory
- XmlBeanFactory
- ApplicationContext
其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
1.1 源码
-
public
interface BeanFactory {
-
/**
-
用于区分factoryBean和bean,后面会讲到
-
/*String FACTORY_BEAN_PREFIX = "&";
-
-
/**
-
返回byName返回bean的实例
-
*/
-
Object getBean(String name) throws BeansException;
-
-
<T>
T getBean(String name, Class<T> requiredType) throws BeansException;
-
-
Object getBean(String name, Object... args) throws BeansException;
-
-
<T>
T getBean(Class<T> requiredType) throws BeansException;
-
-
<T>
T getBean(Class<T> requiredType, Object... args) throws BeansException;
-
-
-
/**
-
* Return a provider for the specified bean, allowing for lazy on-demand retrieval
-
* of instances, including availability and uniqueness options.
-
* @param requiredType type the bean must match; can be an interface or superclass
-
* @return a corresponding provider handle
-
* @since 5.1
-
* @see #getBeanProvider(ResolvableType)
-
*/
-
-
<T>
ObjectProvider<T> getBeanProvider(Class<T> requiredType);
-
-
<T>
ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
-
-
/**
-
判断工厂中是否包含给定名称的bean定义,若有则返回true
-
*/
-
boolean containsBean(String name);
-
-
/**
-
判断bean是否为单例
-
*/
-
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
-
-
/**
-
判断bean是否为多例
-
*/
-
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
-
-
/**
-
检查具有给定名称的bean是否匹配指定的类型。
-
*/
-
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
-
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
-
-
/**
-
返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除*/
-
@Nullable
-
Class<?> getType(String name)
throws NoSuchBeanDefinitionException;
-
-
/**
-
返回给定bean名称的所有别名
-
*/
-
String[] getAliases(String name);
-
}
1.2 使用场景
- 从Ioc容器中获取Bean(byName or byType)
- 检索Ioc容器中是否包含指定的Bean
- 判断Bean是否为单例
二、FactoryBean
使用XML配置spring容器的时候,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,比如一个类大量依赖了其他的对象属性,此时就算是使用自动装配,不需要再显式的写出bean之间的依赖关系,但是其依赖的对象也需要将其装配到spring容器中,也需要为它所依赖的多有对象都创建bean标签将他们注入,如果这个类依赖了上百个对象,那么这个工作量无疑是非常大的。
Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式。
Spring中共有两种bean,一种为普通bean,另一种则为工厂bean
以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。
2.1 源码
-
public
interface FactoryBean<T> {
-
//从工厂中获取bean
-
@Nullable
-
T getObject() throws Exception;
-
-
//获取Bean工厂创建的对象的类型
-
@Nullable
-
Class<?> getObjectType();
-
-
//Bean工厂创建的对象是否是单例模式
-
default boolean isSingleton() {
-
return
true;
-
}
-
}
从它定义的接口可以看出,FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上'&'符号。
- getObject('name')返回工厂中的实例
- getObject('&name')返回工厂本身的实例
通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。
2.2 示例
我们现在要将下面这个TempDaoFactoryBean类交给工厂去创建管理
-
public
class TempDaoFactoryBean {
-
private String msg1;
-
private String msg2;
-
private String msg3;
-
-
public void test() {
-
System.out.println(
"FactoryBean");
-
}
-
public void setMsg1(String msg1) {
-
this.msg1 = msg1;
-
}
-
public void setMsg2(String msg2) {
-
this.msg2 = msg2;
-
}
-
public void setMsg3(String msg3) {
-
this.msg3 = msg3;
-
}
-
public String getMsg1() {
-
return msg1;
-
}
-
public String getMsg2() {
-
return msg2;
-
}
-
public String getMsg3() {
-
return msg3;
-
}
-
}
我们有两种方法可以选择:
方法一:通过spring的xml的方式对其进行配置.
方法二:定义一个CarProxy类,实现factoryBean接口.
2.2.1 方法一
如果使用传统方式配置下面Car的<bean>时,Car的每个属性分别对应一个<property>元素标签,就算是使用自动装配也要写很多<bean>标签,十分的麻烦
2.2.2 方法二
定义DaoFactoryBean实现FactoryBean接口
-
/**
-
* FactoryBean由名字可以看出,是以bean结尾的,就说明这是一个bean,是由IOC容器管理的一个bean对象
-
*
-
* 如果你的类实现了FactoryBean
-
* 那么spring容器当中会存储两个对象:一个是getObject()方法返回的对象(TempDaoFactoryBean),还有一个就是当前对象(DaoFactoryBean)
-
*
-
* getObject()返回的对象(TempDaoFactoryBean)存储在spring容器中给这个对象设置的beanName是当前类指定的对象,也就是 @Component("daoFactoryBean") 中的daoFactoryBean
-
* 当前对象(DaoFactoryBean)在spring容器中设置的beanName是在@Component("")指定name的基础上加一个“&”,这里也就是&daoFactoryBean
-
*
-
* ClassCastException类型转换异常
-
*/
-
public
class DaoFactoryBean implements FactoryBean {
-
// DaoFactoryBean这个工厂bean管理的对象
-
private String msg;
-
-
// 使用setter方法将其注入
-
public void setMsg(String msg) {
-
this.msg = msg;
-
}
-
-
public void testBean() {
-
System.out.println(
"testBean");
-
}
-
-
@Override
-
public Object getObject() throws Exception {
-
// 在FactoryBean内部创建对象实例
-
TempDaoFactoryBean temp =
new TempDaoFactoryBean();
-
String[] msfArray = msg.split(
",");
-
temp.setMsg1(msfArray[
0]);
-
temp.setMsg2(msfArray[
1]);
-
temp.setMsg3(msfArray[
2]);
-
return temp;
-
}
-
-
@Override
-
public Class<?> getObjectType() {
-
return TempDaoFactoryBean.class;
-
}
-
-
/**
-
* 是否是单例
-
* @return
-
*/
-
@Override
-
public boolean isSingleton() {
-
return
true;
-
}
-
}
使用xml将这个factoryBean装配到spring容器中
-
<?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="daoFactory" class="priv.cy.dao.DaoFactoryBean">
-
<property name="msg" value="msg1,msg2,msg3">
</property>
-
</bean>
-
-
</beans>
测试类:
-
public
class Test {
-
public static void main(String[] args) {
-
AnnotationConfigApplicationContext annotationConfigApplicationContext
-
=
new AnnotationConfigApplicationContext(AppConfig.class);
-
-
TempDaoFactoryBean tempDaoFactoryBean = (TempDaoFactoryBean) annotationConfigApplicationContext.getBean(
"daoFactory");
-
System.out.println(tempDaoFactoryBean.getMsg1());
-
System.out.println(tempDaoFactoryBean.getMsg2());
-
System.out.println(tempDaoFactoryBean.getMsg3());
-
}
-
}
执行结果:
因为当我们getBean时,spring对实现了FactoryBean接口的类实现了特殊处理
当调用getBean("daoFactory")时,Spring通过反射机制发现DaoFactoryBean实现了FactoryBean的接口,
这时Spring容器就调用接口方法中的getObject()方法返回。如果希望获取CarFactoryBean的实例,
则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&car");
2.3 FactoryBean的两种用法
2.3.1 简化xml配置,隐藏细节
如果一个类有很多的属性,我们想通过Spring来对类中的属性进行值的注入,势必要在配置文件中书写大量属性配置,造成配置文件臃肿,那么这时可以考虑使用FactoryBean来简化配置
新建bean
-
public
class Student {
-
/** 姓名 */
-
private String name;
-
/** 年龄 */
-
private
int age;
-
/** 班级名称 */
-
private String className;
-
public Student() {
-
}
-
public Student(String name, int age, String className) {
-
this.name = name;
-
this.age = age;
-
this.className = className;
-
}
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public int getAge() {
-
return age;
-
}
-
public void setAge(int age) {
-
this.age = age;
-
}
-
public String getClassName() {
-
return className;
-
}
-
public void setClassName(String className) {
-
this.className = className;
-
}
-
@Override
-
public String toString() {
-
return
"Student{" +
"name='" + name +
'\'' +
", age=" + age +
", className='" + className +
'\'' +
'}';
-
}
-
}
实现FactoryBean接口
-
public
class StudentFactoryBean implements FactoryBean<Student> {
-
private String studentInfo;
-
@Override
-
public Student getObject() throws Exception {
-
if (
this.studentInfo ==
null) {
-
throw
new IllegalArgumentException(
"'studentInfo' is required");
-
}
-
String[] splitStudentInfo = studentInfo.split(
",");
-
if (
null == splitStudentInfo || splitStudentInfo.length !=
3) {
-
throw
new IllegalArgumentException(
"'studentInfo' config error");
-
}
-
-
Student student =
new Student();
-
student.setName(splitStudentInfo[
0]);
-
student.setAge(Integer.valueOf(splitStudentInfo[
1]));
-
student.setClassName(splitStudentInfo[
2]);
-
return student;
-
}
-
@Override
-
public Class<?> getObjectType() {
-
return Student.class;
-
}
-
public void setStudentInfo(String studentInfo) {
-
this.studentInfo = studentInfo;
-
}
-
}
新建day03.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"
-
xmlns:p=
"http://www.springframework.org/schema/p"
-
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans.xsd">
-
<!--注意:class是StudentFactoryBean而不是Student-->
-
<bean id="student" class="com.lyc.cn.day03.StudentFactoryBean" p:studentInfo="张三,25,三年二班"/>
-
</beans>
测试类
-
public
class MyTest {
-
@Before
-
public void before() {
-
System.out.println(
"---测试开始---\n");
-
}
-
@After
-
public void after() {
-
System.out.println(
"\n---测试结束---");
-
}
-
@Test
-
public void testStudentFactoryBean() {
-
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(
"day03.xml");
-
System.out.println(applicationContext.getBean(
"student"));
-
System.out.println(applicationContext.getBean(
"&student"));
-
}
-
}
运行
-
---测试开始---
-
-
Student{name=
'张三', age=
25, className=
'三年二班'}
-
-
org.springframework.beans.factory_bean.StudentFactoryBean@1ae369b7
-
-
---测试结束---
这样我们就实现了通过BeanFactory接口达到了简化配置文件的作用。另外大家也可以发现getBean(“student”)返回的Student类的实例;而getBean("&student")返回的是StudentFactoryBean实例,即工厂bean其本身。
2.3.2 返回不同Bean的实例
既然FactoryBean是一种工厂bean,那么我们就可以根据需要的类型,返回不同的bean的实例,通过代码简单说明一下
新建bean
-
public
interface Animal {
-
void sayHello();
-
}
-
-
public
class Cat implements Animal {
-
@Override
-
public void sayHello() {
-
System.out.println(
"hello, 喵喵喵...");
-
}
-
}
-
-
public
class Dog implements Animal {
-
@Override
-
public void sayHello() {
-
System.out.println(
"hello, 汪汪汪...");
-
}
-
}
创建了一个Animal接口极其两个实现类Cat和Dog,并进行简单输出,那么如何通过FactoryBean来通过配置返回不同的Animal实例呢
新建AnimalFactoryBean
-
public
class AnimalFactoryBean implements FactoryBean<Animal> {
-
private String animal;
-
-
@Override
-
public Animal getObject() throws Exception {
-
if (
null == animal) {
-
throw
new IllegalArgumentException(
"'animal' is required");
-
}
-
if (
"cat".equals(animal)) {
-
return
new Cat();
-
}
else
if (
"dog".equals(animal)) {
-
return
new Dog();
-
}
else {
-
throw
new IllegalArgumentException(
"animal type error");
-
}
-
}
-
-
@Override
-
public Class<?> getObjectType() {
-
if (
null == animal) {
-
throw
new IllegalArgumentException(
"'animal' is required");
-
}
-
if (
"cat".equals(animal)) {
-
return Cat.class;
-
}
else
if (
"dog".equals(animal)) {
-
return Dog.class;
-
}
else {
-
throw
new IllegalArgumentException(
"animal type error");
-
}
-
}
-
public void setAnimal(String animal) {
-
this.animal = animal;
-
}
-
}
修改day03.xml配置文件,增加bean
<bean id="animal" class="com.lyc.cn.day03.AnimalFactoryBean" p:animal="cat"/>
在MyTest中添加测试用例
-
@Test
-
public void testAnimalFactoryBean() {
-
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(
"day03.xml");
-
Animal animal = applicationContext.getBean(
"animal", Animal.class);
-
animal.sayHello();
-
}
运行
-
---测试开始---
-
-
hello, 喵喵喵...
-
-
---测试结束---
可以看到,配置文件里我们将animal配置成了cat,那么返回的就是cat的实例,也是简单工厂的一个实现
2.4 使用场景
说了这么多,为什么要有FactoryBean这个东西呢,有什么具体的作用吗?
FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。
我们知道AOP实际上是Spring在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象通过Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean。
所以,FactoryBean为我们实例化Bean提供了一个更为灵活的方式,我们可以通过FactoryBean创建出更为复杂的Bean实例。
还有比如我们spring需要整合mybatis,在没有spring-mybatis的情况下(spring-mybatis会帮助你将MyBatis 代码无缝地整合到Spring 中),我们需要将mybatis核心类SqlSessionFactory注入到spring容器,那么思考使用最常用的两种方式:
- 注解,可是mybatis是个我们引用的独立的项目.与我们自己的项目源码无关,我们无法去修改它的源码,在它的源码上添加注解,所以不能使用注解的方法
- xml,sqlSessionFacory需要注入许多的依赖,如果使用XML来配置,需要我们写大量的配置标签,非常不方便维护。
所以可以选择一个代理类去处理sqlSessionFacory,也就是我们在整合spring+mybatis时使用的SqlSessionFactoryBean,这个类是由mybatis提供的用来方便我们快速配置mybatis的factoryBean,通过这个类把很多繁琐的配置代码封装了起来,类似于装饰者模式,SqlSessionFactoryBean里面管理了sqlSessionFacory并且对他进行相关配置设置操作,我们只需要将SqlSessionFactoryBean注入到spring容器中,并在在xml向这个factoryBean传入一些简单的配置信息,SqlSessionFactoryBean就会帮我们自动配置好sqlSessionFacory,很多复杂的配置都帮我们填充好了,然后我们就可以通过SqlSessionFactoryBean获取已经配置完成的sqlSessionFacory。
三、BeanFactory和FactoryBean的区别以及共同点
共同点:都是接口
区别:
- BeanFactory 以Factory结尾,表示它是一个工厂类,用于管理Bean的一个工厂。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。该接口是IoC容器的顶级接口,是IoC容器的最基础实现,也是访问Spring容器的根接口,负责对bean的创建,访问等工作
- 对FactoryBean而言,以Bean结尾,说明这是一个交给容器去管理的bean。这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
参考资料:
https://blog.csdn.net/lyc_liyanchao/article/details/82424122
转载:https://blog.csdn.net/cy973071263/article/details/104758856