BeanFactory 和 FactoryBean 的区别

是翻译过来的,自己重新组织了下:原文在这里,需要*。

在 Spring 中,有两个接口:BeanFactoryFactoryBean 因为名字相近很容易被混淆。那他们之间有什么区别呢?

先说结论:

  1. BeanFactory: 是 IOC 容器,并且提供方法支持外部程序对这些 bean 的访问,在程序启动时 根据传入的参数产生各种类型的 bean,并添加到 IOC容器 中。
  2. FactoryBean: 首先是个 bean,也存放在 BeanFactory 中。它具有工厂方法的功能,在程序运行中 产生指定(一种)类型的 bean,至于添不添加到容器中,可以由代码控制。

1. BeanFactory

beanFactory 是 Spring IOC 容器的核心接口,它定义了 Spring 管理 bean 的通用方法,比如:getbean()containsBean() 。Spring 容器都是他的一种实现,比如:

  • DefaultListableBeanFactory。
  • XmlBeanFactory
  • ApplicationContext

这些实现类从不同的维度对 beanFactory 进行了扩展。

注:文章后面锁所说的 ”容器“ 都指的是 ”IOC容器“。

1.1 beanFactory 源码

package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
	
    // factoryBean 的转义标识符。(具体用法后面有介绍)
	String FACTORY_BEAN_PREFIX = "&";
	
    // 根据 name 从容器中拿对应的 bean。
	Object getBean(String name) throws BeansException;
	
    // 根据 name 和 type 从容器中拿对应的 bean,要对 bean 的类型做校验。
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	
    // 在容器中能否找到与 name 匹配的 bean 或者 beanDefinition。
	boolean containsBean(String name);

	// 判断 name 对对应的 bean 是不是 单例。
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	// 判断 name 对应的 bean 与指定的类型是否匹配。
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//根据 name 获取对应的 bean 的类型。
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    // 根据 name 获取对应 bean 的 别名。
	String[] getAliases(String name);
}

1.2 使用场景

  • 通过 名字或类型从容器中获取 bean。
  • 判断容器中是否包含指定的 bean。
  • 判断 bean 是不是单例。

2. FactoryBean

首先 FactoryBean 是一个 bean,但它又不仅仅是个 bean。它是一个可以 创建修饰 其他对象的”工厂 bean“,这跟设计模式中的工厂模式或者装饰设模式很相似,它可以创建除自身以外的其他对象。(后面有个例子,看完就明白这里说的什么意思了)。

2.1 FactoryBean 源码

public interface FactoryBean<T> {
	
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
	//从 factory 中获取 bean。
	@Nullable
	T getObject() throws Exception;

	// 从 beanFactory 中获取类型。
	@Nullable
	Class<?> getObjectType();

	//是单例?
	default boolean isSingleton() {
		return true;
	}
    
}

从上面的接口可以看出,FactoryBean 有工厂的味道。也就是说,如果一个 A类实现了 FactoryBean 接口,那么 类A 就变成了 A工厂。根据 A 的名称获得的实际上是工厂调用 getObject() 返回的对象, 而不是 A工厂自己。如果你想获得 A工厂自己的实例,你需要添加 & 前缀。

到这里再不出 demo 就写不下去了。这是一个简单的 FactoryBean 的使用栗子。

package com.example.demo.domian;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class MyFactoryBean implements FactoryBean {
	
  // 保存一句话,用来区分不同的对象。  
  private String message;
  // 无参构造器。
  public MyFactoryBean() {
      // 意思是:当前对象是 MyFactoryBean 的对象。
    this.message = "object of myFactoryBeanSelf";
  }
  // 有参构造器。
  public MyFactoryBean(String message) {
    this.message = message;
  }
  // 获取 message。
  public String getMessage() {
    return this.message;
  }

  @Override
  /**
   *  这个方法在执行时创建了新的 MyFactoryBean 类型的对象。
   *  这里继续沿用了 MyFactoryBean 类型,但是可以是别的类型
   *  比如:Person、Car、等等。
   */
  public Object getObject() throws Exception {
      // 意思是:当前对象是 MyFactoryBean 的 getObject() 创建的。
    return new MyFactoryBean("object from getObject() of MyFactoryBean");
  }

  @Override
  public Class<?> getObjectType() {
    return MyFactoryBean.class
  }
}

测试内容很简单,MyFactoryBean 被 @Component 注解了,当启动SpringBoot程序的时候,会为 MyFactoryBean 构建 bean 并且保存到容器中。我们要测试的就是用 类: MyFactoryBean 的 名字: myFactoryBean 去容器中拿 bean,看拿到的结果是什么?

package com.example.demo.domian;


import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = com.example.demo.domian.MyFactoryBean.class)
class MyFactoryBeanTest {
  @Autowired
  private ApplicationContext context;

  @Test
  public void test() {
    // 第一次用 myFactoryBean 去拿。  
    MyFactoryBean myBean1 = (MyFactoryBean) context.getBean("myFactoryBean");
    System.out.println("myBean1 = " + myBean1.getMessage());
    // 第二次用 &myFactoryBean 去拿。  
    MyFactoryBean myBean2 = (MyFactoryBean) context.getBean("&myFactoryBean");
    System.out.println("myBean2 = " + myBean2.getMessage());、
    // 判断两次拿到的对象是不是一样的?    
    System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
  }
}

结果:

myBean1 = object from getObject() of MyFactoryBean
myBean2 = object of myFactoryBeanSelf
myBean1.equals(myBean2) = false

结果很明显:

第一次使用 myFactoryBean 去容器中拿,实际上是 容器中 MyFactorybean 的bean 调用了 getObject() 方法,并将结果返回。

第二次使用 &myFactoryBean 去容器中拿,才是真正拿到了 MyFactorybean 的 bean。

& 就是用于区分到底拿谁的的前缀。

两次拿出来的对象当然是不一样的。

而且 new MyFactoryBean("object from getObject() of MyFactoryBean") 根本就没有注册到 容器中,它是在程度运行时 beanFactory 执行 getObject 方法时构造出来的。

getObject方法注释上写着:

Return an instance of the object managed by this factory.

返回一个实例,这个实例是被 factoryBean 管理的,而且也没有规定返回的实例的类型。

那 FactoryBean 产生的 bean 受不受 spring 容器的管理呢?

这需要看 FactoryBean 的实现类是怎么重写 getObject 方法的,如果像上面的 demo 一样,仅仅是返回个新对象,那么新对象不受 spring 容器管理,如果在重写的 getObject 方法中有段代码是将新对象添加进了 Spring 容器,那么新对象肯定要是受管理的。

(打断点看过,上面的测试中,容器里面没有 new MyFactoryBean("object from getObject() of MyFactoryBean")

2.2 使用场景

说了这么说,为什么需要 FactoryBean ,它的特殊功能是什么呢?

Spring 中 FactoryBean 最大的应用场景是用在 AOP 中。我们都知道,AOP 实际上是 Spring 在运行是创建出来的代理对象,这个对象是在运行时才被创建的,而不是在启动时定义的,这与工厂方法模式是一致的。更生动地说,AOP 代理对象通过 java 反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中。Spring 中的 ProxyFactoryBean 就是干这事的。

因此,FactoryBean 提供了更灵活的实例化 bean 的方法。通过 FactoryBean 我们可以创建更复杂的 bean。

上一篇:Mybatis的初始化和结合Spring Framework后初始化的源码探究


下一篇:des加密解密JAVA与.NET互通实例