用了这么久的SpringMVC框架,有一天看到一个问题,BeanFactory和FactoryBean有什么区别?
我当时的第一想法是BeanFactory不就是生产bean的工厂吗,FactoryBean不就是工厂里面生产出来的Bean吗?
后来发现,我的答案不是很对。去查了Spring的源码,看到了FactoryBean
的接口,如下
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
从开始的注释第一句就是,FactoryBean
是BeanFactory里面使用到的接口。真的是在BeanFactory
使用到的bean。同时实现了这个接口的bean不能向常规的bean一样使用他,如何注入一个FactoryBean
后面的例子会提到。
但是FactoryBean
实际上还是一个工厂,他有一个泛型T,表示这个工厂创建的实例类型,getObject()
则是返回T,就是创建bean的方法。getObjectType
返回的是创建bean的Class对象。而isSingleton
则是表示一个FactoryBean
创建出来的Bean是不是单例,
这个应该不难理解。
那么如何在实际开发里面使用呢?
这里有两个对象,一个是要创建出来的泛型T,一个是实现FactoryBean
接口的类。
类:ToolFactoryBean
public class ToolFactoryBean implements FactoryBean<Tool> {
private int factoryId;
private int toolId;
public void setFactoryId(int factoryId) {
this.factoryId = factoryId;
}
public int getToolId() {
return toolId;
}
public void setToolId(int toolId) {
this.toolId = toolId;
}
@Override
public Tool getObject() throws Exception {
return new Tool(toolId);
}
@Override
public Class<?> getObjectType() {
return Tool.class;
}
public int getFactoryId() {
return factoryId;
}
}
需要注意的是如果你用的是xml配置方式,那么你需要有setter来设置属性。
被创建出来的实例:
public class Tool {
private int id;
public Tool(int i) {
this.id = i;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
这里只是实现功能不需要定义复杂的属性。只需要定义一个id即可。
配置类:
@Configuration
public class FactoryBeanAppConfig {
@Bean(name = "toolFactory")
public ToolFactoryBean getTooFactoryBean() {
ToolFactoryBean toolFactoryBean = new ToolFactoryBean();
toolFactoryBean.setFactoryId(1111);
toolFactoryBean.setToolId(2333);
return toolFactoryBean;
}
// @Bean
// public Tool getTool() throws Exception {
// return getTooFactoryBean().getObject();
// }
}
xml配置方式多少有点落伍了,这里直接使用配置类的方式。需要说明的是如果配置了ToolFactoryBean就无需再配置出他创建出来的那个bean了,因为spring会糊涂。
使用方式:
package com.example.springmvcdemo.bean;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FactoryBeanAppConfig.class)
public class ToolFactoryBeanTest1 {
@Autowired
private Tool tool;
@Resource(name = "&toolFactory")
private ToolFactoryBean toolFactoryBean;
@Test
public void test() {
assertThat(tool.getId(), equalTo(2333));
assertThat(toolFactoryBean.getFactoryId(), equalTo(1111));
}
}
由于FactoryBean
无法通过正常的AutoWired获取到,所以使用& + beanName的方式获取,同样的,你会发现没有配置的Toolbean居然可以直接Autowired
以上就是FactoryBean
的简单的使用,可以看出,FactoryBean可以给我们留有一些预设Bean属性的余地。
FactoryBean
是如何在BeanFactory
里面生效的?
- FactoryBean的创建时机
因为是测试所以前面有分析依赖,然后走到了注入创建bean的逻辑。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
核心的代码是:
singletonObject = singletonFactory.getObject();
用来创建一个FactoryBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();// 创建bean
Class<?> beanType = instanceWrapper.getWrappedClass();
//省略一些代码
return exposedObject;
}
```java
FactoryBean里面创建的Bean将在`getObjectForBeanInstance`方法中创建:
```java
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
其实最终调用的都是FactoryBean的getObject方法来创建Bean
那么BeanFactory又是什么呢?
源代码长了,截取部分注释和方法分析
开篇第一句:
The root interface for accessing a Spring bean container. //所有Spring容器的父接口
他的增强的接口有ListableBeanFactory
, ConfigurableBeanFactory
,他提供了一系列的获取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;
获取Bean信息的一些方法
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
另外提供了ObjectProvider的获取方法:
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
ObjectProvider是用来做延迟加载的,可以消除@Autowired
例子:
public class FooService {
private final FooRepository repository;
public FooService(ObjectProvider<FooRepository> repositoryProvider) {
this.repository = repositoryProvider.getIfUnique();
}
}
另外在BeanFactory
中还有,如何获取FactoryBean的方法:
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
总的来说FactoryBean是容器,他里面包含了获取Bean和Bean信息的各种方法,而FactoryBean是在BeanFactory里面使用的一种特殊的Bean,他主要的职能是用来创建Bean。
参考:
- https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/ObjectProvider.html
- https://www.baeldung.com/spring-factorybean
- https://blog.csdn.net/alex_xfboy/article/details/83342164?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242