我们可以将FactoryBean看成Factory + Bean。首先它是一个Bean,和其他Bean一样,归Spring容器管理。同时它也是一个factory可以生产一个定制的Bean。
public interface FactoryBean<T> {
// 返回定制化bean对象
@Nullable
T getObject() throws Exception;
// Spring在查找依赖的时候,通过此方法来判断isTypeMatch
@Nullable
Class<?> getObjectType();
// 默认返回单例对象
default boolean isSingleton() {
return true;
}
}
代码准备
创建POJO ModelA
package com.test.fb.model;
public class ModelA {
private String sign;
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
}
创建配置类
package com.test.fb.config;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("com.test.fb")
public class AppConfig {
}
创建启动类
package com.test.fb;
import com.test.fb.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
创建第一个FactoryBean
package com.test.fb.component;
import com.test.fb.model.TargetObject;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
@Component
public class FirstFactoryBean implements FactoryBean<TargetObject> {
@Override
public TargetObject getObject() throws Exception {
TargetObject targetObject = new TargetObject();
targetObject.setSign("factory create");
return targetObject;
}
@Override
public Class<?> getObjectType() {
return TargetObject.class;
}
}
运行main方法,查看运行结果
通过FactoryBean创建了一个定制Bean
即这个方法目的就是获取原始的beanName
首先我们要注意一点 : 传入的参数是name,最终解析出来的名称是beanName
我们注意这个if判断,它判断我们原始传入的参数name是不是带有特殊符号"&",如果存在特殊字符表示我们需要的是@Component标记的类产生的bean,而不是FactoryBean#getObject方法产生的bean
如果不带有特殊符号,普通bean直接返回,FactoryBean进入getObjectFromFactoryBean方法
我们跟进去代码继续分析
Spring最终会调用我们自己写的getObject方法
beanFactory有个存放bean的单例池singletonObjects,factoryBean为了保持单例特性将实例出来的bean对象放入factoryBeanObjectCache
我们利用上文中“FactroyBean的使用”的代码进行测试
我们查看截图,FactoryBean确实是在我们调用genBean方法以后才放入缓存中的,符合懒加载的特性
FactoryBean一定是懒加载的么?我们再写一个例子验证一下
创建第二个FactoryBean
package com.test.fb.component;
import com.test.fb.model.TargetObject;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.stereotype.Component;
@Component
public class SecondFactoryBean implements SmartFactoryBean<TargetObject> {
@Override
public TargetObject getObject() throws Exception {
TargetObject targetObject = new TargetObject();
targetObject.setSign("second factory create");
return targetObject;
}
@Override
public Class<?> getObjectType() {
return TargetObject.class;
}
@Override
public boolean isEagerInit() {
return true;
}
}
执行main方法 查看运行结果
我们可以看到我们还没有调用genBean方法,factoryBeanObjectCache里面已经有缓存了,说明FactoryBean在Spring启动的过程中就实例化了。
我们查看相关源码DefaultListableBeanFactory#preInstantiateSingletons分析原因
总结
FactoryBean的特点是可以定制化生产bean,这一点@Bean同样可以做到,为什么还需要存在FactoryBean呢?而且FactoryBean还有getBean("&beanName")和getBean("beanName")的区别,怎么看都是@Bean比较简单。关于这一点我们可以思考一下@Bean的短板,@Bean只能处理明确目标的bean。比如我希望扫描一个package,让这个package下的类或者接口都变成bean,这样不确定数量和目标类(或接口)的场景,@Bean是无法处理的。我们熟悉的@MapperScan注解在完成mybatis和spring的整合过程中就用到了FactoryBean的特性,所以FactoryBean有不可替代的地位。