FactoryBean
实现FactoryBean接口
FactoryBean的写法有很多,先试一下实现FactoryBean接口的方法 定义一个Book对象
java
public class Book {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "{\"Book\":{"
+ "\"name\":\""
+ name + '\"'
+ "}}";
}
}
定义一个生产Book的工厂,实现FactoryBean,并通过@Component注解注入到spring容器
java
@Component
public class BookFactory implements FactoryBean<Book> {
@Override
public Book getObject() {
Book book = new Book();
book.setName("factory bean Book");
return book;
}
@Override
public Class<?> getObjectType() {
return Book.class;
}
@Override
public String toString() {
return "{\"BookFactory\":{"
+ "}}";
}
}
简单做一下测试
java
@Configuration
// 扫描两个对象所在的包
@ComponentScan("com.pqsir.factoryBean")
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainApplication.class);
}
}
熟悉源码的都知道ApplicationContext初始化后会提前初始化所有单例(且非懒汉模式)的bean,通过beanFactory.preInstantiateSingletons()方法 debug看一下初始化后的结果,就在preInstantiateSingletons方法最后打个断点,结果如下 
可以看到一级缓存中存储了BookFactory的实例,却没有Book,一会儿在想Book,现在向spring容器要BookFactory应该是没有问题的
getBean
1.getBean(BookFactory.class)
java
@Configuration
// 扫描两个对象所在的包
@ComponentScan("com.pqsir.factoryBean")
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainApplication.class);
Object o1 = context.getBean(BookFactory.class);
System.out.println(o1);
}
}
输出
json
{"BookFactory":{}}
确实没问题,但是拿它有啥用,我们的目的是通过BookFactory向spring容器注册一个Book,肯定想要取得是Book的Bean,那再试一下getByName的方式 2.getBean("bookFactory")
java
@Configuration
// 扫描两个对象所在的包
@ComponentScan("com.pqsir.factoryBean")
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainApplication.class);
Object o2 = context.getBean("bookFactory");
System.out.println(o2);
}
}
输出
json
{"Book":{"name":"factory bean Book"}}
居然取到了Book的实例!说实话,刚开始走到这步我有点懵,传入的明明是bookFactory,而且一级缓存甚至三级缓存中都没有Book实例,却能取到,调试了一下getBean发现原因了 getBean最终会调用doGetBean,doGetBean会先判断一级缓存中有没有实例,如果有则直接返回 
刚才调试已经证明一级缓存里肯定有bookFactory,但是返回的Bean却是Book,于是猜测getObjectForBeanInstance方法中被调包,跟进去看一眼 
其中getObjectFromFactoryBean代码如下 
也就是说,通过getBean再返回的时候,如果发现bean是FactoryBean则不返回一级缓存的实例,而是调用一级缓存实例的getObjects方法,而bookFactory的getObjects返回的是Book,所以或取到的是Book的bean 通过这种方法,有了一个获取Book的方式,但是需要传bookFactory的name,这显然是不太合适,下面试一下直接去取Book。 3.getBean("book")
java
Object o5 = context.getBean("book");
System.out.println(o5);
输出
arduino
NoSuchBeanDefinitionException: No bean named 'book' available
看来此路不通,调试了一下错误的位置,再getBean(String name)里会查询bean定义,如果没有,直接报错(再对preInstantiateSingletons的调试过程中发现并没有Book的)beanDefinition 4.getBean(Book.class)
java
Object o4 = context.getBean(Book.class);
System.out.println(o4);
输出
json
{"Book":{"name":"factory bean Book"}}
取到了哈哈,实际上平时我们用的@Autowired最终走得都会这种方式,那么问题来了为啥"book"取不到,Book.class却能取到,调试一下找到原因,调试路径
getBean(Class<T> requiredType)-->resolveBeanresolveBean-->resolveNamedBeanresolveNamedBean-->getBeanNamesForType
重点就是getBeanNamesForType这个方法,它根据传入的参数Book.class转换为beanName,而这个beanName的值正是"bookFactory",最后再通过getBean("bookFactory")自然取到了Book(第二步已证明) 而getBeanNamesForType是怎么做到的呐,回头看BookFactory有个这个实现
java
@Override
public Class<?> getObjectType() {
return Book.class;
}
getBeanNamesForType就是利用了这个方法,循环所有一级缓存中的FactoryBean,如果getObjectType==requiredType,就返回一级缓存的key,也就是BeanName("bookFactory"),这里代码有点多,截图截不下,纯理解吧
所以getBean(Class<T> requiredType)最终是通过转换还是走Object getBean(String name)
另外一种获取BeanFactory的方式
java
Object o2 = context.getBean("&bookFactory");
System.out.println(o2);
输出
json
{"BookFactory":{}}
单例
刚才说获取Book是通过BookFactory的getObjects()方法,但如果每次都走的话,那相当于获取一次就new一个,这肯定不行,spring是怎么解决的呐,其实解决方案很简单,第一次生成之后存起来,以后再获取就直接返回存的值即可实现单例 重点还是再调包方法getObjectForBeanInstance中 

