Author 罗先生
本文是IOC系列的第三篇,往期回顾:
FactoryBean是什么?
- Spring提供的一种特殊Bean接口。
- 实现了FacotryBean这个接口的Bean,Spring当中会存在两个对象,一个是实现的FactoryBean对象,还有一个就是你实现的getObject()方法中返回的对象。
来编个码:
@Component
public class MyFactoryBean implements FactoryBean {
public void selectOrder() {
System.out.println("MyFactoryBean selectOrder");
}
@Override
public Object getObject() throws Exception {
return new OrderDaoImpl();
}
@Override
public Class<?> getObjectType() {
return OrderDaoImpl.class;
}
}
测试代码一(获取MyFactoryBean 对象):
public class FactoryBeanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
MyFactoryBean myFactoryBean = (MyFactoryBean)annotationConfigApplicationContext.getBean("myFactoryBean");
System.out.println(myFactoryBean);
}
}
//运行结果:
Exception in thread "main" java.lang.ClassCastException: com.luban.ioctest.OrderDaoImpl cannot be cast to com.luban.ioctest.factoryBean.MyFactoryBean
at com.luban.test.FactoryBeanTest.main(FactoryBeanTest.java:15)
发现报错了???这里一定要觉得奇怪。。 不然IOC的基础就不太行了(当然前提是不知道FactoryBean)。因为@Component生成的BeanName应该就是类名首字母小写。
答案在于开篇就说了FactoryBean特殊,除了会生成两个Bean以外,获取自己的时候也特殊,需要加&,后面再跟beanName,才可以get出来
测试代码二(获取MyFactoryBean 对象 - 正确版):
public class FactoryBeanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
MyFactoryBean myFactoryBean = (MyFactoryBean)annotationConfigApplicationContext.getBean("&myFactoryBean");
System.out.println(myFactoryBean);
}
}
//运行结果
com.luban.ioctest.factoryBean.MyFactoryBean@3c5a99da
没报错了,证实了上面说的话
测试代码三(获取MyFactoryBean 往Spring容器里注册的对象):
public class FactoryBeanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
OrderDaoImpl orderDao = (OrderDaoImpl)annotationConfigApplicationContext.getBean("orderDaoImpl");
System.out.println(orderDao);
}
}
这里如果强转成MyFactoryBean对象会报错,不信的话可以尝试一下
FactoryBean内部存储
- getObject()得到的对象,bename存的是当前类指定的名字(类名首字母小写),对象则是return的对象
- FactoryBean对象的beanName其实还是当前类指定的名字(类名首字母小写)
为什么取FactoryBean还要加&
上面说了Spring在容器(DefaultListableBeanFactory)内部的beanName还是myFactoryBean(上面自定义的FactoryBean的名字),但是取的时候加了&,那么怎么取的呢,可以查看doGetBean源码,第一行
final String beanName = transformedBeanName(name);
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
答案就在于这个方法org.springframework.beans.factory.support.AbstractBeanFactory#transformedBeanName
源码也比较简单,如果以&开头,那么代表需要找的是FactoryBean,然后把前缀&去掉,这样就能找到真实的bean了
FactoryBean在什么会用到?
当我们一个类依赖关系十分复杂的时候,而我们想对外提供一种简单的关系,让外部可以很便捷的把某个类配置出来的话就可以选择FactoryBean,经典的场景是SqlSessionFactoryBean,可见SpringIOC-FactoryBean