FactoryBean是spring框架定义的bean工厂,即FactoryBean用于产生另外一个bean。FactoryBean可以用于解决初始化过程较为复杂,通过spring的依赖注入等无法解决的bean的初始化问题。
FactoryBean的定义public interface FactoryBean{ String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; T getObject() throws Exception; Class> getObjectType(); default boolean isSingleton() { return true; } }
如果是单例,首次获取的bean会在FactoryBeanRegistrySupport中进行缓存。
如果是多例,那么每次获取工厂bean的Object都会生成一个新的bean。
该方法用于返回FactoryBean可以生成的目标bean的类型。这样可以避免为了获取bean的类型而进行提前init的问题。
该方法用于实现构造目标bean的逻辑。如果目标bean的构造依赖于其他的bean,那么FactoryBean也可以完成对这些被依赖的bean的注入管理。
FactoryBean的初始化在执行getObject方法之前,故当执行FactoryBean的getObject方法的时候,FactoryBean应该已经完成了实例化。
如果在FactoryBean当中注入一个目标bean,那么在FactoryBean的create过程当中会发生循环依赖问题。后面单独说明。
例子public interface BlueFactoryBeanInterface extends FactoryBean{ void asyncLog(BlueBean blueBean); }
@Builder
@Setter
@Getter
@ToString
public class BlueBean {
private String name;
private long index;
}
@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {
private static long index = 0;
@Autowired
private BlueFactoryBeanInterface blueFactoryBean;
@Autowired
@Lazy
private BlueFactoryBeanInterface blueFactoryBeanProxy;
//@Autowired
//private BlueBean blue;
@Autowired
@Lazy
private BlueBean blueProxy;
@Async
public void asyncLog(BlueBean blueBean) {
System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
}
@Override
public BlueBean getObject() throws Exception {
//build the BlueBean
BlueBean bean = BlueBean.builder()
.name("blue")
.index(index++)
.build();
System.out.println("getObject " + bean);
//print log info in asynchronous way when new object generated.
blueFactoryBeanProxy.asyncLog(bean);
return bean;
}
@Override
public Class> getObjectType() {
return BlueBean.class;
}
@Override
public boolean isSingleton() {
return false;
}
我们定义一个FactoryBean接口的实现BlueFactoryBean。这个类能够生产多例BlueBean,每次调用getBean(''blueFactoryBean'')都会返回一个新的BlueBean。同时,在每次产生一个新的bean的时候,都会异步打印一条日志。这个类的组织关系如下图所示。
为什么保存FactoryBean而不是目标?在容器当中保存的是FactoryBean本身,而不是目标。
| blueFactoryBean=org.blue.bluelearning.springproject.factory_bean.BlueFactoryBean@3a8d467e |
FactoryBean有可能根据isSingleton的配置来判断每次是否返回一个新的Object。假设直接保存Object而不是FactoryBean,则无法通过FactoryBean来实现这个功能。单独保存Object也是没有必要的,因为在spring getBean的实现逻辑当中,首次获取到的target object会被缓存在FactoryBeanRegistrySupport。
org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBean>, String, boolean)
因此,如果为多例。则每次的注入点解析,getBean(''blueFactoryBean'')实际上都可以返回一个新的bean。
总之,通过保存FactoryBean,再通过FactoryBean获取目标bean,这样可以实现更加灵活的bean的生成。
FactoryBean的获取
SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableAsync
public class App {
public static void main(String[] args) throws Exception {
//基于cglib
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);
BlueBean bean1 = configurableApplicationContext.getBean(BlueBean.class);
FactoryBean bean2 =(FactoryBean)configurableApplicationContext.getBean(BlueFactoryBeanInterface.class);
FactoryBean bean3 =(FactoryBean)configurableApplicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX+"blueFactoryBean");
BlueBean bean4 =(BlueBean)configurableApplicationContext.getBean("blueFactoryBean");
//FactoryBean bean = configurableApplicationContext.getBean(FactoryBean.class);
}
}
(一)按照名称获取
在BeanFactory中保存的bean的类型实际上是一个FactoryBean,并且这个FactoryBean的名字就是@Component中指定的bean名称或者bean class name的首字母缩写,比如本例中的blueFactoryBean。
如果执行getBean("blueFactoryBean")会得到Object
如果执行getBean("&blueFactoryBean")会得到FactoryBean本身。
(二)按照类型获取
和按照名称获取不同,按照类型获取到的bean的类型就是指定的类型。
如果执行getBean(BlueFactoryBeanInterface.class)会得到FactoryBean本身。
如果执行getBean(BlueBean.class)会得到FactoryBean本身。当然,spring会首先获取到BlueFactoryBean,再通过BlueFactoryBean得到BlueBean。
这里定义了BlueFactoryBeanInterface,是为了便于根据这个类型得到惟一的一个bean,如果根据FactoryBean去查找,会得到多个候选。
和循环依赖 (一)getObjectForBeanInstance
| AbstractBeanFactory.doGetBean |
| 从缓存当中获取到目标 -getSingleton(beanName) -getObjectForBeanInstance(sharedInstance, beanName, name) |
| OR(新建bean) -getSingleton(beanName, ObjectFactory) --createBean() --addSingleton -getObjectForBeanInstance |
可以看到无论是从缓存当中获取到目标,还是新建一个bean(在新建bean的过程当中可能发生循环依赖问题),都是在getSingleton结束之后,才会执行getObjectForBeanInstance。
从上面可以看到FactoryBean的实例化,和普通bean的实例化是完全相同。自然也可能出现循环依赖问题。在FactoryBean中注入的实例,可以用于完成目标对象的实例化。
但是getObjectForBeanInstance一般不会导致循环依赖的产生。
(二)getObject方法FactoryBean的getObject方法似乎和循环依赖问题关联不大。不过getObject方法的不合理的定义会产生意想不到的问题。
如果要要注入一个BlueBean,那么getBean方法就会执行getObject方法来在BlueFactoryBean的基础上生成一个BlueBean。要在FactoryBean中注入的是一个BlueBean。
Spring会认为这是循环依赖,因为FactoryBean的非带前缀名称就是BlueBean的名称。
如果在getObject的逻辑当中触发getObject会造成overstack问题。
public class BlueFactoryBean implements BlueFactoryBeanInterface {
private static long index = 0;
@Autowired
private BlueBean blue;
@Async
public void asyncLog(BlueBean blueBean) {
System.out.println("asynchrounos log: BlueFactoryBean say hello" + blueBean);
}
@Override
public BlueBean getObject() throws Exception {
//build the BlueBean
BlueBean bean = BlueBean.builder().name("blue").index(index++).build();
System.out.println("getObject " + bean);
System.out.println("The injected blue's index is: " + blue.getIndex());
//print log info in asynchronous way when new object generated.
blueFactoryBeanProxy.asyncLog(bean);
return bean;
}
}
(三)在BlueFactoryBean注入一个BlueBean
如果在BlueFactoryBean当中注入一个BlueBean,则会产生一个循环依赖的报错问题。代码如下。当然循环依赖问题的万能注解@Lazy可以解决这个问题。不过需要具体分析一下报错的原因。
@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {
......
@Autowired
private BlueBean blue;
@Async
public void asyncLog(BlueBean blueBean) {
System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
}
......
}
在spring的启动阶段会遍历容器所有的单例bean,对他们进行提前的预初始化。BlueFactoryBean就是一个单例。这个单例的名字是blueFactoryBean,因此执行逻辑如下:
getBean("blueFactoryBean")
- expose early reference
- poputeBean
-- resolve blue
--- 找到 BlueFactoryBean 可以生产 BlueBean
--- 执行getBean("blueFactoryBean")
--- 注入一个BlueBean。
--- 注册依赖 blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)
-initializeBean
--对BlueFactoryBean增强为 Proxy(BlueFactoryBean)
-check
-- 发现BlueFactoryBean 被增强,并且 他已经提前注册到了另外一个bean里面(就是自己),所以报错。
我个人认为,这里的检查报错的原因并非BlueFactoryBean的提前注册,而是因为 BlueFactoryBean的getObject方法的提前执行。BlueFactoryBean没有完全完成初始化之前,就进行了目标的生产,并且后续BlueFactoryBean又进行了增强,所以肯定存在隐患。
(四)再提名字由在BlueFactoryBean中注入一个BlueBean的分析过程可以知道,在成功注入一个BlueBean之后,会在BeanFactory当中注册依赖。
| blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字) |
我个人认为,这个依赖的注册是spring能够检测到循环依赖问题,并且报错的关键。但是问题是,为什么注册了一个BlueBean,但是这里使用的名字却是blueFactoryBean?
虽然,容器里面注册的是 FactoryBean,甚至名字也是FactoryBean的名字,但是实际上是用于获取一个它的产品。 所以,FactoryBean的名字也理所当然的是它所生产的产品的名字。而他自己的名字需要增 加一个&前缀。而BlueBean在容器当中并没有名字,因为它没有在容器当中直接进行注册,它的名字就是工厂bean的名字,而工厂bean的名字是需要添加&前缀。
(五)在BlueFactoryBean注入一个BlueFactoryBean因为名字FactoryBean自身对应的bean名称的特殊性,在BlueFactoryBean中注入自身并不会出现循环依赖的报错。
@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {
......
@Autowired
private BlueFactoryBean blueFactoryBean;
@Async
public void asyncLog(BlueBean blueBean) {
System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);
}
......
}
虽然为发生报错,但是由于是在create BlueFactoryBean过程当中提前获取的为增强raw bean。所以注入的bean并非最终版本。也是有问题的。下面分析一下为什么没有发生报错问题。
getBean("blueFactoryBean")
- expose early reference
- poputeBean
-- resolve blueFactoryBean
--- 执行getBean(BlueFactoryBean.class)
--- 注入一个BlueFactoryBean。
--- 注册依赖 &blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)
-initializeBean
--对BlueFactoryBean增强为 Proxy(BlueFactoryBean)
-check
-- 发现BlueFactoryBean 被增强,但是没有发现他已经提前注册到了另外一个bean里面。
原因是这里注册依赖是
| &blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字) |
getBean在create过程当中总是认为自己的名字是blueFactoryBean,因为具体判断是返回自身还是产品是在create BlueFactoryBean之后。
(六)@Lazy尽管直接注入BlueFactoryBean或者BlueBean都是有问题的,但是@Lazy这个万能注解可以很好的解决这个问题。
和三级缓存
三级缓存用于应对循环依赖问题,而FactoryBean的create过程和其他bean的create过程无异,必然也会和三级缓存相关。
DefaultSingletonBeanRegistry.getSingleton(String beanName,ObjectFactory> singletonFactory)
-1. objectFactory.getObject():实际为createBean()方法。
-2. DefaultSingletonBeanRegistry.addSingleton(String, Object)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.addSingleton(String, Object)
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
可以看到getSingleton执行完之后,单例已经放到了singletonObjects当中,三级缓存的作用域已经结束。所以,getObjectForBeanInstance获取目标target object的过程和三级缓存没有直接关系。但是FactoryBean的create过程还是和三级缓存直接相关的。



