FactoryBean vs BeanFactory
候选人小韩在面试字节 P6 时,面试官问道:
"FactoryBean 和 BeanFactory 有什么区别?"
小韩说:"FactoryBean 是用来创建 Bean 的..."
面试官追问:"那 BeanFactory 呢?"
小韩说:"BeanFactory 也是...呃...不太一样..."
面试官继续追问:"MyBatis 整合 Spring 时用的是什么?"
小韩答不上来。
【面试官心理】
这道题我用来测试候选人对 Spring 容器体系的理解深度。FactoryBean 和 BeanFactory 只差一个字,但完全是两个东西。能说清楚区别的占 30%,能说出 MyBatis 用的什么的占 10%。能答到最后的,基本都看过 Spring 源码。
一、核心问题 🔴
1.1 问题拆解
第一层:概念
- "FactoryBean 和 BeanFactory 有什么区别?"
- "FactoryBean 是 Bean 吗?"
第二层:用途
- "FactoryBean 用来做什么?"
- "Spring 内部哪些地方用到了 FactoryBean?"
第三层:源码
- "FactoryBean 的 getObject() 什么时候被调用?"
- "FactoryBean 和 @Bean 有什么关系?"
第四层:实战
- "MyBatis 整合 Spring 用的是什么?"
- "什么场景下需要自定义 FactoryBean?"
1.2 ❌ 错误示范
候选人原话 A:"FactoryBean 和 BeanFactory 都是 Spring 的容器,用来创建 Bean 的。"
问题诊断:
- 完全混淆了两个概念
- BeanFactory 是容器,FactoryBean 是特殊的 Bean
- 说明基础知识不扎实
候选人原话 B:"FactoryBean 是一个接口,实现它可以自定义 Bean 的创建过程。"
问题诊断:
- 知道 FactoryBean 是接口,但说不清它和普通 Bean 的区别
- 不知道 FactoryBean 本身也是一个 Bean
候选人原话 C:"FactoryBean 和 @Bean 是一样的,都是创建 Bean。"
问题诊断:
- 混淆了 FactoryBean 和 @Bean 注解
- @Bean 是注解,FactoryBean 是接口
1.3 标准回答
P5 回答:核心区别
这是两个完全不同的东西:
// BeanFactory - Spring IoC 容器的接口
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(Class<T> requiredType);
<T> T getBean(String name, Class<T> requiredType);
boolean containsBean(String name);
// ... 其他容器方法
}
// FactoryBean - 特殊的 Bean,用于创建 Bean
public interface FactoryBean<T> {
// 返回创建的对象
T getObject();
// 返回创建的对象类型
Class<?> getObjectType();
// 是否单例
default boolean isSingleton() {
return true;
}
}
1.4 追问升级
追问 1:FactoryBean 是怎么工作的?
这是理解 FactoryBean 的核心:
// FactoryBean 本身也是一个 Bean
// 但它的作用是"生产"其他 Bean
@Service
public class MyFactoryBean implements FactoryBean<MyObject> {
@Override
public MyObject getObject() {
// 这个方法返回的对象会被注册到容器中
// 而不是 MyFactoryBean 本身
return new MyObject();
}
@Override
public Class<?> getObjectType() {
return MyObject.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
当调用 context.getBean("myFactoryBean") 时:
- 如果 Bean 名字以
& 开头,返回 FactoryBean 本身
- 否则返回
getObject() 返回的对象
ApplicationContext context = ...;
// 获取 getObject() 返回的对象
MyObject obj = context.getBean("myFactoryBean"); // 返回 MyObject
// 获取 FactoryBean 本身
FactoryBean<?> factory = context.getBean("&myFactoryBean"); // 返回 MyFactoryBean
源码实现:
// AbstractBeanFactory.getBean()
protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {
Object bean = getSingleton(beanName);
if (bean == null) {
bean = createBean(beanName, mbd, args);
}
// 【关键】如果是 FactoryBean,获取其产品
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit = (beanName.startsWith("&") || factory.isSingleton());
if (isEagerInit) {
// 调用 getObject() 获取实际对象
bean = factory.getObject();
}
}
return (T) bean;
}
追问 2:Spring 内部哪些地方用到了 FactoryBean?
这是面试高频追问:
MyBatis 整合 Spring:
// MyBatis-Spring 中的核心类
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
@Override
public T getObject() {
// 【关键】创建 Mapper 代理对象
return getSqlSession().getMapper(mapperInterface);
}
@Override
public Class<T> getObjectType() {
return mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
// 使用方式
@Bean
public MapperFactoryBean<UserMapper> userMapper() {
MapperFactoryBean<UserMapper> factory = new MapperFactoryBean<>();
factory.setMapperInterface(UserMapper.class);
factory.setSqlSessionFactory(sqlSessionFactory);
return factory;
}
Spring 自己的 ProxyFactoryBean:
// ProxyFactoryBean 是 Spring AOP 的核心类
public class ProxyFactoryBean implements FactoryBean<Object>, AopProxyFactory {
@Override
public Object getObject() {
// 创建 AOP 代理对象
return createAopProxy().getProxy();
}
@Override
public Class<?> getObjectType() {
return targetClass;
}
}
// 使用示例
@Bean
public ProxyFactoryBean<UserService> userServiceProxy() {
ProxyFactoryBean<UserService> factory = new ProxyFactoryBean<>();
factory.setTarget(targetUserService);
factory.addAdvisor(transactionAdvisor);
factory.addAdvice(loggingInterceptor);
return factory;
}
追问 3:FactoryBean vs @Bean
// FactoryBean 方式
@Service
public class MyFactoryBean implements FactoryBean<MyObject> {
@Override
public MyObject getObject() {
// 复杂的创建逻辑
MyObject obj = new MyObject();
obj.setProperty("value");
return obj;
}
}
// @Bean 方式
@Configuration
public class MyConfig {
@Bean
public MyObject myObject() {
MyObject obj = new MyObject();
obj.setProperty("value");
return obj;
}
}
什么时候用 FactoryBean:
- 创建的 Bean 类型在编译时未知(如泛型)
- 需要在 Bean 创建前后做一些额外处理
- 整合第三方库时(如 MyBatis)
- 需要创建代理对象时(如 AOP)
二、延伸问题 🟡
2.1 自定义 FactoryBean 示例
场景:创建带配置的第三方客户端
// 第三方客户端
public class ThirdPartyClient {
public ThirdPartyClient(String apiKey, String baseUrl) { }
}
// 自定义 FactoryBean
@Component
public class ThirdPartyClientFactoryBean implements FactoryBean<ThirdPartyClient> {
@Value("${thirdparty.api-key}")
private String apiKey;
@Value("${thirdparty.base-url}")
private String baseUrl;
@Override
public ThirdPartyClient getObject() {
// 创建客户端实例
return new ThirdPartyClient(apiKey, baseUrl);
}
@Override
public Class<?> getObjectType() {
return ThirdPartyClient.class;
}
@Override
public boolean isSingleton() {
return true; // 单例
}
}
// 使用
@Autowired
private ThirdPartyClient client; // 直接使用,不需要知道 FactoryBean
2.2 ObjectFactory 和 FactoryBean
// ObjectFactory - 只是 getObject() 接口
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject();
}
// FactoryBean - 增强版,有类型信息和单例标识
public interface FactoryBean<T> {
T getObject();
Class<?> getObjectType();
default boolean isSingleton();
}
// ObjectFactory 的使用场景
@Service
public class MyService {
@Autowired
private ObjectFactory<MyObject> myObjectFactory;
public void useObject() {
// 每次调用 getObject() 都创建新对象
MyObject obj = myObjectFactory.getObject();
}
}
三、生产避坑
3.1 FactoryBean 的名字问题
// 如果 FactoryBean 的 Bean 名字是 userDao
// 通过 getBean("userDao") 获取的是 getObject() 返回的对象
// 通过 getBean("&userDao") 获取的是 FactoryBean 本身
@Autowired
private UserDao userDao; // 注入的是 getObject() 返回的类型
// 如果需要注入 FactoryBean 本身
@Autowired
private FactoryBean<UserDao> userDaoFactory;
3.2 isSingleton 的影响
@Component
public class MyFactoryBean implements FactoryBean<MyObject> {
@Override
public MyObject getObject() {
// 每次调用都创建新对象
return new MyObject();
}
@Override
public boolean isSingleton() {
return false; // 返回 false
}
}
// 行为:
context.getBean("myFactoryBean"); // 每次调用 getObject()
context.getBean("myFactoryBean"); // 每次调用 getObject()
四、工程选型
4.1 选择建议
五、面试总结
FactoryBean vs BeanFactory,虽然名字很像,但完全不是一回事。
P5 候选人能说出"BeanFactory 是容器,FactoryBean 是特殊的 Bean"。
P6 候选人能说清 FactoryBean 的工作原理,能说出 MyBatis 用的是 MapperFactoryBean。
P7 候选人能说出 ProxyFactoryBean 的作用,能给出自定义 FactoryBean 的场景。
记住:
- BeanFactory = 容器
- FactoryBean = 工厂 Bean
- & = 获取工厂本身