也就是生成之后存入factoryBeanObjectCache里
java
/** Cache of singleton objects created by FactoryBeans: FactoryBean name to object. */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
所以spring容器存储bean的地方除了三级缓存,还有这个factoryBeanObjectCache存放了使用FactoryBean生成的bean
小结
继承FactoryBean方式实现工厂bean注入,画个图梳理下整个过程
工厂方法创建的bean,文字总结一下: 工厂创建的bean,实际上bean定义&三级缓存中都是存放着其工厂对象,只是在获取时发现如果工厂对象,通过调用getObject获取实际的bean对象
@Bean
另外一种定义FactoryBean的方法是最常见的@Configuration+@Bean 写个例子,一个类:
java
public class Bird {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "{\"Bird\":{"
+ "\"name\":\""
+ name + '\"'
+ "}}";
}
}
通过@Configuration+@Bean加入spring容器
java
@Configuration
public class BirdConfig {
@Bean
public Bird bird() {
Bird bird = new Bird();
bird.setName("factory bean Bird");
return bird;
}
}
直接先做个测试
java
@Configuration
// 扫描两个对象所在的包
@ComponentScan("com.pqsir.factoryBean")
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainApplication.class);
Object o1 = context.getBean(Bird.class);
System.out.println(o1);
Object o2 = context.getBean("bird");
System.out.println(o2);
}
}
这里就不去获取config了,也没啥实用意义,输出:
json
{"Bird":{"name":"factory bean Bird"}}
{"Bird":{"name":"factory bean Bird"}}
两种方式都取到了,这个确实比实现FactoryBean接口的做法好一点,context.getBean("bird")也可以获取到Bean,那这种方式spring内部如何实现的呐? 按照之前的逻辑,在preInstantiateSingletons方法最后打个断点,看下结果 

可以看到,预初始化后,bird的实例就已经生成在一级缓存了,而且beanDefinition中也有对应的bean定义 这和实现FactoryBean接口的方式就完全不一样了!!! 可以看出,spring是对待@Bean注解是直接扫描生成bean定义,然后预初始化时候直接生成实例存入一级缓存,完全不是上面那种依靠Factory去getObject这种方式了 这种固然非常好,生成的bean和普通的bean存在一起,而且获取方法完全一样,但是如何实现的呐,我们都知道普通的bean是通过反射实例化的,这种@Bean已经提供了初始化的方法,那肯定要走这个方法生成bean而不是反射了 看了下源码找到了关键点,所有预初始化的bean肯定要走createBeanInstance(实例化)这个方法

原来通过@Bean注入的bean,在bean定义中就存入了工厂方法(FactoryMethod),再实例化时判断如果存在工厂方法,直接使用工厂方法实例化并返回即可(不需要再往下走反射了)
总结
虽然这两种方式都是为了实现自定义实例化bean,外界都统称为FactoryBean,但通过以上的对比和源码分析,发现两种方式除了目的基本一样,实现的方式和实例化的实际种种都不同,甚至没有什么交集,严格来讲@Bean不能叫做FactoryBean,但如果把FactoryBean理解为通过工厂模式生成bean的一种方式,那二者确实可以混为一谈